/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you 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 the following location:
*
* 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.jasig.cas.client.tomcat;
import java.io.IOException;
import java.security.Principal;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.TicketValidationException;
import org.jasig.cas.client.validation.TicketValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Version-agnostic authenticator which encapsulates the core CAS workflow of
* redirecting to CAS for unauthenticated sessions and validating service tickets
* when found in the request. Implementations of the Tomcat <code>Authenticator</code>
* class are expected to be thin wrappers that delegate most if not all authentication
* logic to this class.
*
* @author Marvin S. Addison
* @version $Revision$
* @since 3.1.12
*
*/
public final class AuthenticatorDelegate {
/** Log instance */
private final Logger logger = LoggerFactory.getLogger(getClass());
private String serviceUrl;
private String serverName;
private String casServerLoginUrl;
private String artifactParameterName;
private String serviceParameterName;
private TicketValidator ticketValidator;
private CasRealm realm;
/**
* Performs CAS authentication on the given request and returns the principal
* determined by the configured {@link CasRealm} on success.
*
* @param request HTTP request.
* @param response HTTP response.
*
* @return The authenticated principal on authentication success, otherwise
* null. In the case where authentication explicitly fails, either due to
* ticket validation failure or realm authentication failure, a 403 status
* code is set on the response. In cases where no existing CAS session exists,
* a 302 redirect is set on the response to redirect to the CAS server for
* authentication.
*/
public final Principal authenticate(final HttpServletRequest request, final HttpServletResponse response) {
Assertion assertion = null;
HttpSession session = request.getSession();
if (session != null) {
assertion = (Assertion) session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION);
}
if (assertion == null) {
logger.debug("CAS assertion not found in session -- authentication required.");
final String token = request.getParameter(this.artifactParameterName);
final String service = CommonUtils.constructServiceUrl(request, response, this.serviceUrl, this.serverName,
this.serviceParameterName, this.artifactParameterName, true);
if (CommonUtils.isBlank(token)) {
final String redirectUrl = CommonUtils.constructRedirectUrl(this.casServerLoginUrl,
this.serviceParameterName, service, false, false);
logger.debug("Redirecting to {}", redirectUrl);
CommonUtils.sendRedirect(response, redirectUrl);
return null;
}
try {
logger.debug("Attempting to validate {} for {}", token, service);
assertion = this.ticketValidator.validate(token, service);
logger.debug("CAS authentication succeeded.");
if (session == null) {
session = request.getSession(true);
}
session.setAttribute(AbstractCasFilter.CONST_CAS_ASSERTION, assertion);
} catch (final TicketValidationException e) {
setUnauthorized(response, e.getMessage());
return null;
}
}
Principal p = realm.authenticate(assertion.getPrincipal());
if (p == null) {
logger.debug("{} failed to authenticate to {}", assertion.getPrincipal().getName(), realm);
setUnauthorized(response, null);
}
return p;
}
/**
* @return the serviceUrl
*/
public String getServiceUrl() {
return serviceUrl;
}
/**
* @param serviceUrl the serviceUrl to set
*/
public void setServiceUrl(final String serviceUrl) {
this.serviceUrl = serviceUrl;
}
/**
* @return the serverName
*/
public String getServerName() {
return serverName;
}
/**
* @param serverName the serverName to set
*/
public void setServerName(final String serverName) {
this.serverName = serverName;
}
/**
* @return the casServerLoginUrl
*/
public String getCasServerLoginUrl() {
return casServerLoginUrl;
}
/**
* @param casServerLoginUrl the casServerLoginUrl to set
*/
public void setCasServerLoginUrl(final String casServerLoginUrl) {
this.casServerLoginUrl = casServerLoginUrl;
}
/**
* @param artifactParameterName the artifactParameterName to set
*/
public void setArtifactParameterName(final String artifactParameterName) {
this.artifactParameterName = artifactParameterName;
}
/**
* @param serviceParameterName the serviceParameterName to set
*/
public void setServiceParameterName(final String serviceParameterName) {
this.serviceParameterName = serviceParameterName;
}
/**
* @param ticketValidator the ticketValidator to set
*/
public void setTicketValidator(final TicketValidator ticketValidator) {
this.ticketValidator = ticketValidator;
}
/**
* @param realm the realm to set
*/
public void setRealm(final CasRealm realm) {
this.realm = realm;
}
private void setUnauthorized(final HttpServletResponse response, final String message) {
try {
if (message != null) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, message);
} else {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
} catch (IOException e) {
throw new IllegalStateException("Error setting 403 status.", e);
}
}
}