Display error page if kerberos token is unavailable

This commit is contained in:
mposolda 2015-02-11 11:52:14 +01:00
parent 07bda93b81
commit 4b637036ac
9 changed files with 48 additions and 20 deletions

View file

@ -2,10 +2,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent> <parent>
<artifactId>keycloak-broker-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>1.2.0.Beta1-SNAPSHOT</version> <version>1.2.0.Beta1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View file

@ -19,6 +19,7 @@ package org.keycloak.broker.provider;
import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.models.ClientSessionModel; import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
@ -28,6 +29,7 @@ import javax.ws.rs.core.UriInfo;
*/ */
public class AuthenticationRequest { public class AuthenticationRequest {
private final KeycloakSession session;
private final UriInfo uriInfo; private final UriInfo uriInfo;
private final String state; private final String state;
private final HttpRequest httpRequest; private final HttpRequest httpRequest;
@ -35,7 +37,8 @@ public class AuthenticationRequest {
private final String redirectUri; private final String redirectUri;
private final ClientSessionModel clientSession; private final ClientSessionModel clientSession;
public AuthenticationRequest(RealmModel realm, ClientSessionModel clientSession, HttpRequest httpRequest, UriInfo uriInfo, String state, String redirectUri) { public AuthenticationRequest(KeycloakSession session, RealmModel realm, ClientSessionModel clientSession, HttpRequest httpRequest, UriInfo uriInfo, String state, String redirectUri) {
this.session = session;
this.realm = realm; this.realm = realm;
this.httpRequest = httpRequest; this.httpRequest = httpRequest;
this.uriInfo = uriInfo; this.uriInfo = uriInfo;
@ -44,6 +47,10 @@ public class AuthenticationRequest {
this.clientSession = clientSession; this.clientSession = clientSession;
} }
public KeycloakSession getSession() {
return session;
}
public UriInfo getUriInfo() { public UriInfo getUriInfo() {
return this.uriInfo; return this.uriInfo;
} }

View file

@ -2,10 +2,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent> <parent>
<artifactId>keycloak-broker-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>1.2.0.Beta1-SNAPSHOT</version> <version>1.2.0.Beta1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -20,6 +20,11 @@
<artifactId>keycloak-broker-core</artifactId> <artifactId>keycloak-broker-core</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-login-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.jboss.logging</groupId> <groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId> <artifactId>jboss-logging</artifactId>

View file

@ -3,6 +3,7 @@ package org.keycloak.broker.kerberos;
import java.net.URI; import java.net.URI;
import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
@ -14,6 +15,7 @@ import org.keycloak.broker.provider.AbstractIdentityProvider;
import org.keycloak.broker.provider.AuthenticationRequest; import org.keycloak.broker.provider.AuthenticationRequest;
import org.keycloak.broker.provider.AuthenticationResponse; import org.keycloak.broker.provider.AuthenticationResponse;
import org.keycloak.broker.provider.FederatedIdentity; import org.keycloak.broker.provider.FederatedIdentity;
import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.FederatedIdentityModel; import org.keycloak.models.FederatedIdentityModel;
/** /**
@ -54,16 +56,16 @@ public class KerberosIdentityProvider extends AbstractIdentityProvider<KerberosI
// Case when we don't yet have any Negotiate header // Case when we don't yet have any Negotiate header
if (authHeader == null) { if (authHeader == null) {
return sendNegotiateResponse(null); return sendNegotiateResponse(request, null);
} }
String[] tokens = authHeader.split(" "); String[] tokens = authHeader.split(" ");
if (tokens.length != 2) { if (tokens.length != 2) {
logger.warn("Invalid length of tokens: " + tokens.length); logger.warn("Invalid length of tokens: " + tokens.length);
return sendNegotiateResponse(null); return sendNegotiateResponse(request, null);
} else if (!KerberosConstants.NEGOTIATE.equalsIgnoreCase(tokens[0])) { } else if (!KerberosConstants.NEGOTIATE.equalsIgnoreCase(tokens[0])) {
logger.warn("Unknown scheme " + tokens[0]); logger.warn("Unknown scheme " + tokens[0]);
return sendNegotiateResponse(null); return sendNegotiateResponse(request, null);
} else { } else {
String spnegoToken = tokens[1]; String spnegoToken = tokens[1];
SPNEGOAuthenticator spnegoAuthenticator = createSPNEGOAuthenticator(spnegoToken); SPNEGOAuthenticator spnegoAuthenticator = createSPNEGOAuthenticator(spnegoToken);
@ -73,7 +75,7 @@ public class KerberosIdentityProvider extends AbstractIdentityProvider<KerberosI
FederatedIdentity federatedIdentity = getFederatedIdentity(spnegoAuthenticator); FederatedIdentity federatedIdentity = getFederatedIdentity(spnegoAuthenticator);
return AuthenticationResponse.end(federatedIdentity); return AuthenticationResponse.end(federatedIdentity);
} else { } else {
return sendNegotiateResponse(spnegoAuthenticator.getResponseToken()); return sendNegotiateResponse(request, spnegoAuthenticator.getResponseToken());
} }
} }
} }
@ -94,16 +96,22 @@ public class KerberosIdentityProvider extends AbstractIdentityProvider<KerberosI
* @param negotiateToken token to be send back in response or null if just "WWW-Authenticate: Negotiate" should be sent * @param negotiateToken token to be send back in response or null if just "WWW-Authenticate: Negotiate" should be sent
* @return AuthenticationResponse * @return AuthenticationResponse
*/ */
protected AuthenticationResponse sendNegotiateResponse(String negotiateToken) { protected AuthenticationResponse sendNegotiateResponse(AuthenticationRequest request, String negotiateToken) {
String negotiateHeader = negotiateToken == null ? KerberosConstants.NEGOTIATE : KerberosConstants.NEGOTIATE + " " + negotiateToken; String negotiateHeader = negotiateToken == null ? KerberosConstants.NEGOTIATE : KerberosConstants.NEGOTIATE + " " + negotiateToken;
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Sending back " + HttpHeaders.WWW_AUTHENTICATE + ": " + negotiateHeader); logger.trace("Sending back " + HttpHeaders.WWW_AUTHENTICATE + ": " + negotiateHeader);
} }
Response response = Response.status(Response.Status.UNAUTHORIZED) // Error page is rendered just if browser is unable to send Authorization header with SPNEGO token
.header(HttpHeaders.WWW_AUTHENTICATE, negotiateHeader) Response response = request.getSession().getProvider(LoginFormsProvider.class)
.build(); .setRealm(request.getRealm())
.setUriInfo(request.getUriInfo())
.setError("errorKerberosLogin")
.setStatus(Response.Status.UNAUTHORIZED)
.createErrorPage();
response.getMetadata().putSingle(HttpHeaders.WWW_AUTHENTICATE, negotiateHeader);
return AuthenticationResponse.fromResponse(response); return AuthenticationResponse.fromResponse(response);
} }

View file

@ -2,10 +2,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent> <parent>
<artifactId>keycloak-broker-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>1.2.0.Beta1-SNAPSHOT</version> <version>1.2.0.Beta1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View file

@ -2,10 +2,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent> <parent>
<artifactId>keycloak-broker-parent</artifactId> <artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version>1.2.0.Beta1-SNAPSHOT</version> <version>1.2.0.Beta1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View file

@ -97,6 +97,8 @@ actionPasswordWarning=You need to change your password to activate your account.
actionEmailWarning=You need to verify your email address to activate your account. actionEmailWarning=You need to verify your email address to activate your account.
actionFollow=Please fill in the fields below. actionFollow=Please fill in the fields below.
errorKerberosLogin=Unable to login with Kerberos
successHeader=Success! successHeader=Success!
errorHeader=Error! errorHeader=Error!

View file

@ -53,7 +53,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
private String message; private String message;
private String accessCode; private String accessCode;
private Response.Status status = Response.Status.OK; private Response.Status status;
private List<RoleModel> realmRolesRequested; private List<RoleModel> realmRolesRequested;
private MultivaluedMap<String, RoleModel> resourceRolesRequested; private MultivaluedMap<String, RoleModel> resourceRolesRequested;
private MultivaluedMap<String, String> queryParams; private MultivaluedMap<String, String> queryParams;
@ -218,6 +218,10 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
break; break;
} }
if (status == null) {
status = Response.Status.OK;
}
try { try {
String result = freeMarker.processTemplate(attributes, Templates.getTemplate(page), theme); String result = freeMarker.processTemplate(attributes, Templates.getTemplate(page), theme);
Response.ResponseBuilder builder = Response.status(status).type(MediaType.TEXT_HTML).entity(result); Response.ResponseBuilder builder = Response.status(status).type(MediaType.TEXT_HTML).entity(result);
@ -246,7 +250,9 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
} }
public Response createErrorPage() { public Response createErrorPage() {
setStatus(Response.Status.INTERNAL_SERVER_ERROR); if (status == null) {
status = Response.Status.INTERNAL_SERVER_ERROR;
}
return createResponse(LoginFormsPages.ERROR); return createResponse(LoginFormsPages.ERROR);
} }

View file

@ -422,7 +422,7 @@ public class AuthenticationBrokerResource {
} }
private AuthenticationRequest createAuthenticationRequest(String providerId, String code, RealmModel realm, ClientSessionModel clientSession) { private AuthenticationRequest createAuthenticationRequest(String providerId, String code, RealmModel realm, ClientSessionModel clientSession) {
return new AuthenticationRequest(realm, clientSession, this.request, this.uriInfo, code, getRedirectUri(providerId, realm)); return new AuthenticationRequest(this.session, realm, clientSession, this.request, this.uriInfo, code, getRedirectUri(providerId, realm));
} }
private String getRedirectUri(String providerId, RealmModel realm) { private String getRedirectUri(String providerId, RealmModel realm) {