diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/BaseConfigurationStrategy.java b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/BaseConfigurationStrategy.java index 0ae9d97..643c008 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/BaseConfigurationStrategy.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/BaseConfigurationStrategy.java @@ -23,6 +23,10 @@ import org.jasig.cas.client.util.ReflectUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + /** * Base class to provide most of the boiler-plate code (i.e. checking for proper values, returning defaults, etc. * @@ -77,6 +81,38 @@ public abstract class BaseConfigurationStrategy implements ConfigurationStrategy }); } + public Collection getCollection(final ConfigurationKey> configurationKey) { + return getValue(configurationKey, new Parser>() { + public Collection parse(final String value) { + try { + final String[] values = value.split(","); + final Set collection = new HashSet(); + for (final String singleValue : values) { + Object objValue = CommonUtils.parseDouble(singleValue); + if (objValue != null) { + collection.add((T) objValue); + continue; + } + objValue = CommonUtils.parseLong(singleValue); + if (objValue != null) { + collection.add((T) objValue); + continue; + } + objValue = CommonUtils.parseClass(singleValue); + if (objValue != null) { + collection.add((T) objValue); + continue; + } + collection.add((T) singleValue); + } + return collection; + } catch (final IllegalArgumentException e) { + return configurationKey.getDefaultValue(); + } + } + }); + } + private T getValue(final ConfigurationKey configurationKey, final Parser parser) { final String value = getWithCheck(configurationKey); diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationKeys.java b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationKeys.java index 14ec120..fe67d14 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationKeys.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationKeys.java @@ -24,9 +24,11 @@ import org.jasig.cas.client.authentication.DefaultGatewayResolverImpl; import org.jasig.cas.client.authentication.GatewayResolver; import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl; +import org.jasig.cas.client.validation.AssertionValidator; import org.jasig.cas.client.validation.Cas20ServiceTicketValidator; import javax.net.ssl.HostnameVerifier; +import java.util.Collection; /** * Holder interface for all known configuration keys. @@ -51,6 +53,7 @@ public interface ConfigurationKeys { ConfigurationKey GATEWAY = new ConfigurationKey("gateway", Boolean.FALSE); ConfigurationKey> AUTHENTICATION_REDIRECT_STRATEGY_CLASS = new ConfigurationKey>("authenticationRedirectStrategyClass", null); ConfigurationKey> GATEWAY_STORAGE_CLASS = new ConfigurationKey>("gatewayStorageClass", DefaultGatewayResolverImpl.class); + ConfigurationKey>> ASSERTION_VALIDATOR_CLASS = new ConfigurationKey>>("assertionValidatorClasses"); ConfigurationKey CAS_SERVER_URL_PREFIX = new ConfigurationKey("casServerUrlPrefix", null); ConfigurationKey ENCODING = new ConfigurationKey("encoding", null); ConfigurationKey TOLERANCE = new ConfigurationKey("tolerance", 1000L); diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationStrategy.java b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationStrategy.java index 1493d72..fc47815 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationStrategy.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationStrategy.java @@ -20,6 +20,7 @@ package org.jasig.cas.client.configuration; import javax.servlet.Filter; import javax.servlet.FilterConfig; +import java.util.Collection; /** * Abstraction to allow for pluggable methods for retrieving filter configuration. @@ -69,6 +70,15 @@ public interface ConfigurationStrategy { */ Class getClass(ConfigurationKey> configurationKey); + /** + * Retrieves the value for the provided {@param configurationKey}, falling back to the {@param configurationKey}'s + * {@link ConfigurationKey#getDefaultValue()} if nothing can be found. + * + * @param configurationKey the configuration key. MUST NOT BE NULL. + * @return the configured value, or the default value. + */ + Collection getCollection(ConfigurationKey> configurationKey); + /** * Initializes the strategy. This must be called before calling any of the "get" methods. * diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/AbstractConfigurationFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/AbstractConfigurationFilter.java index 9d3e542..1deb289 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/util/AbstractConfigurationFilter.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/AbstractConfigurationFilter.java @@ -28,6 +28,8 @@ import org.jasig.cas.client.configuration.ConfigurationStrategyName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collection; + /** * Abstracts out the ability to configure the filters from the initial properties provided. * @@ -45,7 +47,7 @@ public abstract class AbstractConfigurationFilter implements Filter { private ConfigurationStrategy configurationStrategy; - public void init(FilterConfig filterConfig) throws ServletException { + public void init(final FilterConfig filterConfig) throws ServletException { final String configurationStrategyName = filterConfig.getServletContext().getInitParameter(CONFIGURATION_STRATEGY_KEY); this.configurationStrategy = ReflectUtils.newInstance(ConfigurationStrategyName.resolveToConfigurationStrategy(configurationStrategyName)); this.configurationStrategy.init(filterConfig, getClass()); @@ -71,6 +73,10 @@ public abstract class AbstractConfigurationFilter implements Filter { return this.configurationStrategy.getClass(configurationKey); } + protected final Collection getCollection(final ConfigurationKey> configurationKey) { + return this.configurationStrategy.getCollection(configurationKey); + } + public final void setIgnoreInitConfiguration(final boolean ignoreInitConfiguration) { this.ignoreInitConfiguration = ignoreInitConfiguration; } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/CommonUtils.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/CommonUtils.java index 639b3b4..3e66764 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/util/CommonUtils.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/CommonUtils.java @@ -201,6 +201,30 @@ public final class CommonUtils { } } + public static Double parseDouble(final String value) { + try { + return Double.parseDouble(value); + } catch (final Exception e) { + return null; + } + } + + public static Long parseLong(final String value) { + try { + return Long.parseLong(value); + } catch (final Exception e) { + return null; + } + } + + public static Class parseClass(final String value) { + try { + return Class.forName(value); + } catch (final Exception e) { + return null; + } + } + public static void readAndRespondToProxyReceptorRequest(final HttpServletRequest request, final HttpServletResponse response, final ProxyGrantingTicketStorage proxyGrantingTicketStorage) throws IOException { diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractTicketValidationFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractTicketValidationFilter.java index 51df207..ab74395 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractTicketValidationFilter.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractTicketValidationFilter.java @@ -20,6 +20,9 @@ package org.jasig.cas.client.validation; import java.io.FileInputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Properties; import javax.net.ssl.HostnameVerifier; import javax.servlet.*; @@ -27,6 +30,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.jasig.cas.client.Protocol; +import org.jasig.cas.client.authentication.GatewayResolver; +import org.jasig.cas.client.configuration.ConfigurationKey; import org.jasig.cas.client.configuration.ConfigurationKeys; import org.jasig.cas.client.util.AbstractCasFilter; import org.jasig.cas.client.util.CommonUtils; @@ -52,6 +57,9 @@ import org.jasig.cas.client.util.ReflectUtils; */ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter { + /** Assertion validator instance. Default is to do nothing. */ + private List assertionValidators = new ArrayList(); + /** The TicketValidator we will use to validate tickets. */ private TicketValidator ticketValidator; @@ -138,6 +146,16 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter { } setTicketValidator(getTicketValidator(filterConfig)); + final ConfigurationKey key = ConfigurationKeys.ASSERTION_VALIDATOR_CLASS; + final Collection> assertionValidatorClasses = getCollection(key); + + for (final Class assertionValidatorClass : assertionValidatorClasses) { + this. + } + if (assertionValidatorClass != null) { + + } + super.initInternal(filterConfig); } @@ -166,8 +184,8 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter { * if ticket validation succeeds. This method is called after all ValidationFilter processing required for a successful authentication * occurs. * - * @param request the HttpServletRequest. - * @param response the HttpServletResponse. + * @param request the HttpServletRequest. + * @param response the HttpServletResponse. * @param assertion the successful Assertion from the server. */ protected void onSuccessfulValidation(final HttpServletRequest request, final HttpServletResponse response, @@ -204,6 +222,11 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter { final Assertion assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response)); + if (this.assertionValidator != null) { + logger.debug("Validating assertion via {}", this.assertionValidator); + this.assertionValidator.validate(assertion); + } + logger.debug("Successfully authenticated user: {}", assertion.getPrincipal().getName()); request.setAttribute(CONST_CAS_ASSERTION, assertion); @@ -252,4 +275,8 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter { public final void setUseSession(final boolean useSession) { this.useSession = useSession; } -} \ No newline at end of file + + public void setAssertionValidators(final List assertionValidators) { + this.assertionValidators = assertionValidators; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/AssertionValidationException.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AssertionValidationException.java new file mode 100644 index 0000000..fff9640 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AssertionValidationException.java @@ -0,0 +1,37 @@ +package org.jasig.cas.client.validation; + +/** + * Thrown when assertion validation fails. + * + * @author Misagh Moayyed + */ +public class AssertionValidationException extends TicketValidationException { + private static final long serialVersionUID = 6304299836257355593L; + + /** + * Constructs an exception with the supplied message. + * + * @param string the message + */ + public AssertionValidationException(final String string) { + super(string); + } + + /** + * Constructs an exception with the supplied message and chained throwable. + * + * @param string the message + * @param throwable the original exception + */ + public AssertionValidationException(final String string, final Throwable throwable) { + super(string, throwable); + } + + /** + * Constructs an exception with the chained throwable. + * @param throwable the original exception. + */ + public AssertionValidationException(final Throwable throwable) { + super(throwable); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/AssertionValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AssertionValidator.java new file mode 100644 index 0000000..cc0f1f4 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AssertionValidator.java @@ -0,0 +1,17 @@ +package org.jasig.cas.client.validation; + +/** + * Executes additional proceesses on the retrieved assertion + * to ensure compliance with the application requirements. + * + * @author Misagh Moayyed + */ +public interface AssertionValidator { + + /** + * Validate the given assertion. + * @param assertion retrieved and constructed once the ticket is validated. + * @throws AssertionValidationException throws on failures + */ + void validate(Assertion assertion) throws AssertionValidationException; +}