sendError() handling'
This commit is contained in:
parent
b1deed67df
commit
ff63c5552a
70 changed files with 1023 additions and 188 deletions
|
@ -20,6 +20,7 @@
|
|||
<!ENTITY SpringSecurityAdapter SYSTEM "modules/spring-security-adapter.xml">
|
||||
<!ENTITY InstalledApplications SYSTEM "modules/installed-applications.xml">
|
||||
<!ENTITY Logout SYSTEM "modules/logout.xml">
|
||||
<!ENTITY ErrorHandling SYSTEM "modules/adapter_error_handling.xml">
|
||||
<!ENTITY SAML SYSTEM "modules/saml.xml">
|
||||
<!ENTITY JAAS SYSTEM "modules/jaas.xml">
|
||||
<!ENTITY IdentityBroker SYSTEM "modules/identity-broker.xml">
|
||||
|
@ -115,6 +116,7 @@ This one is short
|
|||
&SpringSecurityAdapter;
|
||||
&InstalledApplications;
|
||||
&Logout;
|
||||
&ErrorHandling;
|
||||
&MultiTenancy;
|
||||
&JAAS;
|
||||
</chapter>
|
||||
|
|
|
@ -83,6 +83,10 @@
|
|||
<title>Migrating to 1.7.0.CR1</title>
|
||||
<simplesect>
|
||||
<title>Option 'Update Profile On First Login' moved from Identity provider to Review Profile authenticator</title>
|
||||
<para>
|
||||
form-error-page in web.xml will no longer work for client adapter authentication errors. You must define an error-page for
|
||||
the the various HTTP error codes. See documentation for more details if you want to catch and handle adapter error conditions.
|
||||
</para>
|
||||
<para>
|
||||
In this version, we added <literal>First Broker Login</literal>, which allows you to specify what exactly should be done
|
||||
when new user is logged through Identity provider (or Social provider), but there is no existing Keycloak user
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<section id="adapter_error_handling">
|
||||
<title>Error Handling</title>
|
||||
<para>
|
||||
Keycloak has some error handling facilities for servlet based client adapters. When an error is encountered in
|
||||
authentication, keycloak will call <literal>HttpServletResponse.sendError()</literal>. You can set up an error-page
|
||||
within your <literal>web.xml</literal> file to handle the error however you want. Keycloak may throw
|
||||
400, 401, 403, and 500 errors.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
<error-page>
|
||||
<error-code>404</error-code>
|
||||
<location>/ErrorHandler</location>
|
||||
</error-page>]]>
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Keycloak also sets an <literal>HttpServletRequest</literal> attribute that you can retrieve. The attribute name
|
||||
is <literal>org.keycloak.adapters.spi.AuthenticationError</literal>. Typecast this object to:
|
||||
<literal>org.keycloak.adapters.OIDCAuthenticationError</literal>. This class can tell you exactly what happened.
|
||||
If this attribute is not set, then the adapter was not responsible for the error code.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting>
|
||||
public class OIDCAuthenticationError implements AuthenticationError {
|
||||
public static enum Reason {
|
||||
NO_BEARER_TOKEN,
|
||||
NO_REDIRECT_URI,
|
||||
INVALID_STATE_COOKIE,
|
||||
OAUTH_ERROR,
|
||||
SSL_REQUIRED,
|
||||
CODE_TO_TOKEN_FAILURE,
|
||||
INVALID_TOKEN,
|
||||
STALE_TOKEN,
|
||||
NO_AUTHORIZATION_HEADER
|
||||
}
|
||||
|
||||
private Reason reason;
|
||||
private String description;
|
||||
|
||||
public OIDCAuthenticationError(Reason reason, String description) {
|
||||
this.reason = reason;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Reason getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
</programlisting>
|
||||
</para>
|
||||
</section>
|
|
@ -9,6 +9,7 @@
|
|||
<!ENTITY Jetty8Adapter SYSTEM "modules/jetty8-adapter.xml">
|
||||
<!ENTITY FilterAdapter SYSTEM "modules/servlet-filter-adapter.xml">
|
||||
<!ENTITY Logout SYSTEM "modules/logout.xml">
|
||||
<!ENTITY ErrorHandling SYSTEM "modules/adapter_error_handling.xml">
|
||||
]>
|
||||
|
||||
<book>
|
||||
|
@ -49,6 +50,7 @@ This one is short
|
|||
&Jetty8Adapter;
|
||||
&FilterAdapter;
|
||||
&Logout;
|
||||
&ErrorHandling;
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<chapter id="adapter_error_handling">
|
||||
<title>Error Handling</title>
|
||||
<para>
|
||||
Keycloak has some error handling facilities for servlet based client adapters. When an error is encountered in
|
||||
authentication, keycloak will call <literal>HttpServletResponse.sendError()</literal>. You can set up an error-page
|
||||
within your <literal>web.xml</literal> file to handle the error however you want. Keycloak may throw
|
||||
400, 401, 403, and 500 errors.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
<error-page>
|
||||
<error-code>404</error-code>
|
||||
<location>/ErrorHandler</location>
|
||||
</error-page>]]>
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Keycloak also sets an <literal>HttpServletRequest</literal> attribute that you can retrieve. The attribute name
|
||||
is <literal>org.keycloak.adapters.spi.AuthenticationError</literal>. Typecast this object to:
|
||||
<literal>org.keycloak.adapters.saml.SamlAuthenticationError</literal>. This class can tell you exactly what happened.
|
||||
If this attribute is not set, then the adapter was not responsible for the error code.
|
||||
</para>
|
||||
<para>
|
||||
<programlisting>
|
||||
public class SamlAuthenticationError implements AuthenticationError {
|
||||
public static enum Reason {
|
||||
EXTRACTION_FAILURE,
|
||||
INVALID_SIGNATURE,
|
||||
ERROR_STATUS
|
||||
}
|
||||
|
||||
public Reason getReason() {
|
||||
return reason;
|
||||
}
|
||||
public StatusResponseType getStatus() {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
</programlisting>
|
||||
</para>
|
||||
</chapter>
|
14
examples/demo-template/offline-access-app/src/main/java/org/keycloak/example/OfflineAccessPortalServlet.java
Normal file → Executable file
14
examples/demo-template/offline-access-app/src/main/java/org/keycloak/example/OfflineAccessPortalServlet.java
Normal file → Executable file
|
@ -17,10 +17,12 @@ import org.apache.http.client.methods.HttpGet;
|
|||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.AdapterDeploymentContext;
|
||||
import org.keycloak.adapters.spi.AuthenticationError;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||
import org.keycloak.adapters.ServerRequest;
|
||||
import org.keycloak.adapters.spi.LogoutError;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.RefreshToken;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
@ -203,6 +205,18 @@ public class OfflineAccessPortalServlet extends HttpServlet {
|
|||
public String getRemoteAddr() {
|
||||
return servletRequest.getRemoteAddr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(AuthenticationError error) {
|
||||
servletRequest.setAttribute(AuthenticationError.class.getName(), error);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(LogoutError error) {
|
||||
servletRequest.setAttribute(LogoutError.class.getName(), error);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
6
integration/adapter-core/src/main/java/org/keycloak/adapters/BasicAuthRequestAuthenticator.java
Normal file → Executable file
6
integration/adapter-core/src/main/java/org/keycloak/adapters/BasicAuthRequestAuthenticator.java
Normal file → Executable file
|
@ -33,7 +33,7 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
|
|||
public AuthOutcome authenticate(HttpFacade exchange) {
|
||||
List<String> authHeaders = exchange.getRequest().getHeaders("Authorization");
|
||||
if (authHeaders == null || authHeaders.size() == 0) {
|
||||
challenge = challengeResponse(exchange, null, null);
|
||||
challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.NO_AUTHORIZATION_HEADER, null, null);
|
||||
return AuthOutcome.NOT_ATTEMPTED;
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
|
|||
}
|
||||
|
||||
if (tokenString == null) {
|
||||
challenge = challengeResponse(exchange, null, null);
|
||||
challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.INVALID_TOKEN, null, null);
|
||||
return AuthOutcome.NOT_ATTEMPTED;
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ public class BasicAuthRequestAuthenticator extends BearerTokenRequestAuthenticat
|
|||
tokenString = atr.getToken();
|
||||
} catch (Exception e) {
|
||||
log.debug("Failed to obtain token", e);
|
||||
challenge = challengeResponse(exchange, "no_token", e.getMessage());
|
||||
challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.INVALID_TOKEN, "no_token", e.getMessage());
|
||||
return AuthOutcome.FAILED;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ public class BearerTokenRequestAuthenticator {
|
|||
public AuthOutcome authenticate(HttpFacade exchange) {
|
||||
List<String> authHeaders = exchange.getRequest().getHeaders("Authorization");
|
||||
if (authHeaders == null || authHeaders.size() == 0) {
|
||||
challenge = challengeResponse(exchange, null, null);
|
||||
challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.NO_BEARER_TOKEN, null, null);
|
||||
return AuthOutcome.NOT_ATTEMPTED;
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ public class BearerTokenRequestAuthenticator {
|
|||
}
|
||||
|
||||
if (tokenString == null) {
|
||||
challenge = challengeResponse(exchange, null, null);
|
||||
challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.NO_BEARER_TOKEN, null, null);
|
||||
return AuthOutcome.NOT_ATTEMPTED;
|
||||
}
|
||||
|
||||
|
@ -70,12 +70,12 @@ public class BearerTokenRequestAuthenticator {
|
|||
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl());
|
||||
} catch (VerificationException e) {
|
||||
log.error("Failed to verify token", e);
|
||||
challenge = challengeResponse(exchange, "invalid_token", e.getMessage());
|
||||
challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.INVALID_TOKEN, "invalid_token", e.getMessage());
|
||||
return AuthOutcome.FAILED;
|
||||
}
|
||||
if (token.getIssuedAt() < deployment.getNotBefore()) {
|
||||
log.error("Stale token");
|
||||
challenge = challengeResponse(exchange, "invalid_token", "Stale token");
|
||||
challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.STALE_TOKEN, "invalid_token", "Stale token");
|
||||
return AuthOutcome.FAILED;
|
||||
}
|
||||
boolean verifyCaller = false;
|
||||
|
@ -112,11 +112,6 @@ public class BearerTokenRequestAuthenticator {
|
|||
|
||||
protected AuthChallenge clientCertChallenge() {
|
||||
return new AuthChallenge() {
|
||||
@Override
|
||||
public boolean errorPage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResponseCode() {
|
||||
return 0;
|
||||
|
@ -131,7 +126,7 @@ public class BearerTokenRequestAuthenticator {
|
|||
}
|
||||
|
||||
|
||||
protected AuthChallenge challengeResponse(HttpFacade facade, String error, String description) {
|
||||
protected AuthChallenge challengeResponse(HttpFacade facade, final OIDCAuthenticationError.Reason reason, final String error, final String description) {
|
||||
StringBuilder header = new StringBuilder("Bearer realm=\"");
|
||||
header.append(deployment.getRealm()).append("\"");
|
||||
if (error != null) {
|
||||
|
@ -142,11 +137,6 @@ public class BearerTokenRequestAuthenticator {
|
|||
}
|
||||
final String challenge = header.toString();
|
||||
return new AuthChallenge() {
|
||||
@Override
|
||||
public boolean errorPage() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResponseCode() {
|
||||
return 401;
|
||||
|
@ -154,8 +144,10 @@ public class BearerTokenRequestAuthenticator {
|
|||
|
||||
@Override
|
||||
public boolean challenge(HttpFacade facade) {
|
||||
facade.getResponse().setStatus(401);
|
||||
OIDCAuthenticationError error = new OIDCAuthenticationError(reason, description);
|
||||
facade.getRequest().setError(error);
|
||||
facade.getResponse().addHeader("WWW-Authenticate", challenge);
|
||||
facade.getResponse().sendError(401);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -174,15 +174,10 @@ public class OAuthRequestAuthenticator {
|
|||
final String state = getStateCode();
|
||||
final String redirect = getRedirectUri(state);
|
||||
if (redirect == null) {
|
||||
return challenge(403);
|
||||
return challenge(403, OIDCAuthenticationError.Reason.NO_REDIRECT_URI, null);
|
||||
}
|
||||
return new AuthChallenge() {
|
||||
|
||||
@Override
|
||||
public boolean errorPage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResponseCode() {
|
||||
return 0;
|
||||
|
@ -205,7 +200,7 @@ public class OAuthRequestAuthenticator {
|
|||
|
||||
if (stateCookie == null) {
|
||||
log.warn("No state cookie");
|
||||
return challenge(400);
|
||||
return challenge(400, OIDCAuthenticationError.Reason.INVALID_STATE_COOKIE, null);
|
||||
}
|
||||
// reset the cookie
|
||||
log.debug("** reseting application state cookie");
|
||||
|
@ -215,13 +210,13 @@ public class OAuthRequestAuthenticator {
|
|||
String state = getQueryParamValue(OAuth2Constants.STATE);
|
||||
if (state == null) {
|
||||
log.warn("state parameter was null");
|
||||
return challenge(400);
|
||||
return challenge(400, OIDCAuthenticationError.Reason.INVALID_STATE_COOKIE, null);
|
||||
}
|
||||
if (!state.equals(stateCookieValue)) {
|
||||
log.warn("state parameter invalid");
|
||||
log.warn("cookie: " + stateCookieValue);
|
||||
log.warn("queryParam: " + state);
|
||||
return challenge(400);
|
||||
return challenge(400, OIDCAuthenticationError.Reason.INVALID_STATE_COOKIE, null);
|
||||
}
|
||||
return null;
|
||||
|
||||
|
@ -235,7 +230,7 @@ public class OAuthRequestAuthenticator {
|
|||
if (error != null) {
|
||||
// todo how do we send a response?
|
||||
log.warn("There was an error: " + error);
|
||||
challenge = challenge(400);
|
||||
challenge = challenge(400, OIDCAuthenticationError.Reason.OAUTH_ERROR, error);
|
||||
return AuthOutcome.FAILED;
|
||||
} else {
|
||||
log.debug("redirecting to auth server");
|
||||
|
@ -253,13 +248,8 @@ public class OAuthRequestAuthenticator {
|
|||
|
||||
}
|
||||
|
||||
protected AuthChallenge challenge(final int code) {
|
||||
protected AuthChallenge challenge(final int code, final OIDCAuthenticationError.Reason reason, final String description) {
|
||||
return new AuthChallenge() {
|
||||
@Override
|
||||
public boolean errorPage() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResponseCode() {
|
||||
return code;
|
||||
|
@ -267,6 +257,8 @@ public class OAuthRequestAuthenticator {
|
|||
|
||||
@Override
|
||||
public boolean challenge(HttpFacade exchange) {
|
||||
OIDCAuthenticationError error = new OIDCAuthenticationError(reason, description);
|
||||
exchange.getRequest().setError(error);
|
||||
exchange.getResponse().sendError(code);
|
||||
return true;
|
||||
}
|
||||
|
@ -289,7 +281,7 @@ public class OAuthRequestAuthenticator {
|
|||
// abort if not HTTPS
|
||||
if (!isRequestSecure() && deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr())) {
|
||||
log.error("Adapter requires SSL. Request: " + facade.getRequest().getURI());
|
||||
return challenge(403);
|
||||
return challenge(403, OIDCAuthenticationError.Reason.SSL_REQUIRED, null);
|
||||
}
|
||||
|
||||
log.debug("checking state cookie for after code");
|
||||
|
@ -308,11 +300,11 @@ public class OAuthRequestAuthenticator {
|
|||
if (failure.getStatus() == 400 && failure.getError() != null) {
|
||||
log.error(" " + failure.getError());
|
||||
}
|
||||
return challenge(403);
|
||||
return challenge(403, OIDCAuthenticationError.Reason.CODE_TO_TOKEN_FAILURE, null);
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("failed to turn code into token", e);
|
||||
return challenge(403);
|
||||
return challenge(403, OIDCAuthenticationError.Reason.CODE_TO_TOKEN_FAILURE, null);
|
||||
}
|
||||
|
||||
tokenString = tokenResponse.getToken();
|
||||
|
@ -331,14 +323,14 @@ public class OAuthRequestAuthenticator {
|
|||
log.debug("Token Verification succeeded!");
|
||||
} catch (VerificationException e) {
|
||||
log.error("failed verification of token: " + e.getMessage());
|
||||
return challenge(403);
|
||||
return challenge(403, OIDCAuthenticationError.Reason.INVALID_TOKEN, null);
|
||||
}
|
||||
if (tokenResponse.getNotBeforePolicy() > deployment.getNotBefore()) {
|
||||
deployment.setNotBefore(tokenResponse.getNotBeforePolicy());
|
||||
}
|
||||
if (token.getIssuedAt() < deployment.getNotBefore()) {
|
||||
log.error("Stale token");
|
||||
return challenge(403);
|
||||
return challenge(403, OIDCAuthenticationError.Reason.STALE_TOKEN, null);
|
||||
}
|
||||
log.debug("successful authenticated");
|
||||
return null;
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package org.keycloak.adapters;
|
||||
|
||||
import org.keycloak.adapters.spi.AuthenticationError;
|
||||
|
||||
/**
|
||||
* Object that describes the OIDC error that happened.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class OIDCAuthenticationError implements AuthenticationError {
|
||||
public static enum Reason {
|
||||
NO_BEARER_TOKEN,
|
||||
NO_REDIRECT_URI,
|
||||
INVALID_STATE_COOKIE,
|
||||
OAUTH_ERROR,
|
||||
SSL_REQUIRED,
|
||||
CODE_TO_TOKEN_FAILURE,
|
||||
INVALID_TOKEN,
|
||||
STALE_TOKEN,
|
||||
NO_AUTHORIZATION_HEADER
|
||||
}
|
||||
|
||||
private Reason reason;
|
||||
private String description;
|
||||
|
||||
public OIDCAuthenticationError(Reason reason, String description) {
|
||||
this.reason = reason;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Reason getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
}
|
|
@ -13,15 +13,7 @@ public interface AuthChallenge {
|
|||
boolean challenge(HttpFacade exchange);
|
||||
|
||||
/**
|
||||
* Whether or not an error page should be displayed if possible along with the challenge
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean errorPage();
|
||||
|
||||
/**
|
||||
* If errorPage is true, this is the response code the challenge will send. This is used by platforms
|
||||
* that call HttpServletResponse.sendError() to forward to error page.
|
||||
* Some platforms need the error code that will be sent (i.e. Undertow)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package org.keycloak.adapters.spi;
|
||||
|
||||
/**
|
||||
* Common marker interface used by keycloak client adapters when there is an error. For servlets, you'll be able
|
||||
* to extract this error from the HttpServletRequest.getAttribute(AuthenticationError.class.getName()). Each protocol
|
||||
* will have their own subclass of this interface.
|
||||
*
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface AuthenticationError {
|
||||
}
|
|
@ -47,6 +47,8 @@ public interface HttpFacade {
|
|||
InputStream getInputStream();
|
||||
|
||||
String getRemoteAddr();
|
||||
void setError(AuthenticationError error);
|
||||
void setError(LogoutError error);
|
||||
}
|
||||
|
||||
interface Response {
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package org.keycloak.adapters.spi;
|
||||
|
||||
/**
|
||||
* Common marker interface used by keycloak client adapters when there is an error. For servlets, you'll be able
|
||||
* to extract this error from the HttpServletRequest.getAttribute(LogoutError.class.getName()). Each protocol
|
||||
* will have their own subclass of this interface.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface LogoutError {
|
||||
}
|
|
@ -12,6 +12,8 @@ import javax.ws.rs.core.SecurityContext;
|
|||
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.OIDCHttpFacade;
|
||||
import org.keycloak.adapters.spi.AuthenticationError;
|
||||
import org.keycloak.adapters.spi.LogoutError;
|
||||
import org.keycloak.common.util.HostUtils;
|
||||
|
||||
/**
|
||||
|
@ -93,6 +95,17 @@ public class JaxrsHttpFacade implements OIDCHttpFacade {
|
|||
// TODO: implement properly
|
||||
return HostUtils.getIpAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(AuthenticationError error) {
|
||||
requestContext.setProperty(AuthenticationError.class.getName(), error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(LogoutError error) {
|
||||
requestContext.setProperty(LogoutError.class.getName(), error);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected class ResponseFacade implements OIDCHttpFacade.Response {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package org.keycloak.adapters.jetty.spi;
|
||||
|
||||
import org.keycloak.adapters.spi.AuthenticationError;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.spi.LogoutError;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.common.util.UriUtils;
|
||||
|
||||
|
@ -125,6 +127,18 @@ public class JettyHttpFacade implements HttpFacade {
|
|||
public String getRemoteAddr() {
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(AuthenticationError error) {
|
||||
request.setAttribute(AuthenticationError.class.getName(), error);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(LogoutError error) {
|
||||
request.setAttribute(LogoutError.class.getName(), error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ResponseFacade implements Response {
|
||||
|
|
|
@ -265,15 +265,6 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
|
|||
AuthChallenge challenge = authenticator.getChallenge();
|
||||
if (challenge != null) {
|
||||
challenge.challenge(facade);
|
||||
if (challenge.errorPage() && errorPage != null) {
|
||||
Response response = (Response)res;
|
||||
try {
|
||||
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), errorPage)));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return Authentication.SEND_CONTINUE;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package org.keycloak.adapters.servlet;
|
||||
|
||||
import org.keycloak.adapters.spi.AuthenticationError;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.spi.LogoutError;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.common.util.ServerCookie;
|
||||
import org.keycloak.common.util.UriUtils;
|
||||
|
@ -111,6 +113,18 @@ public class ServletHttpFacade implements HttpFacade {
|
|||
public String getRemoteAddr() {
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setError(AuthenticationError error) {
|
||||
request.setAttribute(AuthenticationError.class.getName(), error);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(LogoutError error) {
|
||||
request.setAttribute(LogoutError.class.getName(), error);
|
||||
}
|
||||
}
|
||||
public boolean isEnded() {
|
||||
return responseFacade.isEnded();
|
||||
|
|
|
@ -142,11 +142,6 @@ public class KeycloakOIDCFilter implements Filter {
|
|||
if (challenge != null) {
|
||||
log.fine("challenge");
|
||||
challenge.challenge(facade);
|
||||
if (challenge.errorPage()) {
|
||||
response.sendError(challenge.getResponseCode());
|
||||
return;
|
||||
}
|
||||
log.fine("sending challenge");
|
||||
return;
|
||||
}
|
||||
response.sendError(403);
|
||||
|
|
|
@ -6,6 +6,8 @@ import org.keycloak.adapters.AdapterDeploymentContext;
|
|||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.OIDCHttpFacade;
|
||||
import org.keycloak.adapters.ServerRequest;
|
||||
import org.keycloak.adapters.spi.AuthenticationError;
|
||||
import org.keycloak.adapters.spi.LogoutError;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.IDToken;
|
||||
|
@ -237,6 +239,18 @@ public class ServletOAuthClient extends KeycloakDeploymentDelegateOAuthClient {
|
|||
public String getRemoteAddr() {
|
||||
return servletRequest.getRemoteAddr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(AuthenticationError error) {
|
||||
servletRequest.setAttribute(AuthenticationError.class.getName(), error);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(LogoutError error) {
|
||||
servletRequest.setAttribute(LogoutError.class.getName(), error);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package org.keycloak.adapters.springsecurity.facade;
|
||||
|
||||
import org.keycloak.adapters.spi.AuthenticationError;
|
||||
import org.keycloak.adapters.spi.HttpFacade.Cookie;
|
||||
import org.keycloak.adapters.spi.HttpFacade.Request;
|
||||
import org.keycloak.adapters.spi.LogoutError;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -109,4 +111,17 @@ class WrappedHttpServletRequest implements Request {
|
|||
public String getRemoteAddr() {
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(AuthenticationError error) {
|
||||
request.setAttribute(AuthenticationError.class.getName(), error);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(LogoutError error) {
|
||||
request.setAttribute(LogoutError.class.getName(), error);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package org.keycloak.adapters.tomcat;
|
||||
|
||||
import org.keycloak.adapters.spi.AuthenticationError;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.spi.LogoutError;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.common.util.ServerCookie;
|
||||
import org.keycloak.common.util.UriUtils;
|
||||
|
@ -125,6 +127,18 @@ public class CatalinaHttpFacade implements HttpFacade {
|
|||
public String getRemoteAddr() {
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(AuthenticationError error) {
|
||||
request.setAttribute(AuthenticationError.class.getName(), error);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(LogoutError error) {
|
||||
request.setAttribute(LogoutError.class.getName(), error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ResponseFacade implements Response {
|
||||
|
|
|
@ -195,12 +195,6 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
|
|||
}
|
||||
AuthChallenge challenge = authenticator.getChallenge();
|
||||
if (challenge != null) {
|
||||
if (loginConfig == null) {
|
||||
loginConfig = request.getContext().getLoginConfig();
|
||||
}
|
||||
if (challenge.errorPage()) {
|
||||
if (forwardToErrorPageInternal(request, response, loginConfig))return false;
|
||||
}
|
||||
challenge.challenge(facade);
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -2,9 +2,13 @@ package org.keycloak.adapters.undertow;
|
|||
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
||||
import org.keycloak.adapters.spi.AuthenticationError;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.spi.LogoutError;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -18,6 +22,7 @@ public class ServletHttpFacade extends UndertowHttpFacade {
|
|||
super(exchange);
|
||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
||||
request = (HttpServletRequest)servletRequestContext.getServletRequest();
|
||||
response = (HttpServletResponse)servletRequestContext.getServletResponse();
|
||||
}
|
||||
|
||||
protected class RequestFacade extends UndertowHttpFacade.RequestFacade {
|
||||
|
@ -26,6 +31,47 @@ public class ServletHttpFacade extends UndertowHttpFacade {
|
|||
return request.getParameter(param);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(AuthenticationError error) {
|
||||
request.setAttribute(AuthenticationError.class.getName(), error);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(LogoutError error) {
|
||||
request.setAttribute(LogoutError.class.getName(), error);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected class ResponseFacade extends UndertowHttpFacade.ResponseFacade {
|
||||
// can't call sendError from a challenge. Undertow ends up calling send error.
|
||||
/*
|
||||
@Override
|
||||
public void sendError(int code) {
|
||||
try {
|
||||
response.sendError(code);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendError(int code, String message) {
|
||||
try {
|
||||
response.sendError(code, message);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response getResponse() {
|
||||
return new ResponseFacade();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2,9 +2,12 @@ package org.keycloak.adapters.undertow;
|
|||
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.server.handlers.CookieImpl;
|
||||
import io.undertow.util.AttachmentKey;
|
||||
import io.undertow.util.Headers;
|
||||
import io.undertow.util.HttpString;
|
||||
import org.keycloak.adapters.spi.AuthenticationError;
|
||||
import org.keycloak.adapters.spi.HttpFacade;
|
||||
import org.keycloak.adapters.spi.LogoutError;
|
||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||
|
||||
import javax.security.cert.X509Certificate;
|
||||
|
@ -22,6 +25,9 @@ import java.util.Map;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UndertowHttpFacade implements HttpFacade {
|
||||
public static final AttachmentKey<AuthenticationError> AUTH_ERROR_ATTACHMENT_KEY = AttachmentKey.create(AuthenticationError.class);
|
||||
public static final AttachmentKey<LogoutError> LOGOUT_ERROR_ATTACHMENT_KEY = AttachmentKey.create(LogoutError.class);
|
||||
|
||||
protected HttpServerExchange exchange;
|
||||
protected RequestFacade requestFacade = new RequestFacade();
|
||||
protected ResponseFacade responseFacade = new ResponseFacade();
|
||||
|
@ -127,6 +133,17 @@ public class UndertowHttpFacade implements HttpFacade {
|
|||
}
|
||||
return address.getHostAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(AuthenticationError error) {
|
||||
exchange.putAttachment(AUTH_ERROR_ATTACHMENT_KEY, error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setError(LogoutError error) {
|
||||
exchange.putAttachment(LOGOUT_ERROR_ATTACHMENT_KEY, error);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected class ResponseFacade implements Response {
|
||||
|
@ -173,7 +190,6 @@ public class UndertowHttpFacade implements HttpFacade {
|
|||
@Override
|
||||
public void sendError(int code) {
|
||||
exchange.setResponseCode(code);
|
||||
exchange.endExchange();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -56,11 +56,7 @@ public abstract class AbstractUndertowKeycloakAuthMech implements Authentication
|
|||
public ChallengeResult sendChallenge(HttpServerExchange exchange, SecurityContext securityContext) {
|
||||
AuthChallenge challenge = exchange.getAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY);
|
||||
if (challenge != null) {
|
||||
if (challenge.errorPage() && errorPage != null) {
|
||||
Integer code = servePage(exchange, errorPage);
|
||||
return new ChallengeResult(true, code);
|
||||
}
|
||||
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
|
||||
UndertowHttpFacade facade = createFacade(exchange);
|
||||
if (challenge.challenge(facade)) {
|
||||
return new ChallengeResult(true, exchange.getResponseCode());
|
||||
}
|
||||
|
@ -68,6 +64,10 @@ public abstract class AbstractUndertowKeycloakAuthMech implements Authentication
|
|||
return new ChallengeResult(false);
|
||||
}
|
||||
|
||||
public UndertowHttpFacade createFacade(HttpServerExchange exchange) {
|
||||
return new OIDCUndertowHttpFacade(exchange);
|
||||
}
|
||||
|
||||
protected Integer servePage(final HttpServerExchange exchange, final String location) {
|
||||
sendRedirect(exchange, location);
|
||||
return StatusCodes.TEMPORARY_REDIRECT;
|
||||
|
@ -89,7 +89,7 @@ public abstract class AbstractUndertowKeycloakAuthMech implements Authentication
|
|||
if (notification.getEventType() != SecurityNotification.EventType.LOGGED_OUT) return;
|
||||
|
||||
HttpServerExchange exchange = notification.getExchange();
|
||||
UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
|
||||
UndertowHttpFacade facade = createFacade(exchange);
|
||||
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
|
||||
KeycloakSecurityContext ksc = exchange.getAttachment(OIDCUndertowHttpFacade.KEYCLOAK_SECURITY_CONTEXT_KEY);
|
||||
if (ksc != null && ksc instanceof RefreshableKeycloakSecurityContext) {
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
|
||||
* as indicated by the @author tags. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.keycloak.adapters.undertow;
|
||||
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.util.AttachmentKey;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.keycloak.adapters.OIDCHttpFacade;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class OIDCServletUndertowHttpFacade extends ServletHttpFacade implements OIDCHttpFacade {
|
||||
public static final AttachmentKey<KeycloakSecurityContext> KEYCLOAK_SECURITY_CONTEXT_KEY = AttachmentKey.create(KeycloakSecurityContext.class);
|
||||
|
||||
public OIDCServletUndertowHttpFacade(HttpServerExchange exchange) {
|
||||
super(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakSecurityContext getSecurityContext() {
|
||||
return exchange.getAttachment(KEYCLOAK_SECURITY_CONTEXT_KEY);
|
||||
}
|
||||
|
||||
}
|
|
@ -79,7 +79,7 @@ public class ServletKeycloakAuthMech extends AbstractUndertowKeycloakAuthMech {
|
|||
|
||||
@Override
|
||||
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
|
||||
UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
|
||||
UndertowHttpFacade facade = createFacade(exchange);
|
||||
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
|
||||
if (!deployment.isConfigured()) {
|
||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
||||
|
@ -119,4 +119,8 @@ public class ServletKeycloakAuthMech extends AbstractUndertowKeycloakAuthMech {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UndertowHttpFacade createFacade(HttpServerExchange exchange) {
|
||||
return new OIDCServletUndertowHttpFacade(exchange);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,11 +61,13 @@ public class ServletPreAuthActionsHandler implements HttpHandler {
|
|||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||
UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
|
||||
UndertowHttpFacade facade = new OIDCServletUndertowHttpFacade(exchange);
|
||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
||||
SessionManagementBridge bridge = new SessionManagementBridge(userSessionManagement, servletRequestContext.getDeployment().getSessionManager());
|
||||
PreAuthActionsHandler handler = new PreAuthActionsHandler(bridge, deploymentContext, facade);
|
||||
if (handler.handleRequest()) return;
|
||||
next.handleRequest(exchange);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ public class UndertowAuthenticationMechanism extends AbstractUndertowKeycloakAut
|
|||
|
||||
@Override
|
||||
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
|
||||
UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
|
||||
UndertowHttpFacade facade = createFacade(exchange);
|
||||
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
|
||||
if (!deployment.isConfigured()) {
|
||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
||||
|
|
|
@ -47,10 +47,14 @@ public class UndertowPreAuthActionsHandler implements HttpHandler {
|
|||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) throws Exception {
|
||||
UndertowHttpFacade facade = new OIDCUndertowHttpFacade(exchange);
|
||||
UndertowHttpFacade facade = createFacade(exchange);
|
||||
SessionManagementBridge bridge = new SessionManagementBridge(userSessionManagement, sessionManager);
|
||||
PreAuthActionsHandler handler = new PreAuthActionsHandler(bridge, deploymentContext, facade);
|
||||
if (handler.handleRequest()) return;
|
||||
next.handleRequest(exchange);
|
||||
}
|
||||
|
||||
public UndertowHttpFacade createFacade(HttpServerExchange exchange) {
|
||||
return new OIDCUndertowHttpFacade(exchange);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,11 +26,6 @@ public class InitiateLogin implements AuthChallenge {
|
|||
this.sessionStore = sessionStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean errorPage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResponseCode() {
|
||||
return 0;
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
package org.keycloak.adapters.saml;
|
||||
|
||||
import org.keycloak.adapters.spi.AuthenticationError;
|
||||
import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
|
||||
import org.keycloak.dom.saml.v2.protocol.StatusType;
|
||||
|
||||
/**
|
||||
* Object that describes the SAML error that happened.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class SamlAuthenticationError implements AuthenticationError {
|
||||
public static enum Reason {
|
||||
EXTRACTION_FAILURE,
|
||||
INVALID_SIGNATURE,
|
||||
ERROR_STATUS
|
||||
}
|
||||
|
||||
private Reason reason;
|
||||
|
||||
private StatusResponseType status;
|
||||
|
||||
public SamlAuthenticationError(Reason reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public SamlAuthenticationError(Reason reason, StatusResponseType status) {
|
||||
this.reason = reason;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public SamlAuthenticationError(StatusResponseType statusType) {
|
||||
this.status = statusType;
|
||||
}
|
||||
|
||||
public Reason getReason() {
|
||||
return reason;
|
||||
}
|
||||
public StatusResponseType getStatus() {
|
||||
return status;
|
||||
}
|
||||
}
|
|
@ -208,15 +208,34 @@ public abstract class SamlAuthenticator {
|
|||
return AuthOutcome.FAILED;
|
||||
}
|
||||
if (statusResponse instanceof ResponseType) {
|
||||
try {
|
||||
if (deployment.getIDP().getSingleSignOnService().validateResponseSignature()) {
|
||||
try {
|
||||
validateSamlSignature(holder, postBinding, GeneralConstants.SAML_RESPONSE_KEY);
|
||||
} catch (VerificationException e) {
|
||||
log.error("Failed to verify saml response signature", e);
|
||||
|
||||
challenge = new AuthChallenge() {
|
||||
@Override
|
||||
public boolean challenge(HttpFacade exchange) {
|
||||
SamlAuthenticationError error = new SamlAuthenticationError(SamlAuthenticationError.Reason.INVALID_SIGNATURE);
|
||||
exchange.getRequest().setError(error);
|
||||
exchange.getResponse().sendError(403);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResponseCode() {
|
||||
return 403;
|
||||
}
|
||||
};
|
||||
return AuthOutcome.FAILED;
|
||||
}
|
||||
}
|
||||
return handleLoginResponse((ResponseType) statusResponse);
|
||||
} finally {
|
||||
sessionStore.setCurrentAction(SamlSessionStore.CurrentAction.NONE);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (sessionStore.isLoggingOut()) {
|
||||
|
@ -239,18 +258,15 @@ public abstract class SamlAuthenticator {
|
|||
challenge = new AuthChallenge() {
|
||||
@Override
|
||||
public boolean challenge(HttpFacade exchange) {
|
||||
exchange.getResponse().sendError(500, statusResponse.getStatus().getStatusCode().getValue().toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean errorPage() {
|
||||
SamlAuthenticationError error = new SamlAuthenticationError(SamlAuthenticationError.Reason.ERROR_STATUS, statusResponse);
|
||||
exchange.getRequest().setError(error);
|
||||
exchange.getResponse().sendError(403);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResponseCode() {
|
||||
return 500;
|
||||
return 403;
|
||||
}
|
||||
};
|
||||
return AuthOutcome.FAILED;
|
||||
|
@ -280,7 +296,20 @@ public abstract class SamlAuthenticator {
|
|||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error extracting SAML assertion, e");
|
||||
return AuthOutcome.FAILED;
|
||||
challenge = new AuthChallenge() {
|
||||
@Override
|
||||
public boolean challenge(HttpFacade exchange) {
|
||||
SamlAuthenticationError error = new SamlAuthenticationError(SamlAuthenticationError.Reason.EXTRACTION_FAILURE);
|
||||
exchange.getRequest().setError(error);
|
||||
exchange.getResponse().sendError(403);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResponseCode() {
|
||||
return 403;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
SubjectType subject = assertion.getSubject();
|
||||
|
|
|
@ -260,15 +260,6 @@ public abstract class AbstractSamlAuthenticator extends LoginAuthenticator {
|
|||
AuthChallenge challenge = authenticator.getChallenge();
|
||||
if (challenge != null) {
|
||||
challenge.challenge(facade);
|
||||
if (challenge.errorPage() && errorPage != null) {
|
||||
Response response = (Response)res;
|
||||
try {
|
||||
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), errorPage)));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return Authentication.SEND_CONTINUE;
|
||||
}
|
||||
|
|
|
@ -137,11 +137,6 @@ public class SamlFilter implements Filter {
|
|||
if (challenge != null) {
|
||||
log.fine("challenge");
|
||||
challenge.challenge(facade);
|
||||
if (challenge.errorPage()) {
|
||||
response.sendError(challenge.getResponseCode());
|
||||
return;
|
||||
}
|
||||
log.fine("sending challenge");
|
||||
return;
|
||||
}
|
||||
if (!facade.isEnded()) {
|
||||
|
|
|
@ -213,10 +213,6 @@ public abstract class AbstractSamlAuthenticatorValve extends FormAuthenticator i
|
|||
loginConfig = request.getContext().getLoginConfig();
|
||||
}
|
||||
challenge.challenge(facade);
|
||||
if (challenge.errorPage()) {
|
||||
log.fine("error page");
|
||||
if (forwardToErrorPageInternal(request, response, loginConfig))return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -55,10 +55,6 @@ public abstract class AbstractSamlAuthMech implements AuthenticationMechanism {
|
|||
public ChallengeResult sendChallenge(HttpServerExchange exchange, SecurityContext securityContext) {
|
||||
AuthChallenge challenge = exchange.getAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY);
|
||||
if (challenge != null) {
|
||||
if (challenge.errorPage() && errorPage != null) {
|
||||
Integer code = servePage(exchange, errorPage);
|
||||
return new ChallengeResult(true, code);
|
||||
}
|
||||
UndertowHttpFacade facade = createFacade(exchange);
|
||||
if (challenge.challenge(facade)) {
|
||||
return new ChallengeResult(true, exchange.getResponseCode());
|
||||
|
|
|
@ -24,6 +24,7 @@ package org.keycloak.testsuite.adapter;
|
|||
import org.junit.Assert;
|
||||
import org.junit.rules.ExternalResource;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.adapters.OIDCAuthenticationError;
|
||||
import org.keycloak.common.Version;
|
||||
import org.keycloak.representations.VersionRepresentation;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
|
@ -42,6 +43,7 @@ import org.keycloak.testsuite.admin.ApiUtil;
|
|||
import org.keycloak.testsuite.pages.AccountSessionsPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.rule.AbstractKeycloakRule;
|
||||
import org.keycloak.testsuite.rule.ErrorServlet;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
|
@ -385,6 +387,7 @@ public class AdapterTestStrategy extends ExternalResource {
|
|||
* @throws Exception
|
||||
*/
|
||||
public void testNullBearerTokenCustomErrorPage() throws Exception {
|
||||
ErrorServlet.authError = null;
|
||||
Client client = ClientBuilder.newClient();
|
||||
WebTarget target = client.target(APP_SERVER_BASE_URL + "/customer-db-error-page/");
|
||||
|
||||
|
@ -396,11 +399,15 @@ public class AdapterTestStrategy extends ExternalResource {
|
|||
response.close();
|
||||
response = client.target(location).request().get();
|
||||
}
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
Assert.assertEquals(401, response.getStatus());
|
||||
String errorPageResponse = response.readEntity(String.class);
|
||||
Assert.assertTrue(errorPageResponse.contains("Error Page"));
|
||||
response.close();
|
||||
Assert.assertNotNull(ErrorServlet.authError);
|
||||
OIDCAuthenticationError error = (OIDCAuthenticationError)ErrorServlet.authError;
|
||||
Assert.assertEquals(OIDCAuthenticationError.Reason.NO_BEARER_TOKEN, error.getReason());
|
||||
|
||||
ErrorServlet.authError = null;
|
||||
response = target.request().header(HttpHeaders.AUTHORIZATION, "Bearer null").get();
|
||||
// TODO: follow redirects automatically if possible
|
||||
if (response.getStatus() == 302) {
|
||||
|
@ -408,10 +415,13 @@ public class AdapterTestStrategy extends ExternalResource {
|
|||
response.close();
|
||||
response = client.target(location).request().get();
|
||||
}
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
Assert.assertEquals(401, response.getStatus());
|
||||
errorPageResponse = response.readEntity(String.class);
|
||||
Assert.assertTrue(errorPageResponse.contains("Error Page"));
|
||||
response.close();
|
||||
Assert.assertNotNull(ErrorServlet.authError);
|
||||
error = (OIDCAuthenticationError)ErrorServlet.authError;
|
||||
Assert.assertEquals(OIDCAuthenticationError.Reason.INVALID_TOKEN, error.getReason());
|
||||
|
||||
client.close();
|
||||
|
||||
|
|
|
@ -48,12 +48,7 @@ public class SamlAdapterTest {
|
|||
|
||||
@Test
|
||||
public void testPostBadRealmSignature() {
|
||||
testStrategy.testPostBadRealmSignature( new SamlAdapterTestStrategy.CheckAuthError() {
|
||||
@Override
|
||||
public void check(WebDriver driver) {
|
||||
Assert.assertTrue(driver.getPageSource().contains("Forbidden"));
|
||||
}
|
||||
});
|
||||
testStrategy.testPostBadRealmSignature();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -61,7 +56,8 @@ public class SamlAdapterTest {
|
|||
testStrategy.testPostSimpleUnauthorized( new SamlAdapterTestStrategy.CheckAuthError() {
|
||||
@Override
|
||||
public void check(WebDriver driver) {
|
||||
Assert.assertTrue(driver.getPageSource().contains("Forbidden"));
|
||||
String pageSource = driver.getPageSource();
|
||||
Assert.assertTrue(pageSource.contains("Error Page"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.keycloak.testsuite.keycloaksaml;
|
|||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.Assert;
|
||||
import org.junit.rules.ExternalResource;
|
||||
import org.keycloak.adapters.saml.SamlAuthenticationError;
|
||||
import org.keycloak.adapters.saml.SamlPrincipal;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
|
@ -28,6 +29,7 @@ import org.keycloak.services.managers.RealmManager;
|
|||
import org.keycloak.testsuite.KeycloakServer;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.rule.AbstractKeycloakRule;
|
||||
import org.keycloak.testsuite.rule.ErrorServlet;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
|
@ -100,6 +102,7 @@ public class SamlAdapterTestStrategy extends ExternalResource {
|
|||
}
|
||||
|
||||
public void testErrorHandling() throws Exception {
|
||||
ErrorServlet.authError = null;
|
||||
Client client = ClientBuilder.newClient();
|
||||
// make sure
|
||||
Response response = client.target(APP_SERVER_BASE_URL + "/employee-sig/").request().get();
|
||||
|
@ -115,8 +118,13 @@ public class SamlAdapterTestStrategy extends ExternalResource {
|
|||
response = client.target(uri).request().get();
|
||||
String errorPage = response.readEntity(String.class);
|
||||
response.close();
|
||||
Assert.assertTrue(errorPage.contains(JBossSAMLURIConstants.STATUS_RESPONDER.get()));
|
||||
Assert.assertTrue(errorPage.contains("Error Page"));
|
||||
client.close();
|
||||
Assert.assertNotNull(ErrorServlet.authError);
|
||||
SamlAuthenticationError error = (SamlAuthenticationError)ErrorServlet.authError;
|
||||
Assert.assertEquals(SamlAuthenticationError.Reason.ERROR_STATUS, error.getReason());
|
||||
Assert.assertNotNull(error.getStatus());
|
||||
ErrorServlet.authError = null;
|
||||
|
||||
}
|
||||
|
||||
|
@ -388,13 +396,17 @@ public class SamlAdapterTestStrategy extends ExternalResource {
|
|||
void check(WebDriver driver);
|
||||
}
|
||||
|
||||
public void testPostBadRealmSignature(CheckAuthError error) {
|
||||
public void testPostBadRealmSignature() {
|
||||
ErrorServlet.authError = null;
|
||||
driver.navigate().to(APP_SERVER_BASE_URL + "/bad-realm-sales-post-sig/");
|
||||
assertEquals(driver.getCurrentUrl(), AUTH_SERVER_URL + "/realms/demo/protocol/saml");
|
||||
loginPage.login("bburke", "password");
|
||||
assertEquals(driver.getCurrentUrl(), APP_SERVER_BASE_URL + "/bad-realm-sales-post-sig/");
|
||||
System.out.println(driver.getPageSource());
|
||||
error.check(driver);
|
||||
Assert.assertNotNull(ErrorServlet.authError);
|
||||
SamlAuthenticationError error = (SamlAuthenticationError)ErrorServlet.authError;
|
||||
Assert.assertEquals(SamlAuthenticationError.Reason.INVALID_SIGNATURE, error.getReason());
|
||||
ErrorServlet.authError = null;
|
||||
}
|
||||
|
||||
public void testMetadataPostSignedLoginLogout() throws Exception {
|
||||
|
|
|
@ -115,6 +115,7 @@ public abstract class SamlKeycloakRule extends AbstractKeycloakRule {
|
|||
.addServlets(regularServletInfo)
|
||||
.addSecurityConstraint(constraint)
|
||||
.addServletExtension(new SamlServletExtension());
|
||||
addErrorPage("/error.html", deploymentInfo);
|
||||
server.getServer().deploy(deploymentInfo);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.keycloak.testsuite.rule;
|
||||
|
||||
import io.undertow.servlet.api.DeploymentInfo;
|
||||
import io.undertow.servlet.api.ErrorPage;
|
||||
import io.undertow.servlet.api.FilterInfo;
|
||||
import io.undertow.servlet.api.LoginConfig;
|
||||
import io.undertow.servlet.api.SecurityConstraint;
|
||||
|
@ -156,7 +157,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
|
|||
return new DeploymentBuilder();
|
||||
}
|
||||
|
||||
public void addErrorPage(DeploymentInfo di) {
|
||||
public void addErrorPage(String errorPage, DeploymentInfo di) {
|
||||
ServletInfo servlet = new ServletInfo("Error Page", ErrorServlet.class);
|
||||
servlet.addMapping("/error.html");
|
||||
SecurityConstraint constraint = new SecurityConstraint();
|
||||
|
@ -166,6 +167,11 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
|
|||
constraint.setEmptyRoleSemantic(SecurityInfo.EmptyRoleSemantic.PERMIT);
|
||||
di.addSecurityConstraint(constraint);
|
||||
di.addServlet(servlet);
|
||||
di
|
||||
.addErrorPage(new ErrorPage(errorPage, 400))
|
||||
.addErrorPage(new ErrorPage(errorPage, 401))
|
||||
.addErrorPage(new ErrorPage(errorPage, 403))
|
||||
.addErrorPage(new ErrorPage(errorPage, 500));
|
||||
}
|
||||
|
||||
public void deployJaxrsApplication(String name, String contextPath, Class<? extends Application> applicationClass, Map<String,String> initParams) {
|
||||
|
@ -346,9 +352,9 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
|
|||
constraint.addRoleAllowed(role);
|
||||
di.addSecurityConstraint(constraint);
|
||||
}
|
||||
LoginConfig loginConfig = new LoginConfig("KEYCLOAK", "demo", null, errorPage);
|
||||
LoginConfig loginConfig = new LoginConfig("KEYCLOAK", "demo", null, null);
|
||||
di.setLoginConfig(loginConfig);
|
||||
addErrorPage(di);
|
||||
addErrorPage(errorPage, di);
|
||||
|
||||
server.getServer().deploy(di);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.keycloak.testsuite.rule;
|
||||
|
||||
import org.keycloak.adapters.spi.AuthenticationError;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -12,10 +14,11 @@ import java.io.PrintWriter;
|
|||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ErrorServlet extends HttpServlet {
|
||||
public static AuthenticationError authError;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
|
||||
authError = (AuthenticationError)req.getAttribute(AuthenticationError.class.getName());
|
||||
|
||||
resp.setContentType("text/html");
|
||||
PrintWriter pw = resp.getWriter();
|
||||
|
@ -25,4 +28,9 @@ public class ErrorServlet extends HttpServlet {
|
|||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
doGet(req, resp);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,12 +53,7 @@ public class SamlAdapterTest {
|
|||
|
||||
@Test
|
||||
public void testPostBadRealmSignature() {
|
||||
testStrategy.testPostBadRealmSignature( new SamlAdapterTestStrategy.CheckAuthError() {
|
||||
@Override
|
||||
public void check(WebDriver driver) {
|
||||
Assert.assertTrue(driver.getPageSource().contains("Forbidden"));
|
||||
}
|
||||
});
|
||||
testStrategy.testPostBadRealmSignature();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -72,7 +67,7 @@ public class SamlAdapterTest {
|
|||
testStrategy.testPostSimpleUnauthorized(new SamlAdapterTestStrategy.CheckAuthError() {
|
||||
@Override
|
||||
public void check(WebDriver driver) {
|
||||
Assert.assertTrue(driver.getPageSource().contains("Forbidden"));
|
||||
Assert.assertTrue(driver.getPageSource().contains("Error Page"));
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
|
|
|
@ -114,6 +114,7 @@ public abstract class SamlKeycloakRule extends AbstractKeycloakRule {
|
|||
.addFilter(samlFilter)
|
||||
.addFilterUrlMapping("saml-filter", "/*", DispatcherType.REQUEST)
|
||||
.addServletExtension(new SamlServletExtension());
|
||||
addErrorPage("/error.html", deploymentInfo);
|
||||
server.getServer().deploy(deploymentInfo);
|
||||
}
|
||||
|
||||
|
|
|
@ -171,12 +171,7 @@ public class JettySamlTest {
|
|||
|
||||
@Test
|
||||
public void testPostBadRealmSignature() {
|
||||
testStrategy.testPostBadRealmSignature( new SamlAdapterTestStrategy.CheckAuthError() {
|
||||
@Override
|
||||
public void check(WebDriver driver) {
|
||||
Assert.assertEquals(driver.getPageSource(), "");
|
||||
}
|
||||
});
|
||||
testStrategy.testPostBadRealmSignature();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -25,6 +25,26 @@
|
|||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
|
|
@ -10,11 +10,40 @@
|
|||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.keycloaksaml.SendUsernameServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.rule.ErrorServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
|
|
@ -10,11 +10,39 @@
|
|||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.keycloaksaml.SendUsernameServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.rule.ErrorServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
|
|
@ -170,12 +170,7 @@ public class JettySamlTest {
|
|||
|
||||
@Test
|
||||
public void testPostBadRealmSignature() {
|
||||
testStrategy.testPostBadRealmSignature( new SamlAdapterTestStrategy.CheckAuthError() {
|
||||
@Override
|
||||
public void check(WebDriver driver) {
|
||||
Assert.assertEquals(driver.getPageSource(), "");
|
||||
}
|
||||
});
|
||||
testStrategy.testPostBadRealmSignature();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -25,6 +25,26 @@
|
|||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
|
|
@ -10,12 +10,39 @@
|
|||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.keycloaksaml.SendUsernameServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.rule.ErrorServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<security-constraint>
|
||||
<servlet-mapping>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page> <security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
|
|
|
@ -10,11 +10,39 @@
|
|||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.keycloaksaml.SendUsernameServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.rule.ErrorServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
|
|
@ -170,12 +170,7 @@ public class JettySamlTest {
|
|||
|
||||
@Test
|
||||
public void testPostBadRealmSignature() {
|
||||
testStrategy.testPostBadRealmSignature( new SamlAdapterTestStrategy.CheckAuthError() {
|
||||
@Override
|
||||
public void check(WebDriver driver) {
|
||||
Assert.assertEquals(driver.getPageSource(), "");
|
||||
}
|
||||
});
|
||||
testStrategy.testPostBadRealmSignature( );
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -25,6 +25,26 @@
|
|||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
|
|
@ -10,11 +10,39 @@
|
|||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.keycloaksaml.SendUsernameServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.rule.ErrorServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
|
|
@ -10,11 +10,39 @@
|
|||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.keycloaksaml.SendUsernameServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.rule.ErrorServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
|
|
@ -154,12 +154,7 @@ public class TomcatSamlTest {
|
|||
|
||||
@Test
|
||||
public void testPostBadRealmSignature() {
|
||||
testStrategy.testPostBadRealmSignature( new SamlAdapterTestStrategy.CheckAuthError() {
|
||||
@Override
|
||||
public void check(WebDriver driver) {
|
||||
Assert.assertEquals(driver.getPageSource(), "");
|
||||
}
|
||||
});
|
||||
testStrategy.testPostBadRealmSignature();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -25,6 +25,26 @@
|
|||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
@ -44,10 +64,6 @@
|
|||
<login-config>
|
||||
<auth-method>BASIC</auth-method>
|
||||
<realm-name>demo</realm-name>
|
||||
<form-login-config>
|
||||
<form-login-page>/error.html</form-login-page>
|
||||
<form-error-page>/error.html</form-error-page>
|
||||
</form-login-config>
|
||||
</login-config>
|
||||
|
||||
<security-role>
|
||||
|
|
|
@ -10,12 +10,39 @@
|
|||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.keycloaksaml.SendUsernameServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.rule.ErrorServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<security-constraint>
|
||||
<servlet-mapping>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page> <security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
|
|
|
@ -10,11 +10,39 @@
|
|||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.keycloaksaml.SendUsernameServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.rule.ErrorServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
|
|
@ -159,12 +159,7 @@ public class TomcatSamlTest {
|
|||
|
||||
@Test
|
||||
public void testPostBadRealmSignature() {
|
||||
testStrategy.testPostBadRealmSignature( new SamlAdapterTestStrategy.CheckAuthError() {
|
||||
@Override
|
||||
public void check(WebDriver driver) {
|
||||
Assert.assertEquals(driver.getPageSource(), "");
|
||||
}
|
||||
});
|
||||
testStrategy.testPostBadRealmSignature();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -25,6 +25,26 @@
|
|||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
|
|
@ -10,11 +10,39 @@
|
|||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.keycloaksaml.SendUsernameServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.rule.ErrorServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
|
|
@ -10,11 +10,39 @@
|
|||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.keycloaksaml.SendUsernameServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.rule.ErrorServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
|
|
@ -159,12 +159,7 @@ public class TomcatSamlTest {
|
|||
|
||||
@Test
|
||||
public void testPostBadRealmSignature() {
|
||||
testStrategy.testPostBadRealmSignature( new SamlAdapterTestStrategy.CheckAuthError() {
|
||||
@Override
|
||||
public void check(WebDriver driver) {
|
||||
Assert.assertEquals(driver.getPageSource(), "");
|
||||
}
|
||||
});
|
||||
testStrategy.testPostBadRealmSignature();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -25,6 +25,26 @@
|
|||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
|
|
@ -10,11 +10,39 @@
|
|||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.keycloaksaml.SendUsernameServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.rule.ErrorServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
|
|
@ -10,11 +10,39 @@
|
|||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.keycloaksaml.SendUsernameServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<servlet-class>org.keycloak.testsuite.rule.ErrorServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>SendUsernameServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>Error Servlet</servlet-name>
|
||||
<url-pattern>/error.html</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>400</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>401</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>403</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
|
||||
<error-page>
|
||||
<error-code>500</error-code>
|
||||
<location>/error.html</location>
|
||||
</error-page>
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>Users</web-resource-name>
|
||||
|
|
Loading…
Reference in a new issue