casc-210: Merge branch 'master' of https://github.com/jasig/java-cas-client into CASC-210

Resolved Conflicts:
	cas-client-core/src/main/java/org/jasig/cas/client/validation/ProxyList.java
This commit is contained in:
John Gasper 2015-06-16 12:09:37 -07:00
commit f98e776869
88 changed files with 3897 additions and 719 deletions

24
.travis.yml Normal file
View File

@ -0,0 +1,24 @@
#
# 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.
#
before_install:
- mvn -v
- java -version
script: mvn install
language: java

10
NOTICE
View File

@ -16,21 +16,22 @@ specific language governing permissions and limitations
under the License.
This project includes:
"Java Concurrency in Practice" book annotations under Creative Commons Attribution License
AOP alliance under Public Domain
Apache Log4j under The Apache Software License, Version 2.0
Apache Santuario under The Apache Software License, Version 2.0
Apache Velocity under The Apache Software License, Version 2.0
Apache XML Security under The Apache Software License, Version 2.0
Atlassian JIRA - Code - Core under Atlassian End User License
Atlassian Seraph under BSD License
atlassian-osuser under BSD License
Bouncy Castle Provider under Bouncy Castle Licence
catalina under Apache License, Version 2.0
Codec under The Apache Software License, Version 2.0
com.atlassian.confluence:confluence under Atlassian End User License
com.atlassian.event:atlassian-event under Atlassian End User License
com.atlassian.jira:jira-core under Atlassian End User License
com.atlassian.osuser:atlassian-osuser under Atlassian End User License
com.atlassian.seraph:atlassian-seraph under Atlassian End User License
Commons Codec under The Apache Software License, Version 2.0
commons-collections under Apache License, Version 2.0
Confluence Core under Atlassian End User License
Ehcache Core under The Apache Software License, Version 2.0
ESAPI 2.0 under BSD or Creative Commons 3.0 BY-SA
Google Collections Library under The Apache Software License, Version 2.0
@ -49,7 +50,6 @@ This project includes:
JavaBeans Activation Framework (JAF) under Common Development and Distribution License (CDDL) v1.0
JavaMail API under Common Development and Distribution License (CDDL) v1.0
JBoss Application Server Tomcat under lgpl
jcip-annotations under Creative Commons Attribution License
JCL 1.1.1 implemented over SLF4J under MIT License
Joda time under Apache 2
JUL to SLF4J bridge under MIT License

1201
README.md Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +0,0 @@
BUILDING THE CAS CLIENT FOR JAVA
Please note that to be deployed in Maven Central, we mark a number of JARs as provided (related to JBoss and Memcache
Clients). In order to build the clients, you must enable the commented out repositories in the appropriate pom.xml
files in the modules (cas-client-integration-jboss and cas-client-support-distributed-memcached) or follow the
instructions on how to install the file manually.

View File

@ -16,6 +16,7 @@ specific language governing permissions and limitations
under the License.
This project includes:
"Java Concurrency in Practice" book annotations under Creative Commons Attribution License
AOP alliance under Public Domain
Apache Log4j under The Apache Software License, Version 2.0
Apache Santuario under The Apache Software License, Version 2.0
@ -30,7 +31,6 @@ This project includes:
Java Servlet API under CDDL + GPLv2 with classpath exception
JavaBeans Activation Framework (JAF) under Common Development and Distribution License (CDDL) v1.0
JavaMail API under Common Development and Distribution License (CDDL) v1.0
jcip-annotations under Creative Commons Attribution License
JCL 1.1.1 implemented over SLF4J under MIT License
Joda time under Apache 2
JUL to SLF4J bridge under MIT License

View File

@ -1,7 +1,7 @@
<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">
<parent>
<groupId>org.jasig.cas.client</groupId>
<version>3.3.2-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
<artifactId>cas-client</artifactId>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -10,7 +10,30 @@
<packaging>jar</packaging>
<name>Jasig CAS Client for Java - Core</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>xml-security</groupId>
<artifactId>xmlsec</artifactId>
@ -19,20 +42,6 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml</artifactId>
<version>${opensaml.version}</version>
<type>jar</type>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
@ -51,27 +60,18 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
@ -97,8 +97,4 @@
</dependency>
</dependencies>
<properties>
<spring.version>3.1.3.RELEASE</spring.version>
<opensaml.version>2.5.1-1</opensaml.version>
</properties>
</project>

View File

@ -0,0 +1,51 @@
/*
* 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;
/**
* Simple enumeration to hold/capture some of the standard request parameters used by the various protocols.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public enum Protocol {
CAS1("ticket", "service"), CAS2(CAS1), CAS3(CAS2), SAML11("SAMLart", "TARGET");
private final String artifactParameterName;
private final String serviceParameterName;
private Protocol(final String artifactParameterName, final String serviceParameterName) {
this.artifactParameterName = artifactParameterName;
this.serviceParameterName = serviceParameterName;
}
private Protocol(final Protocol protocol) {
this(protocol.getArtifactParameterName(), protocol.getServiceParameterName());
}
public String getArtifactParameterName() {
return this.artifactParameterName;
}
public String getServiceParameterName() {
return this.serviceParameterName;
}
}

View File

@ -27,6 +27,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.ReflectUtils;
@ -79,22 +81,24 @@ public class AuthenticationFilter extends AbstractCasFilter {
PATTERN_MATCHER_TYPES.put("REGEX", RegexUrlPatternMatcherStrategy.class);
PATTERN_MATCHER_TYPES.put("EXACT", ExactUrlPatternMatcherStrategy.class);
}
public AuthenticationFilter() {
this(Protocol.CAS2);
}
protected AuthenticationFilter(final Protocol protocol) {
super(protocol);
}
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
if (!isIgnoreInitConfiguration()) {
super.initInternal(filterConfig);
setCasServerLoginUrl(getPropertyFromInitParams(filterConfig, "casServerLoginUrl", null));
logger.trace("Loaded CasServerLoginUrl parameter: {}", this.casServerLoginUrl);
setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
logger.trace("Loaded renew parameter: {}", this.renew);
setGateway(parseBoolean(getPropertyFromInitParams(filterConfig, "gateway", "false")));
logger.trace("Loaded gateway parameter: {}", this.gateway);
setCasServerLoginUrl(getString(ConfigurationKeys.CAS_SERVER_LOGIN_URL));
setRenew(getBoolean(ConfigurationKeys.RENEW));
setGateway(getBoolean(ConfigurationKeys.GATEWAY));
final String ignorePattern = getPropertyFromInitParams(filterConfig, "ignorePattern", null);
logger.trace("Loaded ignorePattern parameter: {}", ignorePattern);
final String ignoreUrlPatternType = getPropertyFromInitParams(filterConfig, "ignoreUrlPatternType", "REGEX");
logger.trace("Loaded ignoreUrlPatternType parameter: {}", ignoreUrlPatternType);
final String ignorePattern = getString(ConfigurationKeys.IGNORE_PATTERN);
final String ignoreUrlPatternType = getString(ConfigurationKeys.IGNORE_URL_PATTERN_TYPE);
if (ignorePattern != null) {
final Class<? extends UrlPatternMatcherStrategy> ignoreUrlMatcherClass = PATTERN_MATCHER_TYPES.get(ignoreUrlPatternType);
@ -113,14 +117,13 @@ public class AuthenticationFilter extends AbstractCasFilter {
}
}
final String gatewayStorageClass = getPropertyFromInitParams(filterConfig, "gatewayStorageClass", null);
final Class<? extends GatewayResolver> gatewayStorageClass = getClass(ConfigurationKeys.GATEWAY_STORAGE_CLASS);
if (gatewayStorageClass != null) {
this.gatewayStorage = ReflectUtils.newInstance(gatewayStorageClass);
setGatewayStorage(ReflectUtils.newInstance(gatewayStorageClass));
}
final String authenticationRedirectStrategyClass = getPropertyFromInitParams(filterConfig,
"authenticationRedirectStrategyClass", null);
final Class<? extends AuthenticationRedirectStrategy> authenticationRedirectStrategyClass = getClass(ConfigurationKeys.AUTHENTICATION_REDIRECT_STRATEGY_CLASS);
if (authenticationRedirectStrategyClass != null) {
this.authenticationRedirectStrategy = ReflectUtils.newInstance(authenticationRedirectStrategyClass);
@ -175,7 +178,7 @@ public class AuthenticationFilter extends AbstractCasFilter {
logger.debug("Constructed service url: {}", modifiedServiceUrl);
final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl,
getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
logger.debug("redirecting to \"{}\"", urlToRedirectTo);
this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);

View File

@ -0,0 +1,113 @@
/*
* 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.configuration;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.ReflectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Base class to provide most of the boiler-plate code (i.e. checking for proper values, returning defaults, etc.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public abstract class BaseConfigurationStrategy implements ConfigurationStrategy {
protected final Logger logger = LoggerFactory.getLogger(getClass());
public final boolean getBoolean(final ConfigurationKey<Boolean> configurationKey) {
return getValue(configurationKey, new Parser<Boolean>() {
public Boolean parse(final String value) {
return BooleanUtils.toBoolean(value);
}
});
}
public final long getLong(final ConfigurationKey<Long> configurationKey) {
return getValue(configurationKey, new Parser<Long>() {
public Long parse(final String value) {
return NumberUtils.toLong(value, configurationKey.getDefaultValue());
}
});
}
public final int getInt(final ConfigurationKey<Integer> configurationKey) {
return getValue(configurationKey, new Parser<Integer>() {
public Integer parse(final String value) {
return NumberUtils.toInt(value, configurationKey.getDefaultValue());
}
});
}
public final String getString(final ConfigurationKey<String> configurationKey) {
return getValue(configurationKey, new Parser<String>() {
public String parse(final String value) {
return value;
}
});
}
public <T> Class<? extends T> getClass(final ConfigurationKey<Class<? extends T>> configurationKey) {
return getValue(configurationKey, new Parser<Class<? extends T>>() {
public Class<? extends T> parse(final String value) {
try {
return ReflectUtils.loadClass(value);
} catch (final IllegalArgumentException e) {
return configurationKey.getDefaultValue();
}
}
});
}
private <T> T getValue(final ConfigurationKey<T> configurationKey, final Parser<T> parser) {
final String value = getWithCheck(configurationKey);
if (CommonUtils.isBlank(value)) {
logger.trace("No value found for property {}, returning default {}", configurationKey.getName(), configurationKey.getDefaultValue());
return configurationKey.getDefaultValue();
} else {
logger.trace("Loaded property {} with value {}", configurationKey.getName(), configurationKey.getDefaultValue());
}
return parser.parse(value);
}
private String getWithCheck(final ConfigurationKey configurationKey) {
CommonUtils.assertNotNull(configurationKey, "configurationKey cannot be null");
return get(configurationKey);
}
/**
* Retrieve the String value for this key. Returns null if there is no value.
*
* @param configurationKey the key to retrieve. MUST NOT BE NULL.
* @return the String if its found, null otherwise.
*/
protected abstract String get(ConfigurationKey configurationKey);
private interface Parser<T> {
T parse(String value);
}
}

View File

@ -0,0 +1,63 @@
/*
* 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.configuration;
import org.jasig.cas.client.util.CommonUtils;
/**
* Holder class to represent a particular configuration key and its optional default value.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public final class ConfigurationKey<E> {
private final String name;
private final E defaultValue;
public ConfigurationKey(final String name) {
this(name, null);
}
public ConfigurationKey(final String name, final E defaultValue) {
CommonUtils.assertNotNull(name, "name must not be null.");
this.name = name;
this.defaultValue = defaultValue;
}
/**
* The referencing name of the configuration key (i.e. what you would use to look it up in your configuration strategy)
*
* @return the name. MUST NOT BE NULL.
*/
public String getName() {
return this.name;
}
/**
* The (optional) default value to use when this configuration key is not set. If a value is provided it should be used. A <code>null</code> value indicates that there is no default.
*
* @return the default value or null.
*/
public E getDefaultValue() {
return this.defaultValue;
}
}

View File

@ -0,0 +1,82 @@
/*
* 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.configuration;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.authentication.AuthenticationRedirectStrategy;
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.Cas20ServiceTicketValidator;
import javax.net.ssl.HostnameVerifier;
/**
* Holder interface for all known configuration keys.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public interface ConfigurationKeys {
ConfigurationKey<String> ARTIFACT_PARAMETER_NAME = new ConfigurationKey<String>("artifactParameterName", Protocol.CAS2.getArtifactParameterName());
ConfigurationKey<String> SERVER_NAME = new ConfigurationKey<String>("serverName", null);
ConfigurationKey<String> SERVICE = new ConfigurationKey<String>("service");
ConfigurationKey<Boolean> RENEW = new ConfigurationKey<Boolean>("renew", Boolean.FALSE);
ConfigurationKey<String> LOGOUT_PARAMETER_NAME = new ConfigurationKey<String>("logoutParameterName", "logoutRequest");
ConfigurationKey<Boolean> ARTIFACT_PARAMETER_OVER_POST = new ConfigurationKey<Boolean>("artifactParameterOverPost", Boolean.FALSE);
ConfigurationKey<Boolean> EAGERLY_CREATE_SESSIONS = new ConfigurationKey<Boolean>("eagerlyCreateSessions", Boolean.TRUE);
ConfigurationKey<Boolean> ENCODE_SERVICE_URL = new ConfigurationKey<Boolean>("encodeServiceUrl", Boolean.TRUE);
ConfigurationKey<String> SSL_CONFIG_FILE = new ConfigurationKey<String>("sslConfigFile", null);
ConfigurationKey<String> ROLE_ATTRIBUTE = new ConfigurationKey<String>("roleAttribute", null);
ConfigurationKey<Boolean> IGNORE_CASE = new ConfigurationKey<Boolean>("ignoreCase", Boolean.FALSE);
ConfigurationKey<String> CAS_SERVER_LOGIN_URL = new ConfigurationKey<String>("casServerLoginUrl", null);
ConfigurationKey<Boolean> GATEWAY = new ConfigurationKey<Boolean>("gateway", Boolean.FALSE);
ConfigurationKey<Class<? extends AuthenticationRedirectStrategy>> AUTHENTICATION_REDIRECT_STRATEGY_CLASS = new ConfigurationKey<Class<? extends AuthenticationRedirectStrategy>>("authenticationRedirectStrategyClass", null);
ConfigurationKey<Class<? extends GatewayResolver>> GATEWAY_STORAGE_CLASS = new ConfigurationKey<Class<? extends GatewayResolver>>("gatewayStorageClass", DefaultGatewayResolverImpl.class);
ConfigurationKey<String> CAS_SERVER_URL_PREFIX = new ConfigurationKey<String>("casServerUrlPrefix", null);
ConfigurationKey<String> ENCODING = new ConfigurationKey<String>("encoding", null);
ConfigurationKey<Long> TOLERANCE = new ConfigurationKey<Long>("tolerance", 1000L);
/**
* @deprecated As of 3.4. This constant is not used by the client and will
* be removed in future versions.
*/
@Deprecated
ConfigurationKey<Boolean> DISABLE_XML_SCHEMA_VALIDATION = new ConfigurationKey<Boolean>("disableXmlSchemaValidation", Boolean.FALSE);
ConfigurationKey<String> IGNORE_PATTERN = new ConfigurationKey<String>("ignorePattern", null);
ConfigurationKey<String> IGNORE_URL_PATTERN_TYPE = new ConfigurationKey<String>("ignoreUrlPatternType", "REGEX");
ConfigurationKey<Class<? extends HostnameVerifier>> HOSTNAME_VERIFIER = new ConfigurationKey<Class<? extends HostnameVerifier>>("hostnameVerifier", null);
ConfigurationKey<String> HOSTNAME_VERIFIER_CONFIG = new ConfigurationKey<String>("hostnameVerifierConfig", null);
ConfigurationKey<Boolean> EXCEPTION_ON_VALIDATION_FAILURE = new ConfigurationKey<Boolean>("exceptionOnValidationFailure", Boolean.TRUE);
ConfigurationKey<Boolean> REDIRECT_AFTER_VALIDATION = new ConfigurationKey<Boolean>("redirectAfterValidation", Boolean.TRUE);
ConfigurationKey<Boolean> USE_SESSION = new ConfigurationKey<Boolean>("useSession", Boolean.TRUE);
ConfigurationKey<String> SECRET_KEY = new ConfigurationKey<String>("secretKey", null);
ConfigurationKey<String> CIPHER_ALGORITHM = new ConfigurationKey<String>("cipherAlgorithm", "DESede");
ConfigurationKey<String> PROXY_RECEPTOR_URL = new ConfigurationKey<String>("proxyReceptorUrl", null);
ConfigurationKey<Class<? extends ProxyGrantingTicketStorage>> PROXY_GRANTING_TICKET_STORAGE_CLASS = new ConfigurationKey<Class<? extends ProxyGrantingTicketStorage>>("proxyGrantingTicketStorageClass", ProxyGrantingTicketStorageImpl.class);
ConfigurationKey<Integer> MILLIS_BETWEEN_CLEAN_UPS = new ConfigurationKey<Integer>("millisBetweenCleanUps", 60000);
ConfigurationKey<Boolean> ACCEPT_ANY_PROXY = new ConfigurationKey<Boolean>("acceptAnyProxy", Boolean.FALSE);
ConfigurationKey<String> ALLOWED_PROXY_CHAINS = new ConfigurationKey<String>("allowedProxyChains", null);
ConfigurationKey<Class<? extends Cas20ServiceTicketValidator>> TICKET_VALIDATOR_CLASS = new ConfigurationKey<Class<? extends Cas20ServiceTicketValidator>>("ticketValidatorClass", null);
ConfigurationKey<String> PROXY_CALLBACK_URL = new ConfigurationKey<String>("proxyCallbackUrl", null);
ConfigurationKey<String> FRONT_LOGOUT_PARAMETER_NAME = new ConfigurationKey<String>("frontLogoutParameterName", "SAMLRequest");
ConfigurationKey<String> RELAY_STATE_PARAMETER_NAME = new ConfigurationKey<String>("relayStateParameterName", "RelayState");
}

View File

@ -0,0 +1,79 @@
/*
* 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.configuration;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
/**
* Abstraction to allow for pluggable methods for retrieving filter configuration.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public interface ConfigurationStrategy {
/**
* 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.
*/
boolean getBoolean(ConfigurationKey<Boolean> 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.
*/
String getString(ConfigurationKey<String> 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.
*/
long getLong(ConfigurationKey<Long> 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.
*/
int getInt(ConfigurationKey<Integer> 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.
*/
<T> Class<? extends T> getClass(ConfigurationKey<Class<? extends T>> configurationKey);
/**
* Initializes the strategy. This must be called before calling any of the "get" methods.
*
* @param filterConfig the filter configuration object.
* @param filterClazz the filter
*/
void init(FilterConfig filterConfig, Class<? extends Filter> filterClazz);
}

View File

@ -0,0 +1,74 @@
/*
* 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.configuration;
import org.jasig.cas.client.util.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Enumeration to map simple names to the underlying classes so that deployers can reference the simple name in the
* <code>web.xml</code> instead of the fully qualified class name.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public enum ConfigurationStrategyName {
DEFAULT(LegacyConfigurationStrategyImpl.class), JNDI(JndiConfigurationStrategyImpl.class), WEB_XML(WebXmlConfigurationStrategyImpl.class),
PROPERTY_FILE(PropertiesConfigurationStrategyImpl.class), SYSTEM_PROPERTIES(SystemPropertiesConfigurationStrategyImpl.class);
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationStrategyName.class);
private final Class<? extends ConfigurationStrategy> configurationStrategyClass;
private ConfigurationStrategyName(final Class<? extends ConfigurationStrategy> configurationStrategyClass) {
this.configurationStrategyClass = configurationStrategyClass;
}
/**
* Static helper method that will resolve a simple string to either an enum value or a {@link org.jasig.cas.client.configuration.ConfigurationStrategy} class.
*
* @param value the value to attempt to resolve.
* @return the underlying class that this maps to (either via simple name or fully qualified class name).
*/
public static Class<? extends ConfigurationStrategy> resolveToConfigurationStrategy(final String value) {
if (CommonUtils.isBlank(value)) {
return DEFAULT.configurationStrategyClass;
}
for (final ConfigurationStrategyName csn : values()) {
if (csn.name().equalsIgnoreCase(value)) {
return csn.configurationStrategyClass;
}
}
try {
final Class<?> clazz = Class.forName(value);
if (clazz.isAssignableFrom(ConfigurationStrategy.class)) {
return (Class<? extends ConfigurationStrategy>) clazz;
}
} catch (final ClassNotFoundException e) {
LOGGER.error("Unable to locate strategy {} by name or class name. Using default strategy instead.", value, e);
}
return DEFAULT.configurationStrategyClass;
}
}

View File

@ -0,0 +1,93 @@
/*
* 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.configuration;
import org.jasig.cas.client.util.CommonUtils;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
/**
* Loads configuration information from JNDI, using the <code>defaultValue</code> if it can't.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public class JndiConfigurationStrategyImpl extends BaseConfigurationStrategy {
private static final String ENVIRONMENT_PREFIX = "java:comp/env/cas/";
private final String environmentPrefix;
private InitialContext context;
private String simpleFilterName;
public JndiConfigurationStrategyImpl() {
this(ENVIRONMENT_PREFIX);
}
public JndiConfigurationStrategyImpl(final String environmentPrefix) {
this.environmentPrefix = environmentPrefix;
}
@Override
protected final String get(final ConfigurationKey configurationKey) {
if (context == null) {
return null;
}
final String propertyName = configurationKey.getName();
final String filterValue = loadFromContext(context, this.environmentPrefix + this.simpleFilterName + "/" + propertyName);
if (CommonUtils.isNotBlank(filterValue)) {
logger.info("Property [{}] loaded from JNDI Filter Specific Property with value [{}]", propertyName, filterValue);
return filterValue;
}
final String rootValue = loadFromContext(context, this.environmentPrefix + propertyName);
if (CommonUtils.isNotBlank(rootValue)) {
logger.info("Property [{}] loaded from JNDI with value [{}]", propertyName, rootValue);
return rootValue;
}
return null;
}
private String loadFromContext(final InitialContext context, final String path) {
try {
return (String) context.lookup(path);
} catch (final NamingException e) {
return null;
}
}
public final void init(final FilterConfig filterConfig, final Class<? extends Filter> clazz) {
this.simpleFilterName = clazz.getSimpleName();
try {
this.context = new InitialContext();
} catch (final NamingException e) {
logger.error("Unable to create InitialContext. No properties can be loaded via JNDI.", e);
}
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.configuration;
import org.jasig.cas.client.util.CommonUtils;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
/**
* Replicates the original behavior by checking the {@link org.jasig.cas.client.configuration.WebXmlConfigurationStrategyImpl} first, and then
* the {@link org.jasig.cas.client.configuration.JndiConfigurationStrategyImpl} before using the <code>defaultValue</code>.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public final class LegacyConfigurationStrategyImpl extends BaseConfigurationStrategy {
private final WebXmlConfigurationStrategyImpl webXmlConfigurationStrategy = new WebXmlConfigurationStrategyImpl();
private final JndiConfigurationStrategyImpl jndiConfigurationStrategy = new JndiConfigurationStrategyImpl();
public void init(FilterConfig filterConfig, Class<? extends Filter> filterClazz) {
this.webXmlConfigurationStrategy.init(filterConfig, filterClazz);
this.jndiConfigurationStrategy.init(filterConfig, filterClazz);
}
protected String get(final ConfigurationKey key) {
final String value1 = this.webXmlConfigurationStrategy.get(key);
if (CommonUtils.isNotBlank(value1)) {
return value1;
}
return this.jndiConfigurationStrategy.get(key);
}
}

View File

@ -0,0 +1,101 @@
/*
* 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.configuration;
import org.jasig.cas.client.util.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
/**
* @author Scott Battaglia
* @since 3.4.0
*/
public final class PropertiesConfigurationStrategyImpl extends BaseConfigurationStrategy {
/**
* Property name we'll use in the {@link javax.servlet.FilterConfig} and {@link javax.servlet.ServletConfig} to try and find where
* you stored the configuration file.
*/
private static final String CONFIGURATION_FILE_LOCATION = "configFileLocation";
/**
* Default location of the configuration file. Mostly for testing/demo. You will most likely want to configure an alternative location.
*/
private static final String DEFAULT_CONFIGURATION_FILE_LOCATION = "/etc/java-cas-client.properties";
private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesConfigurationStrategyImpl.class);
private String simpleFilterName;
private Properties properties = new Properties();
@Override
protected String get(final ConfigurationKey configurationKey) {
final String property = configurationKey.getName();
final String filterSpecificProperty = this.simpleFilterName + "." + property;
final String filterSpecificValue = this.properties.getProperty(filterSpecificProperty);
if (CommonUtils.isNotEmpty(filterSpecificValue)) {
return filterSpecificValue;
}
return this.properties.getProperty(property);
}
public void init(final FilterConfig filterConfig, final Class<? extends Filter> filterClazz) {
this.simpleFilterName = filterClazz.getSimpleName();
final String fileLocationFromFilterConfig = filterConfig.getInitParameter(CONFIGURATION_FILE_LOCATION);
final boolean filterConfigFileLoad = loadPropertiesFromFile(fileLocationFromFilterConfig);
if (!filterConfigFileLoad) {
final String fileLocationFromServletConfig = filterConfig.getServletContext().getInitParameter(CONFIGURATION_FILE_LOCATION);
final boolean servletContextFileLoad = loadPropertiesFromFile(fileLocationFromServletConfig);
if (!servletContextFileLoad) {
final boolean defaultConfigFileLoaded = loadPropertiesFromFile(DEFAULT_CONFIGURATION_FILE_LOCATION);
CommonUtils.assertTrue(defaultConfigFileLoaded, "unable to load properties to configure CAS client");
}
}
}
private boolean loadPropertiesFromFile(final String file) {
if (CommonUtils.isEmpty(file)) {
return false;
}
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
this.properties.load(fis);
return true;
} catch (final IOException e) {
LOGGER.warn("Unable to load properties for file {}", file, e);
return false;
} finally {
CommonUtils.closeQuietly(fis);
}
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.configuration;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
/**
* Load all configuration from system properties.
*
* @author Jerome Leleu
* @since 3.4.0
*/
public class SystemPropertiesConfigurationStrategyImpl extends BaseConfigurationStrategy {
public void init(FilterConfig filterConfig, Class<? extends Filter> filterClazz) {
}
@Override
protected String get(ConfigurationKey configurationKey) {
return System.getProperty(configurationKey.getName());
}
}

View File

@ -0,0 +1,60 @@
/*
* 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.configuration;
import org.jasig.cas.client.util.CommonUtils;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
/**
* Implementation of the {@link org.jasig.cas.client.configuration.ConfigurationStrategy} that first checks the {@link javax.servlet.FilterConfig} and
* then checks the {@link javax.servlet.ServletContext}, ultimately falling back to the <code>defaultValue</code>.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public final class WebXmlConfigurationStrategyImpl extends BaseConfigurationStrategy {
private FilterConfig filterConfig;
protected String get(final ConfigurationKey configurationKey) {
final String value = this.filterConfig.getInitParameter(configurationKey.getName());
if (CommonUtils.isNotBlank(value)) {
CommonUtils.assertFalse(ConfigurationKeys.RENEW.equals(configurationKey), "Renew MUST be specified via context parameter or JNDI environment to avoid misconfiguration.");
logger.info("Property [{}] loaded from FilterConfig.getInitParameter with value [{}]", configurationKey, value);
return value;
}
final String value2 = filterConfig.getServletContext().getInitParameter(configurationKey.getName());
if (CommonUtils.isNotBlank(value2)) {
logger.info("Property [{}] loaded from ServletContext.getInitParameter with value [{}]", configurationKey,
value2);
return value2;
}
return null;
}
public void init(final FilterConfig filterConfig, final Class<? extends Filter> clazz) {
this.filterConfig = filterConfig;
}
}

View File

@ -29,6 +29,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
@ -50,12 +51,16 @@ import org.jasig.cas.client.util.CommonUtils;
*/
public final class Servlet3AuthenticationFilter extends AbstractCasFilter {
public Servlet3AuthenticationFilter() {
super(Protocol.CAS2);
}
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
final FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final HttpSession session = request.getSession();
final String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());
final String ticket = CommonUtils.safeGetParameter(request, getProtocol().getArtifactParameterName());
if (session != null && session.getAttribute(CONST_CAS_ASSERTION) == null && ticket != null) {
try {

View File

@ -18,6 +18,8 @@
*/
package org.jasig.cas.client.proxy;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
@ -36,11 +38,9 @@ import javax.crypto.spec.DESedeKeySpec;
*/
public abstract class AbstractEncryptedProxyGrantingTicketStorageImpl implements ProxyGrantingTicketStorage {
public static final String DEFAULT_ENCRYPTION_ALGORITHM = "DESede";
private Key key;
private String cipherAlgorithm = DEFAULT_ENCRYPTION_ALGORITHM;
private String cipherAlgorithm = ConfigurationKeys.CIPHER_ALGORITHM.getDefaultValue();
public final void setSecretKey(final String key) throws NoSuchAlgorithmException, InvalidKeyException,
InvalidKeySpecException {

View File

@ -55,6 +55,11 @@ public final class Cas20ProxyRetriever implements ProxyRetriever {
/** Url connection factory to use when communicating with the server **/
private final HttpURLConnectionFactory urlConnectionFactory;
@Deprecated
public Cas20ProxyRetriever(final String casServerUrl, final String encoding) {
this(casServerUrl, encoding, null);
}
/**
* Main Constructor.
*
@ -75,7 +80,13 @@ public final class Cas20ProxyRetriever implements ProxyRetriever {
CommonUtils.assertNotNull(targetService, "targetService cannot be null.");
final URL url = constructUrl(proxyGrantingTicketId, targetService);
final String response = CommonUtils.getResponseFromServer(url, this.urlConnectionFactory, this.encoding);
final String response;
if (this.urlConnectionFactory != null) {
response = CommonUtils.getResponseFromServer(url, this.urlConnectionFactory, this.encoding);
} else {
response = CommonUtils.getResponseFromServer(url, this.encoding);
}
final String error = XmlUtils.getTextForElement(response, "proxyFailure");
if (CommonUtils.isNotEmpty(error)) {

View File

@ -19,10 +19,13 @@
package org.jasig.cas.client.session;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.jasig.cas.client.util.AbstractConfigurationFilter;
/**
@ -34,49 +37,47 @@ import org.jasig.cas.client.util.AbstractConfigurationFilter;
*/
public final class SingleSignOutFilter extends AbstractConfigurationFilter {
private static final SingleSignOutHandler handler = new SingleSignOutHandler();
private static final SingleSignOutHandler HANDLER = new SingleSignOutHandler();
private AtomicBoolean handlerInitialized = new AtomicBoolean(false);
public void init(final FilterConfig filterConfig) throws ServletException {
super.init(filterConfig);
if (!isIgnoreInitConfiguration()) {
handler.setArtifactParameterName(getPropertyFromInitParams(filterConfig, "artifactParameterName",
SingleSignOutHandler.DEFAULT_ARTIFACT_PARAMETER_NAME));
handler.setLogoutParameterName(getPropertyFromInitParams(filterConfig, "logoutParameterName",
SingleSignOutHandler.DEFAULT_LOGOUT_PARAMETER_NAME));
handler.setFrontLogoutParameterName(getPropertyFromInitParams(filterConfig, "frontLogoutParameterName",
SingleSignOutHandler.DEFAULT_FRONT_LOGOUT_PARAMETER_NAME));
handler.setRelayStateParameterName(getPropertyFromInitParams(filterConfig, "relayStateParameterName",
SingleSignOutHandler.DEFAULT_RELAY_STATE_PARAMETER_NAME));
handler.setCasServerUrlPrefix(getPropertyFromInitParams(filterConfig, "casServerUrlPrefix", null));
handler.setArtifactParameterOverPost(parseBoolean(getPropertyFromInitParams(filterConfig,
"artifactParameterOverPost", "false")));
handler.setEagerlyCreateSessions(parseBoolean(getPropertyFromInitParams(filterConfig,
"eagerlyCreateSessions", "true")));
setArtifactParameterName(getString(ConfigurationKeys.ARTIFACT_PARAMETER_NAME));
setLogoutParameterName(getString(ConfigurationKeys.LOGOUT_PARAMETER_NAME));
setFrontLogoutParameterName(getString(ConfigurationKeys.FRONT_LOGOUT_PARAMETER_NAME));
setRelayStateParameterName(getString(ConfigurationKeys.RELAY_STATE_PARAMETER_NAME));
setCasServerUrlPrefix(getString(ConfigurationKeys.CAS_SERVER_URL_PREFIX));
HANDLER.setArtifactParameterOverPost(getBoolean(ConfigurationKeys.ARTIFACT_PARAMETER_OVER_POST));
HANDLER.setEagerlyCreateSessions(getBoolean(ConfigurationKeys.EAGERLY_CREATE_SESSIONS));
}
handler.init();
HANDLER.init();
handlerInitialized.set(true);
}
public void setArtifactParameterName(final String name) {
handler.setArtifactParameterName(name);
HANDLER.setArtifactParameterName(name);
}
public void setLogoutParameterName(final String name) {
handler.setLogoutParameterName(name);
HANDLER.setLogoutParameterName(name);
}
public void setFrontLogoutParameterName(final String name) {
handler.setFrontLogoutParameterName(name);
HANDLER.setFrontLogoutParameterName(name);
}
public void setRelayStateParameterName(final String name) {
handler.setRelayStateParameterName(name);
HANDLER.setRelayStateParameterName(name);
}
public void setCasServerUrlPrefix(final String casServerUrlPrefix) {
handler.setCasServerUrlPrefix(casServerUrlPrefix);
HANDLER.setCasServerUrlPrefix(casServerUrlPrefix);
}
public void setSessionMappingStorage(final SessionMappingStorage storage) {
handler.setSessionMappingStorage(storage);
HANDLER.setSessionMappingStorage(storage);
}
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
@ -84,7 +85,15 @@ public final class SingleSignOutFilter extends AbstractConfigurationFilter {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
if (handler.process(request, response)) {
/**
* <p>Workaround for now for the fact that Spring Security will fail since it doesn't call {@link #init(javax.servlet.FilterConfig)}.</p>
* <p>Ultimately we need to allow deployers to actually inject their fully-initialized {@link org.jasig.cas.client.session.SingleSignOutHandler}.</p>
*/
if (!this.handlerInitialized.getAndSet(true)) {
HANDLER.init();
}
if (HANDLER.process(request, response)) {
filterChain.doFilter(servletRequest, servletResponse);
}
}
@ -94,6 +103,6 @@ public final class SingleSignOutFilter extends AbstractConfigurationFilter {
}
protected static SingleSignOutHandler getSingleSignOutHandler() {
return handler;
return HANDLER;
}
}

View File

@ -28,7 +28,9 @@ import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.XmlUtils;
import org.slf4j.Logger;
@ -44,11 +46,6 @@ import org.slf4j.LoggerFactory;
*/
public final class SingleSignOutHandler {
public final static String DEFAULT_ARTIFACT_PARAMETER_NAME = "ticket";
public final static String DEFAULT_LOGOUT_PARAMETER_NAME = "logoutRequest";
public final static String DEFAULT_FRONT_LOGOUT_PARAMETER_NAME = "SAMLRequest";
public final static String DEFAULT_RELAY_STATE_PARAMETER_NAME = "RelayState";
private final static int DECOMPRESSION_FACTOR = 10;
/** Logger instance */
@ -58,19 +55,19 @@ public final class SingleSignOutHandler {
private SessionMappingStorage sessionMappingStorage = new HashMapBackedSessionMappingStorage();
/** The name of the artifact parameter. This is used to capture the session identifier. */
private String artifactParameterName = DEFAULT_ARTIFACT_PARAMETER_NAME;
private String artifactParameterName = Protocol.CAS2.getArtifactParameterName();
/** Parameter name that stores logout request for back channel SLO */
private String logoutParameterName = DEFAULT_LOGOUT_PARAMETER_NAME;
private String logoutParameterName = ConfigurationKeys.LOGOUT_PARAMETER_NAME.getDefaultValue();
/** Parameter name that stores logout request for front channel SLO */
private String frontLogoutParameterName = DEFAULT_FRONT_LOGOUT_PARAMETER_NAME;
private String frontLogoutParameterName = ConfigurationKeys.FRONT_LOGOUT_PARAMETER_NAME.getDefaultValue();
/** Parameter name that stores the state of the CAS server webflow for the callback */
private String relayStateParameterName = DEFAULT_RELAY_STATE_PARAMETER_NAME;
private String relayStateParameterName = ConfigurationKeys.RELAY_STATE_PARAMETER_NAME.getDefaultValue();
/** The prefix url of the CAS server */
private String casServerUrlPrefix;
private String casServerUrlPrefix = "";
private boolean artifactParameterOverPost = false;
@ -78,6 +75,8 @@ public final class SingleSignOutHandler {
private List<String> safeParameters;
private LogoutStrategy logoutStrategy = isServlet30() ? new Servlet30LogoutStrategy() : new Servlet25LogoutStrategy();
public void setSessionMappingStorage(final SessionMappingStorage storage) {
this.sessionMappingStorage = storage;
}
@ -132,18 +131,24 @@ public final class SingleSignOutHandler {
/**
* Initializes the component for use.
*/
public void init() {
CommonUtils.assertNotNull(this.artifactParameterName, "artifactParameterName cannot be null.");
CommonUtils.assertNotNull(this.logoutParameterName, "logoutParameterName cannot be null.");
CommonUtils.assertNotNull(this.frontLogoutParameterName, "frontLogoutParameterName cannot be null.");
CommonUtils.assertNotNull(this.sessionMappingStorage, "sessionMappingStorage cannot be null.");
CommonUtils.assertNotNull(this.relayStateParameterName, "relayStateParameterName cannot be null.");
CommonUtils.assertNotNull(this.casServerUrlPrefix, "casServerUrlPrefix cannot be null.");
public synchronized void init() {
if (this.safeParameters == null) {
CommonUtils.assertNotNull(this.artifactParameterName, "artifactParameterName cannot be null.");
CommonUtils.assertNotNull(this.logoutParameterName, "logoutParameterName cannot be null.");
CommonUtils.assertNotNull(this.frontLogoutParameterName, "frontLogoutParameterName cannot be null.");
CommonUtils.assertNotNull(this.sessionMappingStorage, "sessionMappingStorage cannot be null.");
CommonUtils.assertNotNull(this.relayStateParameterName, "relayStateParameterName cannot be null.");
CommonUtils.assertNotNull(this.casServerUrlPrefix, "casServerUrlPrefix cannot be null.");
if (this.artifactParameterOverPost) {
this.safeParameters = Arrays.asList(this.logoutParameterName, this.artifactParameterName);
} else {
this.safeParameters = Arrays.asList(this.logoutParameterName);
if (CommonUtils.isBlank(this.casServerUrlPrefix)) {
logger.warn("Front Channel single sign out redirects are disabled when the 'casServerUrlPrefix' value is not set.");
}
if (this.artifactParameterOverPost) {
this.safeParameters = Arrays.asList(this.logoutParameterName, this.artifactParameterName);
} else {
this.safeParameters = Arrays.asList(this.logoutParameterName);
}
}
}
@ -174,14 +179,15 @@ public final class SingleSignOutHandler {
}
/**
* Determines whether the given request is a CAS front channel logout request.
* Determines whether the given request is a CAS front channel logout request. Front Channel log out requests are only supported
* when the 'casServerUrlPrefix' value is set.
*
* @param request HTTP request.
*
* @return True if request is logout request, false otherwise.
*/
private boolean isFrontChannelLogoutRequest(final HttpServletRequest request) {
return "GET".equals(request.getMethod())
return "GET".equals(request.getMethod()) && CommonUtils.isNotBlank(this.casServerUrlPrefix)
&& CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.frontLogoutParameterName));
}
@ -304,11 +310,7 @@ public final class SingleSignOutHandler {
} catch (final IllegalStateException e) {
logger.debug("Error invalidating session.", e);
}
try {
request.logout();
} catch (final ServletException e) {
logger.debug("Error performing request.logout.");
}
this.logoutStrategy.logout(request);
}
}
}
@ -343,4 +345,39 @@ public final class SingleSignOutHandler {
private boolean isMultipartRequest(final HttpServletRequest request) {
return request.getContentType() != null && request.getContentType().toLowerCase().startsWith("multipart");
}
private static boolean isServlet30() {
try {
return HttpServletRequest.class.getMethod("logout") != null;
} catch (final NoSuchMethodException e) {
return false;
}
}
/**
* Abstracts the ways we can force logout with the Servlet spec.
*/
private interface LogoutStrategy {
void logout(HttpServletRequest request);
}
private class Servlet25LogoutStrategy implements LogoutStrategy {
public void logout(final HttpServletRequest request) {
// nothing additional to do here
}
}
private class Servlet30LogoutStrategy implements LogoutStrategy {
public void logout(final HttpServletRequest request) {
try {
request.logout();
} catch (final ServletException e) {
logger.debug("Error performing request.logout.");
}
}
}
}

View File

@ -18,6 +18,7 @@
*/
package org.jasig.cas.client.ssl;
import java.io.Serializable;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
@ -28,7 +29,7 @@ import java.net.URLConnection;
* @author Misagh Moayyed
* @since 3.3
*/
public interface HttpURLConnectionFactory {
public interface HttpURLConnectionFactory extends Serializable {
/**
* Receives a {@link URLConnection} instance typically as a result of a {@link URL}

View File

@ -18,8 +18,7 @@
*/
package org.jasig.cas.client.ssl;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URLConnection;
import java.security.KeyStore;
@ -41,6 +40,8 @@ import org.slf4j.LoggerFactory;
*/
public final class HttpsURLConnectionFactory implements HttpURLConnectionFactory {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = LoggerFactory.getLogger(HttpsURLConnectionFactory.class);
/**
@ -146,4 +147,45 @@ public final class HttpsURLConnectionFactory implements HttpURLConnectionFactory
return null;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final HttpsURLConnectionFactory that = (HttpsURLConnectionFactory) o;
if (!hostnameVerifier.equals(that.hostnameVerifier)) return false;
if (!sslConfiguration.equals(that.sslConfiguration)) return false;
return true;
}
@Override
public int hashCode() {
int result = hostnameVerifier.hashCode();
result = 31 * result + sslConfiguration.hashCode();
return result;
}
private void writeObject(final ObjectOutputStream out) throws IOException {
if (this.hostnameVerifier == HttpsURLConnection.getDefaultHostnameVerifier()) {
out.writeObject(null);
} else {
out.writeObject(this.hostnameVerifier);
}
out.writeObject(this.sslConfiguration);
}
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
final Object internalHostNameVerifier = in.readObject();
if (internalHostNameVerifier == null) {
this.hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
} else {
this.hostnameVerifier = (HostnameVerifier) internalHostNameVerifier;
}
this.sslConfiguration = (Properties) in.readObject();
}
}

View File

@ -19,6 +19,7 @@
package org.jasig.cas.client.ssl;
import java.io.Serializable;
import java.util.regex.Pattern;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
@ -32,7 +33,9 @@ import javax.net.ssl.SSLSession;
* @since 3.1.10
*
*/
public final class RegexHostnameVerifier implements HostnameVerifier {
public final class RegexHostnameVerifier implements HostnameVerifier, Serializable {
private static final long serialVersionUID = 1L;
/** Allowed hostname pattern */
private Pattern pattern;

View File

@ -20,6 +20,7 @@ package org.jasig.cas.client.ssl;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.io.Serializable;
/**
* Verifies a SSL peer host name based on an explicit whitelist of allowed hosts.
@ -29,7 +30,9 @@ import javax.net.ssl.SSLSession;
* @since 3.1.10
*
*/
public final class WhitelistHostnameVerifier implements HostnameVerifier {
public final class WhitelistHostnameVerifier implements HostnameVerifier, Serializable {
private static final long serialVersionUID = 1L;
/** Allowed hosts */
private String[] allowedHosts;

View File

@ -18,6 +18,9 @@
*/
package org.jasig.cas.client.util;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -42,12 +45,8 @@ public abstract class AbstractCasFilter extends AbstractConfigurationFilter {
/** Represents the constant for where the assertion will be located in memory. */
public static final String CONST_CAS_ASSERTION = "_const_cas_assertion_";
/** Defines the parameter to look for for the artifact. */
private String artifactParameterName = "ticket";
private Protocol protocol;
/** Defines the parameter to look for for the service. */
private String serviceParameterName = "service";
/** Sets where response.encodeUrl should be called on service urls when constructed. */
private boolean encodeServiceUrl = true;
@ -59,18 +58,16 @@ public abstract class AbstractCasFilter extends AbstractConfigurationFilter {
/** The exact url of the service. */
private String service;
protected AbstractCasFilter(final Protocol protocol) {
this.protocol = protocol;
}
public final void init(final FilterConfig filterConfig) throws ServletException {
super.init(filterConfig);
if (!isIgnoreInitConfiguration()) {
setServerName(getPropertyFromInitParams(filterConfig, "serverName", null));
logger.trace("Loading serverName property: {}", this.serverName);
setService(getPropertyFromInitParams(filterConfig, "service", null));
logger.trace("Loading service property: {}", this.service);
setArtifactParameterName(getPropertyFromInitParams(filterConfig, "artifactParameterName", "ticket"));
logger.trace("Loading artifact parameter name property: {}", this.artifactParameterName);
setServiceParameterName(getPropertyFromInitParams(filterConfig, "serviceParameterName", "service"));
logger.trace("Loading serviceParameterName property: {} ", this.serviceParameterName);
setEncodeServiceUrl(parseBoolean(getPropertyFromInitParams(filterConfig, "encodeServiceUrl", "true")));
logger.trace("Loading encodeServiceUrl property: {}", this.encodeServiceUrl);
setServerName(getString(ConfigurationKeys.SERVER_NAME));
setService(getString(ConfigurationKeys.SERVICE));
setEncodeServiceUrl(getBoolean(ConfigurationKeys.ENCODE_SERVICE_URL));
initInternal(filterConfig);
}
@ -92,8 +89,6 @@ public abstract class AbstractCasFilter extends AbstractConfigurationFilter {
* afterPropertiesSet();
*/
public void init() {
CommonUtils.assertNotNull(this.artifactParameterName, "artifactParameterName cannot be null.");
CommonUtils.assertNotNull(this.serviceParameterName, "serviceParameterName cannot be null.");
CommonUtils.assertTrue(CommonUtils.isNotEmpty(this.serverName) || CommonUtils.isNotEmpty(this.service),
"serverName or service must be set.");
CommonUtils.assertTrue(CommonUtils.isBlank(this.serverName) || CommonUtils.isBlank(this.service),
@ -107,7 +102,7 @@ public abstract class AbstractCasFilter extends AbstractConfigurationFilter {
protected final String constructServiceUrl(final HttpServletRequest request, final HttpServletResponse response) {
return CommonUtils.constructServiceUrl(request, response, this.service, this.serverName,
this.artifactParameterName, this.encodeServiceUrl);
this.protocol.getArtifactParameterName(), this.encodeServiceUrl);
}
/**
@ -129,26 +124,14 @@ public abstract class AbstractCasFilter extends AbstractConfigurationFilter {
this.service = service;
}
public final void setArtifactParameterName(final String artifactParameterName) {
this.artifactParameterName = artifactParameterName;
}
public final void setServiceParameterName(final String serviceParameterName) {
this.serviceParameterName = serviceParameterName;
}
public final void setEncodeServiceUrl(final boolean encodeServiceUrl) {
this.encodeServiceUrl = encodeServiceUrl;
}
public final String getArtifactParameterName() {
return this.artifactParameterName;
protected Protocol getProtocol() {
return this.protocol;
}
public final String getServiceParameterName() {
return this.serviceParameterName;
}
/**
* Template method to allow you to change how you retrieve the ticket.
*
@ -156,6 +139,6 @@ public abstract class AbstractCasFilter extends AbstractConfigurationFilter {
* @return the ticket if its found, null otherwise.
*/
protected String retrieveTicketFromRequest(final HttpServletRequest request) {
return CommonUtils.safeGetParameter(request, getArtifactParameterName());
return CommonUtils.safeGetParameter(request, this.protocol.getArtifactParameterName());
}
}

View File

@ -18,10 +18,13 @@
*/
package org.jasig.cas.client.util;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import org.jasig.cas.client.configuration.ConfigurationKey;
import org.jasig.cas.client.configuration.ConfigurationStrategy;
import org.jasig.cas.client.configuration.ConfigurationStrategyName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -34,94 +37,41 @@ import org.slf4j.LoggerFactory;
*/
public abstract class AbstractConfigurationFilter implements Filter {
private static final String CONFIGURATION_STRATEGY_KEY = "configurationStrategy";
protected final Logger logger = LoggerFactory.getLogger(getClass());
private boolean ignoreInitConfiguration = false;
/**
* Retrieves the property from the FilterConfig. First it checks the FilterConfig's initParameters to see if it
* has a value.
* If it does, it returns that, otherwise it retrieves the ServletContext's initParameters and returns that value if any.
* <p>
* Finally, it will check JNDI if all other methods fail. All the JNDI properties should be stored under either java:comp/env/cas/SHORTFILTERNAME/{propertyName}
* or java:comp/env/cas/{propertyName}
* <p>
* Essentially the documented order is:
* <ol>
* <li>FilterConfig.getInitParameter</li>
* <li>ServletContext.getInitParameter</li>
* <li>java:comp/env/cas/SHORTFILTERNAME/{propertyName}</li>
* <li>java:comp/env/cas/{propertyName}</li>
* <li>Default Value</li>
* </ol>
*
*
* @param filterConfig the Filter Configuration.
* @param propertyName the property to retrieve.
* @param defaultValue the default value if the property is not found.
* @return the property value, following the above conventions. It will always return the more specific value (i.e.
* filter vs. context).
*/
protected final String getPropertyFromInitParams(final FilterConfig filterConfig, final String propertyName,
final String defaultValue) {
final String value = filterConfig.getInitParameter(propertyName);
private ConfigurationStrategy configurationStrategy;
if (CommonUtils.isNotBlank(value)) {
if ("renew".equals(propertyName)) {
throw new IllegalArgumentException(
"Renew MUST be specified via context parameter or JNDI environment to avoid misconfiguration.");
}
logger.info("Property [{}] loaded from FilterConfig.getInitParameter with value [{}]", propertyName, value);
return value;
}
final String value2 = filterConfig.getServletContext().getInitParameter(propertyName);
if (CommonUtils.isNotBlank(value2)) {
logger.info("Property [{}] loaded from ServletContext.getInitParameter with value [{}]", propertyName,
value2);
return value2;
}
InitialContext context;
try {
context = new InitialContext();
} catch (final NamingException e) {
logger.warn(e.getMessage(), e);
return defaultValue;
}
final String shortName = this.getClass().getName().substring(this.getClass().getName().lastIndexOf(".") + 1);
final String value3 = loadFromContext(context, "java:comp/env/cas/" + shortName + "/" + propertyName);
if (CommonUtils.isNotBlank(value3)) {
logger.info("Property [{}] loaded from JNDI Filter Specific Property with value [{}]", propertyName, value3);
return value3;
}
final String value4 = loadFromContext(context, "java:comp/env/cas/" + propertyName);
if (CommonUtils.isNotBlank(value4)) {
logger.info("Property [{}] loaded from JNDI with value [{}]", propertyName, value4);
return value4;
}
logger.info("Property [{}] not found. Using default value [{}]", propertyName, defaultValue);
return defaultValue;
public void init(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());
}
protected final boolean parseBoolean(final String value) {
return ((value != null) && value.equalsIgnoreCase("true"));
protected final boolean getBoolean(final ConfigurationKey<Boolean> configurationKey) {
return this.configurationStrategy.getBoolean(configurationKey);
}
protected final String loadFromContext(final InitialContext context, final String path) {
try {
return (String) context.lookup(path);
} catch (final NamingException e) {
return null;
}
protected final String getString(final ConfigurationKey<String> configurationKey) {
return this.configurationStrategy.getString(configurationKey);
}
public final void setIgnoreInitConfiguration(boolean ignoreInitConfiguration) {
protected final long getLong(final ConfigurationKey<Long> configurationKey) {
return this.configurationStrategy.getLong(configurationKey);
}
protected final int getInt(final ConfigurationKey<Integer> configurationKey) {
return this.configurationStrategy.getInt(configurationKey);
}
protected final <T> Class<? extends T> getClass(final ConfigurationKey<Class<? extends T>> configurationKey) {
return this.configurationStrategy.getClass(configurationKey);
}
public final void setIgnoreInitConfiguration(final boolean ignoreInitConfiguration) {
this.ignoreInitConfiguration = ignoreInitConfiguration;
}

View File

@ -22,13 +22,12 @@ import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
import org.jasig.cas.client.validation.ProxyList;
import org.jasig.cas.client.validation.ProxyListEditor;
import org.slf4j.Logger;
@ -55,16 +54,12 @@ public final class CommonUtils {
*/
private static final String PARAM_PROXY_GRANTING_TICKET = "pgtId";
private static final HttpURLConnectionFactory DEFAULT_URL_CONNECTION_FACTORY = new HttpsURLConnectionFactory();
private CommonUtils() {
// nothing to do
}
public static String formatForUtcTime(final Date date) {
final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return dateFormat.format(date);
}
/**
* Check whether the object is null or not. If it is, throw an exception and
* display the message.
@ -96,7 +91,7 @@ public final class CommonUtils {
* Assert that the statement is true, otherwise throw an exception with the
* provided message.
*
* @param cond the codition to assert is true.
* @param cond the condition to assert is true.
* @param message the message to display if the condition is not true.
*/
public static void assertTrue(final boolean cond, final String message) {
@ -105,6 +100,20 @@ public final class CommonUtils {
}
}
/**
* Assert that the statement is true, otherwise throw an exception with the
* provided message.
*
* @param cond the condition to assert is false.
* @param message the message to display if the condition is not false.
*/
public static void assertFalse(final boolean cond, final String message) {
if (cond) {
throw new IllegalArgumentException(message);
}
}
/**
* Determines whether the String is null or of length 0.
*
@ -348,6 +357,28 @@ public final class CommonUtils {
return safeGetParameter(request, parameter, Arrays.asList("logoutRequest"));
}
/**
* Contacts the remote URL and returns the response.
*
* @param constructedUrl the url to contact.
* @param encoding the encoding to use.
* @return the response.
*/
@Deprecated
public static String getResponseFromServer(final String constructedUrl, final String encoding) {
try {
return getResponseFromServer(new URL(constructedUrl), DEFAULT_URL_CONNECTION_FACTORY, encoding);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
@Deprecated
public static String getResponseFromServer(final URL constructedUrl, final String encoding) {
return getResponseFromServer(constructedUrl, DEFAULT_URL_CONNECTION_FACTORY, encoding);
}
/**
* Contacts the remote URL and returns the response.
*

View File

@ -24,22 +24,22 @@ import java.util.Enumeration;
import java.util.List;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Filters that redirects to the supplied url based on an exception. Exceptions and the urls are configured via
* init filter name/param values.
* <p>
* <p/>
* If there is an exact match the filter uses that value. If there's a non-exact match (i.e. inheritance), then the filter
* uses the last value that matched.
* <p>
* <p/>
* If there is no match it will redirect to a default error page. The default exception is configured via the "defaultErrorRedirectPage" property.
*
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1.4
*
*/
public final class ErrorRedirectFilter implements Filter {
@ -58,8 +58,8 @@ public final class ErrorRedirectFilter implements Filter {
final HttpServletResponse httpResponse = (HttpServletResponse) response;
try {
filterChain.doFilter(request, response);
} catch (final ServletException e) {
final Throwable t = e.getCause();
} catch (final Exception e) {
final Throwable t = extractErrorToCompare(e);
ErrorHolder currentMatch = null;
for (final ErrorHolder errorHolder : this.errors) {
if (errorHolder.exactMatch(t)) {
@ -78,6 +78,22 @@ public final class ErrorRedirectFilter implements Filter {
}
}
/**
* Determine which error to use for comparison. If there is an {@link Throwable#getCause()} then that will be used. Otherwise, the original throwable is used.
*
* @param throwable the throwable to look for a root cause.
* @return the throwable to use for comparison. MUST NOT BE NULL.
*/
private Throwable extractErrorToCompare(final Throwable throwable) {
final Throwable cause = throwable.getCause();
if (cause != null) {
return cause;
}
return throwable;
}
public void init(final FilterConfig filterConfig) throws ServletException {
this.defaultErrorRedirectPage = filterConfig.getInitParameter("defaultErrorRedirectPage");

View File

@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpSession;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.jasig.cas.client.validation.Assertion;
/**
@ -82,8 +83,9 @@ public final class HttpServletRequestWrapperFilter extends AbstractConfiguration
}
public void init(final FilterConfig filterConfig) throws ServletException {
this.roleAttribute = getPropertyFromInitParams(filterConfig, "roleAttribute", null);
this.ignoreCase = Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "ignoreCase", "false"));
super.init(filterConfig);
this.roleAttribute = getString(ConfigurationKeys.ROLE_ATTRIBUTE);
this.ignoreCase = getBoolean(ConfigurationKeys.IGNORE_CASE);
}
final class CasHttpServletRequestWrapper extends HttpServletRequestWrapper {

View File

@ -0,0 +1,74 @@
package org.jasig.cas.client.util;
import java.io.*;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
/**
* IO utility class.
*
* @author Marvin S. Addison
* @since 3.4
*/
public final class IOUtils {
/** UTF-8 character set. */
public static final Charset UTF8 = Charset.forName("UTF-8");
private IOUtils() { /** Utility class pattern. */ }
/**
* Reads all data from the given stream as UTF-8 character data and closes it on completion or errors.
*
* @param in Input stream containing character data.
*
* @return String of all data in stream.
*
* @throws IOException On IO errors.
*/
public static String readString(final InputStream in) throws IOException {
return readString(in, UTF8);
}
/**
* Reads all data from the given stream as character data in the given character set and closes it on completion
* or errors.
*
* @param in Input stream containing character data.
* @param charset Character set of data in stream.
*
* @return String of all data in stream.
*
* @throws IOException On IO errors.
*/
public static String readString(final InputStream in, final Charset charset) throws IOException {
final Reader reader = new InputStreamReader(in, charset);
final StringBuilder builder = new StringBuilder();
final CharBuffer buffer = CharBuffer.allocate(2048);
try {
while (reader.read(buffer) > -1) {
buffer.flip();
builder.append(buffer);
}
} finally {
closeQuietly(reader);
}
return builder.toString();
}
/**
* Unconditionally close a {@link Closeable} resource. Errors on close are ignored.
*
* @param resource Resource to close.
*/
public static void closeQuietly(final Closeable resource) {
try {
if (resource != null) {
resource.close();
}
} catch (final IOException e) {
//ignore
}
}
}

View File

@ -0,0 +1,62 @@
package org.jasig.cas.client.util;
import javax.xml.namespace.NamespaceContext;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Namespace context implementation backed by a map of XML prefixes to namespace URIs.
*
* @author Marvin S. Addison
* @since 3.4
*/
public class MapNamespaceContext implements NamespaceContext {
private final Map<String, String> namespaceMap;
/**
* Creates a new instance from an array of namespace delcarations.
*
* @param namespaceDeclarations An array of namespace declarations of the form prefix->uri.
*/
public MapNamespaceContext(final String ... namespaceDeclarations) {
namespaceMap = new HashMap<String, String>();
int index;
String key;
String value;
for (final String decl : namespaceDeclarations) {
index = decl.indexOf('-');
key = decl.substring(0, index);
value = decl.substring(index + 2);
namespaceMap.put(key, value);
}
}
/**
* Creates a new instance from a map.
*
* @param namespaceMap Map of XML namespace prefixes (keys) to URIs (values).
*/
public MapNamespaceContext(final Map<String, String> namespaceMap) {
this.namespaceMap = namespaceMap;
}
public String getNamespaceURI(final String prefix) {
return namespaceMap.get(prefix);
}
public String getPrefix(final String namespaceURI) {
for (final Map.Entry<String, String> entry : namespaceMap.entrySet()) {
if (entry.getValue().equalsIgnoreCase(namespaceURI)) {
return entry.getKey();
}
}
return null;
}
public Iterator getPrefixes(final String namespaceURI) {
return Collections.singleton(getPrefix(namespaceURI)).iterator();
}
}

View File

@ -0,0 +1,87 @@
package org.jasig.cas.client.util;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.xpath.*;
/**
* Thread local XPath expression.
*
* @author Marvin S. Addison
* @since 3.4
*/
public class ThreadLocalXPathExpression extends ThreadLocal<XPathExpression> implements XPathExpression {
/** XPath expression */
private final String expression;
/** Namespace context. */
private final NamespaceContext context;
/**
* Creates a new instance from an XPath expression and namespace context.
*
* @param xPath XPath expression.
* @param context Namespace context for handling namespace prefix to URI mappings.
*/
public ThreadLocalXPathExpression(final String xPath, final NamespaceContext context) {
this.expression = xPath;
this.context = context;
}
public Object evaluate(final Object o, final QName qName) throws XPathExpressionException {
return get().evaluate(o, qName);
}
public String evaluate(final Object o) throws XPathExpressionException {
return get().evaluate(o);
}
public Object evaluate(final InputSource inputSource, final QName qName) throws XPathExpressionException {
return get().evaluate(inputSource, qName);
}
public String evaluate(final InputSource inputSource) throws XPathExpressionException {
return get().evaluate(inputSource);
}
/**
* Evaluates the XPath expression and returns the result coerced to a string.
*
* @param o Object on which to evaluate the expression; typically a DOM node.
*
* @return Evaluation result as a string.
*
* @throws XPathExpressionException On XPath evaluation errors.
*/
public String evaluateAsString(final Object o) throws XPathExpressionException {
return (String) evaluate(o, XPathConstants.STRING);
}
/**
* Evaluates the XPath expression and returns the result coerced to a node list.
*
* @param o Object on which to evaluate the expression; typically a DOM node.
*
* @return Evaluation result as a node list.
*
* @throws XPathExpressionException On XPath evaluation errors.
*/
public NodeList evaluateAsNodeList(final Object o) throws XPathExpressionException {
return (NodeList) evaluate(o, XPathConstants.NODESET);
}
@Override
protected XPathExpression initialValue() {
try {
final XPath xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(context);
return xPath.compile(expression);
} catch (XPathExpressionException e) {
throw new IllegalArgumentException("Invalid XPath expression");
}
}
}

View File

@ -19,16 +19,20 @@
package org.jasig.cas.client.util;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
/**
@ -45,6 +49,34 @@ public final class XmlUtils {
*/
private final static Logger LOGGER = LoggerFactory.getLogger(XmlUtils.class);
/**
* Creates a new namespace-aware DOM document object by parsing the given XML.
*
* @param xml XML content.
*
* @return DOM document.
*/
public static Document newDocument(final String xml) {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final Map<String, Boolean> features = new HashMap<String, Boolean>();
features.put(XMLConstants.FEATURE_SECURE_PROCESSING, true);
features.put("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
for (final Map.Entry<String, Boolean> entry : features.entrySet()) {
try {
factory.setFeature(entry.getKey(), entry.getValue());
} catch (ParserConfigurationException e) {
LOGGER.warn("Failed setting XML feature {}: {}", entry.getKey(), e);
}
}
factory.setNamespaceAware(true);
try {
return factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
} catch (Exception e) {
throw new RuntimeException("XML parsing error: " + e);
}
}
/**
* Get an instance of an XML reader from the XMLReaderFactory.
*
@ -62,6 +94,7 @@ public final class XmlUtils {
}
}
/**
* Retrieve the text for a group of elements. Each text element is an entry
* in a list.

View File

@ -34,10 +34,6 @@ public abstract class AbstractCasProtocolUrlBasedTicketValidator extends Abstrac
super(casServerUrlPrefix);
}
protected final void setDisableXmlSchemaValidation(final boolean disable) {
// nothing to do
}
/**
* Retrieves the response from the server by opening a connection and merely reading the response.
*/

View File

@ -25,6 +25,9 @@ import javax.net.ssl.HostnameVerifier;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.ReflectUtils;
@ -68,6 +71,10 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
*/
private boolean useSession = true;
protected AbstractTicketValidationFilter(final Protocol protocol) {
super(protocol);
}
/**
* Template method to return the appropriate validator.
*
@ -81,12 +88,11 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
/**
* Gets the ssl config to use for HTTPS connections
* if one is configured for this filter.
* @param filterConfig Servlet filter configuration.
* @return Properties that can contains key/trust info for Client Side Certificates
*/
protected Properties getSSLConfig(final FilterConfig filterConfig) {
protected Properties getSSLConfig() {
final Properties properties = new Properties();
final String fileName = getPropertyFromInitParams(filterConfig, "sslConfigFile", null);
final String fileName = getString(ConfigurationKeys.SSL_CONFIG_FILE);
if (fileName != null) {
FileInputStream fis = null;
@ -106,14 +112,11 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
/**
* Gets the configured {@link HostnameVerifier} to use for HTTPS connections
* if one is configured for this filter.
* @param filterConfig Servlet filter configuration.
* @return Instance of specified host name verifier or null if none specified.
*/
protected HostnameVerifier getHostnameVerifier(final FilterConfig filterConfig) {
final String className = getPropertyFromInitParams(filterConfig, "hostnameVerifier", null);
logger.trace("Using hostnameVerifier parameter: {}", className);
final String config = getPropertyFromInitParams(filterConfig, "hostnameVerifierConfig", null);
logger.trace("Using hostnameVerifierConfig parameter: {}", config);
protected HostnameVerifier getHostnameVerifier() {
final Class<? extends HostnameVerifier> className = getClass(ConfigurationKeys.HOSTNAME_VERIFIER);
final String config = getString(ConfigurationKeys.HOSTNAME_VERIFIER_CONFIG);
if (className != null) {
if (config != null) {
return ReflectUtils.newInstance(className, config);
@ -125,14 +128,9 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
}
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
setExceptionOnValidationFailure(parseBoolean(getPropertyFromInitParams(filterConfig,
"exceptionOnValidationFailure", "false")));
logger.trace("Setting exceptionOnValidationFailure parameter: {}", this.exceptionOnValidationFailure);
setRedirectAfterValidation(parseBoolean(getPropertyFromInitParams(filterConfig, "redirectAfterValidation",
"true")));
logger.trace("Setting redirectAfterValidation parameter: {}", this.redirectAfterValidation);
setUseSession(parseBoolean(getPropertyFromInitParams(filterConfig, "useSession", "true")));
logger.trace("Setting useSession parameter: {}", this.useSession);
setExceptionOnValidationFailure(getBoolean(ConfigurationKeys.EXCEPTION_ON_VALIDATION_FAILURE));
setRedirectAfterValidation(getBoolean(ConfigurationKeys.REDIRECT_AFTER_VALIDATION));
setUseSession(getBoolean(ConfigurationKeys.USE_SESSION));
if (!this.useSession && this.redirectAfterValidation) {
logger.warn("redirectAfterValidation parameter may not be true when useSession parameter is false. Resetting it to false in order to prevent infinite redirects.");

View File

@ -90,13 +90,6 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
*/
protected abstract String getUrlSuffix();
/**
* Disable XML Schema validation. Note, setting this to true may not be reversable. Defaults to false. Setting it to false
* after setting it to true may not have any affect.
*
* @param disabled whether to disable or not.
*/
protected abstract void setDisableXmlSchemaValidation(boolean disabled);
/**
* Constructs the URL to send the validation request to.
@ -110,7 +103,7 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
logger.debug("Placing URL parameters in map.");
urlParameters.put("ticket", ticket);
urlParameters.put("service", encodeUrl(serviceUrl));
urlParameters.put("service", serviceUrl);
if (this.renew) {
urlParameters.put("renew", "true");
@ -144,7 +137,8 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
buffer.append(i++ == 0 ? "?" : "&");
buffer.append(key);
buffer.append("=");
buffer.append(value);
final String encodedValue = encodeUrl(value);
buffer.append(encodedValue);
}
}

View File

@ -19,29 +19,36 @@
package org.jasig.cas.client.validation;
import javax.servlet.FilterConfig;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
/**
* Implementation of AbstractTicketValidatorFilter that instanciates a Cas10TicketValidator.
* Implementation of AbstractTicketValidatorFilter that creates a Cas10TicketValidator.
* <p>Deployers can provide the "casServerPrefix" and the "renew" attributes via the standard context or filter init
* parameters.
*
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public class Cas10TicketValidationFilter extends AbstractTicketValidationFilter {
protected final TicketValidator getTicketValidator(final FilterConfig filterConfig) {
final String casServerUrlPrefix = getPropertyFromInitParams(filterConfig, "casServerUrlPrefix", null);
final Cas10TicketValidator validator = new Cas10TicketValidator(casServerUrlPrefix);
validator.setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
public Cas10TicketValidationFilter() {
super(Protocol.CAS1);
}
final HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(filterConfig),
getSSLConfig(filterConfig));
protected final TicketValidator getTicketValidator(final FilterConfig filterConfig) {
final String casServerUrlPrefix = getString(ConfigurationKeys.CAS_SERVER_URL_PREFIX);
final Cas10TicketValidator validator = new Cas10TicketValidator(casServerUrlPrefix);
validator.setRenew(getBoolean(ConfigurationKeys.RENEW));
final HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(),
getSSLConfig());
validator.setURLConnectionFactory(factory);
validator.setEncoding(getPropertyFromInitParams(filterConfig, "encoding", null));
validator.setEncoding(getString(ConfigurationKeys.ENCODING));
return validator;
}

View File

@ -23,16 +23,21 @@ import java.util.*;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.jasig.cas.client.proxy.*;
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.ReflectUtils;
import static org.jasig.cas.client.configuration.ConfigurationKeys.*;
/**
* Creates either a CAS20ProxyTicketValidator or a CAS20ServiceTicketValidator depending on whether any of the
* proxy parameters are set.
* <p>
* <p/>
* This filter can also pass additional parameters to the ticket validator. Any init parameter not included in the
* reserved list {@link org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter#RESERVED_INIT_PARAMS}.
*
@ -40,20 +45,20 @@ import org.jasig.cas.client.util.ReflectUtils;
* @author Brad Cupit (brad [at] lsu {dot} edu)
* @version $Revision$ $Date$
* @since 3.1
*
*/
public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketValidationFilter {
private static final String[] RESERVED_INIT_PARAMS = new String[] { "proxyGrantingTicketStorageClass",
"proxyReceptorUrl", "acceptAnyProxy", "allowedProxyChains", "casServerUrlPrefix", "proxyCallbackUrl",
"renew", "exceptionOnValidationFailure", "redirectAfterValidation", "useSession", "serverName", "service",
"artifactParameterName", "serviceParameterName", "encodeServiceUrl", "millisBetweenCleanUps",
"hostnameVerifier", "encoding", "config", "ticketValidatorClass" };
private static final int DEFAULT_MILLIS_BETWEEN_CLEANUPS = 60 * 1000;
private static final String[] RESERVED_INIT_PARAMS = new String[]{ARTIFACT_PARAMETER_NAME.getName(), SERVER_NAME.getName(), SERVICE.getName(), RENEW.getName(), LOGOUT_PARAMETER_NAME.getName(),
ARTIFACT_PARAMETER_OVER_POST.getName(), EAGERLY_CREATE_SESSIONS.getName(), ENCODE_SERVICE_URL.getName(), SSL_CONFIG_FILE.getName(), ROLE_ATTRIBUTE.getName(), IGNORE_CASE.getName(),
CAS_SERVER_LOGIN_URL.getName(), GATEWAY.getName(), AUTHENTICATION_REDIRECT_STRATEGY_CLASS.getName(), GATEWAY_STORAGE_CLASS.getName(), CAS_SERVER_URL_PREFIX.getName(), ENCODING.getName(),
TOLERANCE.getName(), IGNORE_PATTERN.getName(), IGNORE_URL_PATTERN_TYPE.getName(), HOSTNAME_VERIFIER.getName(), HOSTNAME_VERIFIER_CONFIG.getName(),
EXCEPTION_ON_VALIDATION_FAILURE.getName(), REDIRECT_AFTER_VALIDATION.getName(), USE_SESSION.getName(), SECRET_KEY.getName(), CIPHER_ALGORITHM.getName(), PROXY_RECEPTOR_URL.getName(),
PROXY_GRANTING_TICKET_STORAGE_CLASS.getName(), MILLIS_BETWEEN_CLEAN_UPS.getName(), ACCEPT_ANY_PROXY.getName(), ALLOWED_PROXY_CHAINS.getName(), TICKET_VALIDATOR_CLASS.getName(),
PROXY_CALLBACK_URL.getName(), FRONT_LOGOUT_PARAMETER_NAME.getName(), RELAY_STATE_PARAMETER_NAME.getName()
};
/**
* The URL to send to the CAS server as the URL that will process proxying requests on the CAS client.
* The URL to send to the CAS server as the URL that will process proxying requests on the CAS client.
*/
private String proxyReceptorUrl;
@ -63,25 +68,37 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
private int millisBetweenCleanUps;
protected Class<? extends Cas20ServiceTicketValidator> defaultServiceTicketValidatorClass;
protected Class<? extends Cas20ProxyTicketValidator> defaultProxyTicketValidatorClass;
/**
* Storage location of ProxyGrantingTickets and Proxy Ticket IOUs.
*/
private ProxyGrantingTicketStorage proxyGrantingTicketStorage = new ProxyGrantingTicketStorageImpl();
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
setProxyReceptorUrl(getPropertyFromInitParams(filterConfig, "proxyReceptorUrl", null));
public Cas20ProxyReceivingTicketValidationFilter() {
this(Protocol.CAS2);
this.defaultServiceTicketValidatorClass = Cas20ServiceTicketValidator.class;
this.defaultProxyTicketValidatorClass = Cas20ProxyTicketValidator.class;
}
final String proxyGrantingTicketStorageClass = getPropertyFromInitParams(filterConfig,
"proxyGrantingTicketStorageClass", null);
protected Cas20ProxyReceivingTicketValidationFilter(final Protocol protocol) {
super(protocol);
}
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
setProxyReceptorUrl(getString(ConfigurationKeys.PROXY_RECEPTOR_URL));
final Class<? extends ProxyGrantingTicketStorage> proxyGrantingTicketStorageClass = getClass(ConfigurationKeys.PROXY_GRANTING_TICKET_STORAGE_CLASS);
if (proxyGrantingTicketStorageClass != null) {
this.proxyGrantingTicketStorage = ReflectUtils.newInstance(proxyGrantingTicketStorageClass);
if (this.proxyGrantingTicketStorage instanceof AbstractEncryptedProxyGrantingTicketStorageImpl) {
final AbstractEncryptedProxyGrantingTicketStorageImpl p = (AbstractEncryptedProxyGrantingTicketStorageImpl) this.proxyGrantingTicketStorage;
final String cipherAlgorithm = getPropertyFromInitParams(filterConfig, "cipherAlgorithm",
AbstractEncryptedProxyGrantingTicketStorageImpl.DEFAULT_ENCRYPTION_ALGORITHM);
final String secretKey = getPropertyFromInitParams(filterConfig, "secretKey", null);
final String cipherAlgorithm = getString(ConfigurationKeys.CIPHER_ALGORITHM);
final String secretKey = getString(ConfigurationKeys.SECRET_KEY);
p.setCipherAlgorithm(cipherAlgorithm);
@ -95,9 +112,7 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
}
}
logger.trace("Setting proxyReceptorUrl parameter: {}", this.proxyReceptorUrl);
this.millisBetweenCleanUps = Integer.parseInt(getPropertyFromInitParams(filterConfig, "millisBetweenCleanUps",
Integer.toString(DEFAULT_MILLIS_BETWEEN_CLEANUPS)));
this.millisBetweenCleanUps = getInt(ConfigurationKeys.MILLIS_BETWEEN_CLEAN_UPS);
super.initInternal(filterConfig);
}
@ -115,13 +130,13 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
this.timer.schedule(this.timerTask, this.millisBetweenCleanUps, this.millisBetweenCleanUps);
}
private <T> T createNewTicketValidator(final String ticketValidatorClass, final String casServerUrlPrefix,
final Class<T> clazz) {
if (CommonUtils.isBlank(ticketValidatorClass)) {
private <T> T createNewTicketValidator(final Class<? extends Cas20ServiceTicketValidator> ticketValidatorClass, final String casServerUrlPrefix,
final Class<T> clazz) {
if (ticketValidatorClass == null) {
return ReflectUtils.newInstance(clazz, casServerUrlPrefix);
}
return ReflectUtils.newInstance(ticketValidatorClass, casServerUrlPrefix);
return (T) ReflectUtils.newInstance(ticketValidatorClass, casServerUrlPrefix);
}
/**
@ -131,38 +146,37 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
* @return a fully constructed TicketValidator.
*/
protected final TicketValidator getTicketValidator(final FilterConfig filterConfig) {
final String allowAnyProxy = getPropertyFromInitParams(filterConfig, "acceptAnyProxy", null);
final String allowedProxyChains = getPropertyFromInitParams(filterConfig, "allowedProxyChains", null);
final String casServerUrlPrefix = getPropertyFromInitParams(filterConfig, "casServerUrlPrefix", null);
final String ticketValidatorClass = getPropertyFromInitParams(filterConfig, "ticketValidatorClass", null);
final boolean allowAnyProxy = getBoolean(ConfigurationKeys.ACCEPT_ANY_PROXY);
final String allowedProxyChains = getString(ConfigurationKeys.ALLOWED_PROXY_CHAINS);
final String casServerUrlPrefix = getString(ConfigurationKeys.CAS_SERVER_URL_PREFIX);
final Class<? extends Cas20ServiceTicketValidator> ticketValidatorClass = getClass(ConfigurationKeys.TICKET_VALIDATOR_CLASS);
final Cas20ServiceTicketValidator validator;
if (CommonUtils.isNotBlank(allowAnyProxy) || CommonUtils.isNotBlank(allowedProxyChains)) {
if (allowAnyProxy || CommonUtils.isNotBlank(allowedProxyChains)) {
final Cas20ProxyTicketValidator v = createNewTicketValidator(ticketValidatorClass, casServerUrlPrefix,
Cas20ProxyTicketValidator.class);
v.setAcceptAnyProxy(parseBoolean(allowAnyProxy));
this.defaultProxyTicketValidatorClass);
v.setAcceptAnyProxy(allowAnyProxy);
v.setAllowedProxyChains(CommonUtils.createProxyList(allowedProxyChains));
validator = v;
} else {
validator = createNewTicketValidator(ticketValidatorClass, casServerUrlPrefix,
Cas20ServiceTicketValidator.class);
this.defaultServiceTicketValidatorClass);
}
validator.setProxyCallbackUrl(getPropertyFromInitParams(filterConfig, "proxyCallbackUrl", null));
validator.setProxyCallbackUrl(getString(ConfigurationKeys.PROXY_CALLBACK_URL));
validator.setProxyGrantingTicketStorage(this.proxyGrantingTicketStorage);
final HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(filterConfig),
getSSLConfig(filterConfig));
final HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(),
getSSLConfig());
validator.setURLConnectionFactory(factory);
validator.setProxyRetriever(new Cas20ProxyRetriever(casServerUrlPrefix, getPropertyFromInitParams(filterConfig,
"encoding", null), factory));
validator.setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
validator.setEncoding(getPropertyFromInitParams(filterConfig, "encoding", null));
validator.setProxyRetriever(new Cas20ProxyRetriever(casServerUrlPrefix, getString(ConfigurationKeys.ENCODING), factory));
validator.setRenew(getBoolean(ConfigurationKeys.RENEW));
validator.setEncoding(getString(ConfigurationKeys.ENCODING));
final Map<String, String> additionalParameters = new HashMap<String, String>();
final List<String> params = Arrays.asList(RESERVED_INIT_PARAMS);
for (final Enumeration<?> e = filterConfig.getInitParameterNames(); e.hasMoreElements();) {
for (final Enumeration<?> e = filterConfig.getInitParameterNames(); e.hasMoreElements(); ) {
final String s = (String) e.nextElement();
if (!params.contains(s)) {
@ -183,7 +197,7 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
* This processes the ProxyReceptor request before the ticket validation code executes.
*/
protected final boolean preFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
final FilterChain filterChain) throws IOException, ServletException {
final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final String requestUri = request.getRequestURI();

View File

@ -18,6 +18,7 @@
*/
package org.jasig.cas.client.validation;
import java.util.Arrays;
import java.util.List;
import org.jasig.cas.client.util.XmlUtils;
@ -53,8 +54,22 @@ public class Cas20ProxyTicketValidator extends Cas20ServiceTicketValidator {
throws TicketValidationException {
final List<String> proxies = XmlUtils.getTextForElements(response, "proxy");
if (proxies == null) {
throw new InvalidProxyChainTicketValidationException(
"Invalid proxy chain: No proxy could be retrieved from response. "
+ "This indicates a problem with CAS validation. Review logs/configuration to find the root cause."
);
}
// this means there was nothing in the proxy chain, which is okay
if ((this.allowEmptyProxyChain && proxies.isEmpty()) || this.acceptAnyProxy) {
if ((this.allowEmptyProxyChain && proxies.isEmpty())) {
logger.debug("Found an empty proxy chain, permitted by client configuration");
return;
}
if (this.acceptAnyProxy) {
logger.debug("Client configuration accepts any proxy. "
+ "It is generally dangerous to use a non-proxied CAS filter "
+ "specially for protecting resources that require proxy access.");
return;
}
@ -63,6 +78,10 @@ public class Cas20ProxyTicketValidator extends Cas20ServiceTicketValidator {
return;
}
logger.warn("Proxies received from the CAS validation response are {}. "
+ "However, none are allowed by allowed proxy chain of the client which is {}",
Arrays.toString(proxiedList), this.allowedProxyChains);
throw new InvalidProxyChainTicketValidationException("Invalid proxy chain: " + proxies.toString());
}

View File

@ -70,7 +70,7 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick
* @param urlParameters the Map containing the existing parameters to send to the server.
*/
protected final void populateUrlAttributeMap(final Map<String, String> urlParameters) {
urlParameters.put("pgtUrl", encodeUrl(this.proxyCallbackUrl));
urlParameters.put("pgtUrl", this.proxyCallbackUrl);
}
protected String getUrlSuffix() {

View File

@ -0,0 +1,40 @@
/*
* 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.validation;
import org.jasig.cas.client.Protocol;
/**
* Creates either a Cas30ProxyTicketValidator or a Cas30ServiceTicketValidator depending on whether any of the
* proxy parameters are set.
* <p/>
* This filter can also pass additional parameters to the ticket validator. Any init parameter not included in the
* reserved list {@link org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter#RESERVED_INIT_PARAMS}.
*
* @author Jerome Leleu
* @since 3.4.0
*/
public class Cas30ProxyReceivingTicketValidationFilter extends Cas20ProxyReceivingTicketValidationFilter {
public Cas30ProxyReceivingTicketValidationFilter() {
super(Protocol.CAS3);
this.defaultServiceTicketValidatorClass = Cas30ServiceTicketValidator.class;
this.defaultProxyTicketValidatorClass = Cas30ProxyTicketValidator.class;
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.validation;
/**
* Service and proxy tickets validation service for the CAS protocol v3.
*
* @author Jerome Leleu
* @since 3.4.0
*/
public class Cas30ProxyTicketValidator extends Cas20ProxyTicketValidator {
public Cas30ProxyTicketValidator(final String casServerUrlPrefix) {
super(casServerUrlPrefix);
}
@Override
protected String getUrlSuffix() {
return "p3/proxyValidate";
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.validation;
/**
* Service tickets validation service for the CAS protocol v3.
*
* @author Jerome Leleu
* @since 3.4.0
*/
public class Cas30ServiceTicketValidator extends Cas20ServiceTicketValidator {
public Cas30ServiceTicketValidator(String casServerUrlPrefix) {
super(casServerUrlPrefix);
}
@Override
protected String getUrlSuffix() {
return "p3/serviceValidate";
}
}

View File

@ -64,7 +64,7 @@ public final class ProxyList {
this(new ArrayList<String[]>());
}
public boolean contains(String[] proxiedList) {
public boolean contains(final String[] proxiedList) {
StringBuilder loggingOutput;
for (final List<UrlPatternMatcherStrategy> proxyChain : this.proxyChains) {

View File

@ -1,288 +0,0 @@
/*
* 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.validation;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.*;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.authentication.AttributePrincipalImpl;
import org.jasig.cas.client.util.CommonUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
import org.opensaml.Configuration;
import org.opensaml.DefaultBootstrap;
import org.opensaml.common.IdentifierGenerator;
import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
import org.opensaml.saml1.core.*;
import org.opensaml.ws.soap.soap11.Envelope;
import org.opensaml.xml.ConfigurationException;
import org.opensaml.xml.io.Unmarshaller;
import org.opensaml.xml.io.UnmarshallerFactory;
import org.opensaml.xml.io.UnmarshallingException;
import org.opensaml.xml.parse.BasicParserPool;
import org.opensaml.xml.parse.XMLParserException;
import org.opensaml.xml.schema.XSAny;
import org.opensaml.xml.schema.XSString;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* TicketValidator that can understand validating a SAML artifact. This includes the SOAP request/response.
*
* @author Scott Battaglia
* @since 3.1
*/
public final class Saml11TicketValidator extends AbstractUrlBasedTicketValidator {
static {
try {
// we really only need to do this once, so this is why its here.
DefaultBootstrap.bootstrap();
} catch (final ConfigurationException e) {
throw new RuntimeException(e);
}
}
/** Time tolerance to allow for time drifting. */
private long tolerance = 1000L;
private final BasicParserPool basicParserPool;
private final IdentifierGenerator identifierGenerator;
public Saml11TicketValidator(final String casServerUrlPrefix) {
super(casServerUrlPrefix);
this.basicParserPool = new BasicParserPool();
this.basicParserPool.setNamespaceAware(true);
try {
this.identifierGenerator = new SecureRandomIdentifierGenerator();
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
protected String getUrlSuffix() {
return "samlValidate";
}
protected void populateUrlAttributeMap(final Map<String, String> urlParameters) {
final String service = urlParameters.get("service");
urlParameters.remove("service");
urlParameters.remove("ticket");
urlParameters.put("TARGET", service);
}
@Override
protected void setDisableXmlSchemaValidation(final boolean disabled) {
if (disabled) {
this.basicParserPool.setSchema(null);
}
}
protected byte[] getBytes(final String text) {
try {
return CommonUtils.isNotBlank(getEncoding()) ? text.getBytes(getEncoding()) : text.getBytes();
} catch (final Exception e) {
return text.getBytes();
}
}
protected Assertion parseResponseFromServer(final String response) throws TicketValidationException {
try {
final Document responseDocument = this.basicParserPool.parse(new ByteArrayInputStream(getBytes(response)));
final Element responseRoot = responseDocument.getDocumentElement();
final UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory();
final Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(responseRoot);
final Envelope envelope = (Envelope) unmarshaller.unmarshall(responseRoot);
final Response samlResponse = (Response) envelope.getBody().getOrderedChildren().get(0);
final List<org.opensaml.saml1.core.Assertion> assertions = samlResponse.getAssertions();
if (assertions.isEmpty()) {
throw new TicketValidationException("No assertions found.");
}
for (final org.opensaml.saml1.core.Assertion assertion : assertions) {
if (!isValidAssertion(assertion)) {
continue;
}
final AuthenticationStatement authenticationStatement = getSAMLAuthenticationStatement(assertion);
if (authenticationStatement == null) {
throw new TicketValidationException("No AuthentiationStatement found in SAML Assertion.");
}
final Subject subject = authenticationStatement.getSubject();
if (subject == null) {
throw new TicketValidationException("No Subject found in SAML Assertion.");
}
final List<Attribute> attributes = getAttributesFor(assertion, subject);
final Map<String, Object> personAttributes = new HashMap<String, Object>();
for (final Attribute samlAttribute : attributes) {
final List<?> values = getValuesFrom(samlAttribute);
personAttributes.put(samlAttribute.getAttributeName(), values.size() == 1 ? values.get(0) : values);
}
final AttributePrincipal principal = new AttributePrincipalImpl(subject.getNameIdentifier()
.getNameIdentifier(), personAttributes);
final Map<String, Object> authenticationAttributes = new HashMap<String, Object>();
authenticationAttributes.put("samlAuthenticationStatement::authMethod",
authenticationStatement.getAuthenticationMethod());
final DateTime notBefore = assertion.getConditions().getNotBefore();
final DateTime notOnOrAfter = assertion.getConditions().getNotOnOrAfter();
final DateTime authenticationInstant = authenticationStatement.getAuthenticationInstant();
return new AssertionImpl(principal, notBefore.toDate(), notOnOrAfter.toDate(),
authenticationInstant.toDate(), authenticationAttributes);
}
} catch (final UnmarshallingException e) {
throw new TicketValidationException(e);
} catch (final XMLParserException e) {
throw new TicketValidationException(e);
}
throw new TicketValidationException(
"No Assertion found within valid time range. Either there's a replay of the ticket or there's clock drift. Check tolerance range, or server/client synchronization.");
}
private boolean isValidAssertion(final org.opensaml.saml1.core.Assertion assertion) {
final DateTime notBefore = assertion.getConditions().getNotBefore();
final DateTime notOnOrAfter = assertion.getConditions().getNotOnOrAfter();
if (notBefore == null || notOnOrAfter == null) {
logger.debug("Assertion has no bounding dates. Will not process.");
return false;
}
final DateTime currentTime = new DateTime(DateTimeZone.UTC);
final Interval validityRange = new Interval(notBefore.minus(this.tolerance), notOnOrAfter.plus(this.tolerance));
if (validityRange.contains(currentTime)) {
logger.debug("Current time is within the interval validity.");
return true;
}
if (currentTime.isBefore(validityRange.getStart())) {
logger.debug("skipping assertion that's not yet valid...");
return false;
}
logger.debug("skipping expired assertion...");
return false;
}
private AuthenticationStatement getSAMLAuthenticationStatement(final org.opensaml.saml1.core.Assertion assertion) {
final List<AuthenticationStatement> statements = assertion.getAuthenticationStatements();
if (statements.isEmpty()) {
return null;
}
return statements.get(0);
}
private List<Attribute> getAttributesFor(final org.opensaml.saml1.core.Assertion assertion, final Subject subject) {
final List<Attribute> attributes = new ArrayList<Attribute>();
for (final AttributeStatement attribute : assertion.getAttributeStatements()) {
if (subject.getNameIdentifier().getNameIdentifier()
.equals(attribute.getSubject().getNameIdentifier().getNameIdentifier())) {
attributes.addAll(attribute.getAttributes());
}
}
return attributes;
}
private List<?> getValuesFrom(final Attribute attribute) {
final List<Object> list = new ArrayList<Object>();
for (final Object o : attribute.getAttributeValues()) {
if (o instanceof XSAny) {
list.add(((XSAny) o).getTextContent());
} else if (o instanceof XSString) {
list.add(((XSString) o).getValue());
} else {
list.add(o.toString());
}
}
return list;
}
protected String retrieveResponseFromServer(final URL validationUrl, final String ticket) {
final String MESSAGE_TO_SEND = "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"><SOAP-ENV:Header/><SOAP-ENV:Body><samlp:Request xmlns:samlp=\"urn:oasis:names:tc:SAML:1.0:protocol\" MajorVersion=\"1\" MinorVersion=\"1\" RequestID=\""
+ this.identifierGenerator.generateIdentifier()
+ "\" IssueInstant=\""
+ CommonUtils.formatForUtcTime(new Date())
+ "\">"
+ "<samlp:AssertionArtifact>"
+ ticket
+ "</samlp:AssertionArtifact></samlp:Request></SOAP-ENV:Body></SOAP-ENV:Envelope>";
HttpURLConnection conn = null;
DataOutputStream out = null;
BufferedReader in = null;
try {
conn = this.getURLConnectionFactory().buildHttpURLConnection(validationUrl.openConnection());
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "text/xml");
conn.setRequestProperty("Content-Length", Integer.toString(MESSAGE_TO_SEND.length()));
conn.setRequestProperty("SOAPAction", "http://www.oasis-open.org/committees/security");
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(true);
out = new DataOutputStream(conn.getOutputStream());
out.writeBytes(MESSAGE_TO_SEND);
out.flush();
in = new BufferedReader(CommonUtils.isNotBlank(getEncoding()) ? new InputStreamReader(
conn.getInputStream(), Charset.forName(getEncoding())) : new InputStreamReader(
conn.getInputStream()));
final StringBuilder buffer = new StringBuilder(256);
String line;
while ((line = in.readLine()) != null) {
buffer.append(line);
}
return buffer.toString();
} catch (final IOException e) {
throw new RuntimeException(e);
} finally {
CommonUtils.closeQuietly(out);
CommonUtils.closeQuietly(in);
if (conn != null) {
conn.disconnect();
}
}
}
public void setTolerance(final long tolerance) {
this.tolerance = tolerance;
}
}

View File

@ -0,0 +1,8 @@
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:oasis:names:tc:SAML:1.0:protocol">
<soap:Header/>
<soap:Body>
<Request MajorVersion="1" MinorVersion="1" RequestID="%s" IssueInstant="%s">
<AssertionArtifact>%s</AssertionArtifact>
</Request>
</soap:Body>
</soap:Envelope>

View File

@ -0,0 +1,45 @@
/*
* 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.configuration;
import org.junit.Test;
import static org.junit.Assert.*;
public final class ConfigurationKeyTests {
@Test
public void gettersWithNoDefaultValue() {
final String name = "name";
final ConfigurationKey<Boolean> configurationKey = new ConfigurationKey<Boolean>(name);
assertEquals(name, configurationKey.getName());
assertNull(configurationKey.getDefaultValue());
}
@Test
public void gettersWithDefaultValue() {
final String name = "name";
final Boolean defaultValue = Boolean.TRUE;
final ConfigurationKey<Boolean> configurationKey = new ConfigurationKey<Boolean>(name, defaultValue);
assertEquals(name, configurationKey.getName());
assertEquals(defaultValue, configurationKey.getDefaultValue());
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.configuration;
import org.junit.Test;
import static org.junit.Assert.*;
public final class ConfigurationStrategyNameTests {
@Test
public void stringToClass() {
assertEquals(JndiConfigurationStrategyImpl.class, ConfigurationStrategyName.resolveToConfigurationStrategy(ConfigurationStrategyName.JNDI.name()));
assertEquals(WebXmlConfigurationStrategyImpl.class, ConfigurationStrategyName.resolveToConfigurationStrategy(ConfigurationStrategyName.WEB_XML.name()));
assertEquals(PropertiesConfigurationStrategyImpl.class, ConfigurationStrategyName.resolveToConfigurationStrategy(ConfigurationStrategyName.PROPERTY_FILE.name()));
assertEquals(SystemPropertiesConfigurationStrategyImpl.class, ConfigurationStrategyName.resolveToConfigurationStrategy(ConfigurationStrategyName.SYSTEM_PROPERTIES.name()));
assertEquals(LegacyConfigurationStrategyImpl.class, ConfigurationStrategyName.resolveToConfigurationStrategy(ConfigurationStrategyName.DEFAULT.name()));
assertEquals(LegacyConfigurationStrategyImpl.class, ConfigurationStrategyName.resolveToConfigurationStrategy("bleh!"));
}
}

View File

@ -0,0 +1,62 @@
/*
* 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.configuration;
import static org.junit.Assert.assertEquals;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockFilterConfig;
/**
* Tests {@link SystemPropertiesConfigurationStrategyImpl}.
*
* @author Jerome Leleu
* @since 3.4.0
*/
public class SystemPropertiesConfigurationStrategyImplTests {
private final static String PARAMETER_NAME = "parameter";
private SystemPropertiesConfigurationStrategyImpl impl;
private MockFilterConfig filterConfig;
@Before
public void setUp() throws Exception {
this.filterConfig = new MockFilterConfig();
this.impl = new SystemPropertiesConfigurationStrategyImpl();
this.impl.init(this.filterConfig, AbstractCasFilter.class);
}
@Test
public void testNoSystemPropertyDefined() {
final ConfigurationKey<String> key = ConfigurationKeys.SERVER_NAME;
// no system property defined
assertEquals(key.getDefaultValue(), impl.getString(key));
}
@Test
public void testWithSystemProperty() {
final ConfigurationKey<String> key = ConfigurationKeys.ARTIFACT_PARAMETER_NAME;
System.setProperty(key.getName(), PARAMETER_NAME);
assertEquals(PARAMETER_NAME, impl.getString(key));
}
}

View File

@ -0,0 +1,145 @@
/*
* 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.configuration;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockFilterConfig;
import org.springframework.mock.web.MockServletContext;
import static org.junit.Assert.*;
public final class WebXmlConfigurationStrategyImplTests {
private WebXmlConfigurationStrategyImpl impl;
private MockFilterConfig filterConfig;
@Before
public void setUp() throws Exception {
this.filterConfig = new MockFilterConfig();
this.impl = new WebXmlConfigurationStrategyImpl();
this.impl.init(this.filterConfig, AbstractCasFilter.class);
}
@Test
public void noKeyFoundGetDefaultForBoolean() {
final ConfigurationKey<Boolean> key = ConfigurationKeys.ACCEPT_ANY_PROXY;
assertEquals(key.getDefaultValue(), this.impl.getBoolean(key));
}
@Test
public void noKeyFoundGetDefaultForString() {
final ConfigurationKey<String> key = ConfigurationKeys.ARTIFACT_PARAMETER_NAME;
assertEquals(key.getDefaultValue(), this.impl.getString(key));
}
@Test
public void noKeyFoundGetDefaultForLong() {
final ConfigurationKey<Long> key = ConfigurationKeys.TOLERANCE;
assertEquals(key.getDefaultValue().longValue(), this.impl.getLong(key));
}
@Test
public void noKeyFoundGetDefaultForInt() {
final ConfigurationKey<Integer> key = ConfigurationKeys.MILLIS_BETWEEN_CLEAN_UPS;
assertEquals(key.getDefaultValue().intValue(), this.impl.getInt(key));
}
@Test
public void filterConfigValueForBoolean() {
final ConfigurationKey<Boolean> key = ConfigurationKeys.ACCEPT_ANY_PROXY;
final Boolean value = Boolean.TRUE;
this.filterConfig.addInitParameter(key.getName(), value.toString());
assertEquals(value, this.impl.getBoolean(key));
}
@Test
public void filterConfigValueForString() {
final ConfigurationKey<String> key = ConfigurationKeys.ARTIFACT_PARAMETER_NAME;
final String value = "foobar";
this.filterConfig.addInitParameter(key.getName(), value);
assertEquals(value, this.impl.getString(key));
}
@Test
public void filterConfigValueForLong() {
final ConfigurationKey<Long> key = ConfigurationKeys.TOLERANCE;
final long value = 1500;
this.filterConfig.addInitParameter(key.getName(), Long.toString(value));
assertEquals(value, this.impl.getLong(key));
}
@Test
public void filterConfigValueForInt() {
final ConfigurationKey<Integer> key = ConfigurationKeys.MILLIS_BETWEEN_CLEAN_UPS;
final int value = 1500;
this.filterConfig.addInitParameter(key.getName(), Integer.toString(value));
assertEquals(value, this.impl.getInt(key));
}
@Test
public void servletConfigValueForBoolean() {
final ConfigurationKey<Boolean> key = ConfigurationKeys.ACCEPT_ANY_PROXY;
final Boolean value = Boolean.TRUE;
final MockServletContext context = (MockServletContext) this.filterConfig.getServletContext();
context.addInitParameter(key.getName(), value.toString());
assertEquals(value, this.impl.getBoolean(key));
}
@Test
public void servletConfigValueForString() {
final ConfigurationKey<String> key = ConfigurationKeys.ARTIFACT_PARAMETER_NAME;
final String value = "foobar";
final MockServletContext context = (MockServletContext) this.filterConfig.getServletContext();
context.addInitParameter(key.getName(), value);
assertEquals(value, this.impl.getString(key));
}
@Test
public void servletConfigValueForLong() {
final ConfigurationKey<Long> key = ConfigurationKeys.TOLERANCE;
final long value = 1500;
final MockServletContext context = (MockServletContext) this.filterConfig.getServletContext();
context.addInitParameter(key.getName(), Long.toString(value));
assertEquals(value, this.impl.getLong(key));
}
@Test
public void servletConfigValueForInt() {
final ConfigurationKey<Integer> key = ConfigurationKeys.MILLIS_BETWEEN_CLEAN_UPS;
final int value = 1500;
final MockServletContext context = (MockServletContext) this.filterConfig.getServletContext();
context.addInitParameter(key.getName(), Integer.toString(value));
assertEquals(value, this.impl.getInt(key));
}
}

View File

@ -1,3 +1,21 @@
/*
* 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.session;
import java.nio.charset.Charset;

View File

@ -25,6 +25,8 @@ import java.io.IOException;
import javax.servlet.ServletException;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockFilterChain;
@ -35,12 +37,12 @@ import org.springframework.mock.web.MockHttpSession;
/**
* Tests {@link SingleSignOutFilter}.
*
*
* @author Jerome Leleu
* @since 3.3.1
*/
public class SingleSignOutFilterTests {
private final static String CAS_SERVER_URL_PREFIX = "http://myhost.com/mycasserver";
private final static String TICKET = "ST-yyyyy";
private final static String RELAY_STATE = "e1s1";
@ -49,7 +51,7 @@ public class SingleSignOutFilterTests {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private MockFilterChain filterChain;
@Before
public void setUp() throws Exception {
filter = new SingleSignOutFilter();
@ -61,16 +63,10 @@ public class SingleSignOutFilterTests {
filterChain = new MockFilterChain();
}
@Test(expected = IllegalArgumentException.class)
public void initWithoutCasServerUrlPrefix() throws ServletException {
filter = new SingleSignOutFilter();
filter.init(new MockFilterConfig());
}
@Test
public void tokenRequest() throws IOException, ServletException {
request.setParameter(SingleSignOutHandler.DEFAULT_ARTIFACT_PARAMETER_NAME, TICKET);
request.setQueryString(SingleSignOutHandler.DEFAULT_ARTIFACT_PARAMETER_NAME + "=" + TICKET);
request.setParameter(Protocol.CAS2.getArtifactParameterName(), TICKET);
request.setQueryString(Protocol.CAS2.getArtifactParameterName() + "=" + TICKET);
final MockHttpSession session = new MockHttpSession();
request.setSession(session);
filter.doFilter(request, response, filterChain);
@ -79,7 +75,7 @@ public class SingleSignOutFilterTests {
@Test
public void backChannelRequest() throws IOException, ServletException {
request.setParameter(SingleSignOutHandler.DEFAULT_LOGOUT_PARAMETER_NAME,
request.setParameter(ConfigurationKeys.LOGOUT_PARAMETER_NAME.getDefaultValue(),
LogoutMessageGenerator.generateBackChannelLogoutMessage(TICKET));
request.setMethod("POST");
final MockHttpSession session = new MockHttpSession();
@ -91,8 +87,8 @@ public class SingleSignOutFilterTests {
@Test
public void frontChannelRequest() throws IOException, ServletException {
final String logoutMessage = LogoutMessageGenerator.generateFrontChannelLogoutMessage(TICKET);
request.setParameter(SingleSignOutHandler.DEFAULT_FRONT_LOGOUT_PARAMETER_NAME, logoutMessage);
request.setQueryString(SingleSignOutHandler.DEFAULT_FRONT_LOGOUT_PARAMETER_NAME + "=" + logoutMessage);
request.setParameter(ConfigurationKeys.FRONT_LOGOUT_PARAMETER_NAME.getDefaultValue(), logoutMessage);
request.setQueryString(ConfigurationKeys.FRONT_LOGOUT_PARAMETER_NAME.getDefaultValue() + "=" + logoutMessage);
request.setMethod("GET");
final MockHttpSession session = new MockHttpSession();
SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage().addSessionById(TICKET, session);
@ -104,16 +100,16 @@ public class SingleSignOutFilterTests {
@Test
public void frontChannelRequestRelayState() throws IOException, ServletException {
final String logoutMessage = LogoutMessageGenerator.generateFrontChannelLogoutMessage(TICKET);
request.setParameter(SingleSignOutHandler.DEFAULT_FRONT_LOGOUT_PARAMETER_NAME, logoutMessage);
request.setParameter(SingleSignOutHandler.DEFAULT_RELAY_STATE_PARAMETER_NAME, RELAY_STATE);
request.setQueryString(SingleSignOutHandler.DEFAULT_FRONT_LOGOUT_PARAMETER_NAME + "=" + logoutMessage + "&" +
SingleSignOutHandler.DEFAULT_RELAY_STATE_PARAMETER_NAME + "=" + RELAY_STATE);
request.setParameter(ConfigurationKeys.FRONT_LOGOUT_PARAMETER_NAME.getDefaultValue(), logoutMessage);
request.setParameter(ConfigurationKeys.RELAY_STATE_PARAMETER_NAME.getDefaultValue(), RELAY_STATE);
request.setQueryString(ConfigurationKeys.FRONT_LOGOUT_PARAMETER_NAME.getDefaultValue() + "=" + logoutMessage + "&" +
ConfigurationKeys.RELAY_STATE_PARAMETER_NAME.getDefaultValue() + "=" + RELAY_STATE);
request.setMethod("GET");
final MockHttpSession session = new MockHttpSession();
SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage().addSessionById(TICKET, session);
filter.doFilter(request, response, filterChain);
assertNull(SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage().removeSessionByMappingId(TICKET));
assertEquals(CAS_SERVER_URL_PREFIX + "/logout?_eventId=next&" +
SingleSignOutHandler.DEFAULT_RELAY_STATE_PARAMETER_NAME + "=" + RELAY_STATE, response.getRedirectedUrl());
ConfigurationKeys.RELAY_STATE_PARAMETER_NAME.getDefaultValue() + "=" + RELAY_STATE, response.getRedirectedUrl());
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.ssl;
import org.junit.Before;
import org.junit.Test;
import java.io.*;
import static org.junit.Assert.*;
public final class HttpsURLConnectionFactoryTests {
private HttpsURLConnectionFactory httpsURLConnectionFactory;
@Before
public void setUp() throws Exception {
this.httpsURLConnectionFactory = new HttpsURLConnectionFactory();
}
@Test
public void serializeAndDeserialize() throws Exception {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this.httpsURLConnectionFactory);
oos.close();
final byte[] serializedHttpsUrlConnectionFactory = baos.toByteArray();
final ByteArrayInputStream bais = new ByteArrayInputStream(serializedHttpsUrlConnectionFactory);
final ObjectInputStream ois = new ObjectInputStream(bais);
final HttpsURLConnectionFactory deserializedObject = (HttpsURLConnectionFactory) ois.readObject();
assertEquals(this.httpsURLConnectionFactory, deserializedObject);
}
}

View File

@ -43,7 +43,7 @@ import junit.framework.TestCase;
*/
public class WhitelistHostnameVerifierTests extends TestCase {
/**
* Test method for {@link WhitelistHostnameVerifier#verify(String, SSLSession)}.
* Test method for {@link WhitelistHostnameVerifier#verify(String, javax.net.ssl.SSLSession)}.
*/
public void testVerify() {
final WhitelistHostnameVerifier verifier = new WhitelistHostnameVerifier("red.vt.edu, green.vt.edu,blue.vt.edu");

View File

@ -24,6 +24,8 @@ import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.jasig.cas.client.Protocol;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
@ -55,6 +57,10 @@ public final class CasFilterTests {
}
private static class TestCasFilter extends AbstractCasFilter {
public TestCasFilter() {
super(Protocol.CAS2);
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
// nothing to do

View File

@ -21,6 +21,7 @@ package org.jasig.cas.client.util;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import junit.framework.TestCase;
import org.jasig.cas.client.PublicTestHttpServer;
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;

View File

@ -0,0 +1,66 @@
/*
* 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.util;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockFilterConfig;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import javax.servlet.FilterChain;
import static org.junit.Assert.*;
public final class ErrorRedirectFilterTests {
private static final String REDIRECT_URL = "/ise.html";
private ErrorRedirectFilter errorRedirectFilter;
private FilterChain filterChain;
@Before
public void setUp() throws Exception {
this.errorRedirectFilter = new ErrorRedirectFilter();
final MockFilterConfig filterConfig = new MockFilterConfig();
filterConfig.addInitParameter(IllegalStateException.class.getName(), REDIRECT_URL);
this.errorRedirectFilter.init(filterConfig);
this.filterChain = new MockFilterChain();
}
@Test
public void noRootCause() throws Exception {
final MockHttpServletRequest request = new MockHttpServletRequest();
final MockHttpServletResponse response = new MockHttpServletResponse();
// this should be okay as the mock filter chain allows one call
this.errorRedirectFilter.doFilter(request, response, this.filterChain);
// this will fail as the mock filter chain will throw IllegalStateException
this.errorRedirectFilter.doFilter(request, response, this.filterChain);
assertEquals(REDIRECT_URL, response.getRedirectedUrl());
}
}

View File

@ -50,7 +50,10 @@ public class Cas10TicketValidationFilterTests {
final MockServletContext context = new MockServletContext();
context.addInitParameter("casServerUrlPrefix", "https://cas.example.com");
context.addInitParameter("renew", "true");
final TicketValidator validator = f.getTicketValidator(new MockFilterConfig(context));
context.addInitParameter("service", "http://www.jasig.org");
final MockFilterConfig config = new MockFilterConfig(context);
f.init(config);
final TicketValidator validator = f.getTicketValidator(config);
assertTrue(validator instanceof Cas10TicketValidator);
assertTrue(((Cas10TicketValidator) validator).isRenew());
}

View File

@ -18,8 +18,7 @@
*/
package org.jasig.cas.client.validation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.junit.Assert.*;
import java.io.UnsupportedEncodingException;
import org.jasig.cas.client.PublicTestHttpServer;
import org.junit.Before;
@ -80,4 +79,15 @@ public final class Cas10TicketValidatorTests extends AbstractTicketValidatorTest
// expected
}
}
@Test
public void urlEncodedValues() {
final String ticket = "ST-1-owKEOtYJjg77iHcCQpkl-cas01.example.org%26%73%65%72%76%69%63%65%3d%68%74%74%70%25%33%41%25%32%46%25%32%46%31%32%37%2e%30%2e%30%2e%31%25%32%46%62%6f%72%69%6e%67%25%32%46%23";
final String service = "foobar";
final String url = this.ticketValidator.constructValidationUrl(ticket, service);
final String encodedValue = this.ticketValidator.encodeUrl(ticket);
assertTrue(url.contains(encodedValue));
assertFalse(url.contains(ticket));
}
}

View File

@ -25,6 +25,7 @@ import org.jasig.cas.client.proxy.CleanUpTimerTask;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
import org.jasig.cas.client.util.MethodFlag;
import org.junit.Test;
import org.springframework.mock.web.MockFilterConfig;
import org.springframework.mock.web.MockServletContext;
@ -148,32 +149,40 @@ public class Cas20ProxyReceivingTicketValidationFilterTests extends TestCase {
public void testGetTicketValidator() throws Exception {
Cas20ProxyReceivingTicketValidationFilter filter = newCas20ProxyReceivingTicketValidationFilter();
filter.setProxyGrantingTicketStorage(storage);
filter.setMillisBetweenCleanUps(250);
filter.setTimer(defaultTimer);
filter.setTimerTask(new TimerTask() {
public void run() {
}
});
filter.init();
// Test case #1
final MockFilterConfig config1 = new MockFilterConfig();
config1.addInitParameter("allowedProxyChains", "https://a.example.com");
config1.addInitParameter("casServerUrlPrefix", "https://cas.jasig.org/");
config1.addInitParameter("service", "http://www.jasig.org");
filter.init(config1);
assertNotNull(filter.getTicketValidator(config1));
}
@Test
public void getTicketValidatorWithProxyChains() throws Exception {
Cas20ProxyReceivingTicketValidationFilter filter = newCas20ProxyReceivingTicketValidationFilter();
// Test case #2
final MockFilterConfig config2 = new MockFilterConfig();
config2.addInitParameter("allowedProxyChains", "https://a.example.com https://b.example.com");
config2.addInitParameter("casServerUrlPrefix", "https://cas.jasig.org/");
config2.addInitParameter("service", "http://www.jasig.org");
filter.init(config2);
assertNotNull(filter.getTicketValidator(config2));
}
@Test
public void getTIcketValidatorWithProxyChainsAndLineBreak() throws Exception {
Cas20ProxyReceivingTicketValidationFilter filter = newCas20ProxyReceivingTicketValidationFilter();
// Test case #3
final MockFilterConfig config3 = new MockFilterConfig();
config3.addInitParameter("allowedProxyChains",
"https://a.example.com https://b.example.com\nhttps://c.example.com");
config3.addInitParameter("casServerUrlPrefix", "https://cas.jasig.org/");
config3.addInitParameter("service", "http://www.jasig.org");
filter.init(config3);
assertNotNull(filter.getTicketValidator(config3));
}
@ -195,7 +204,10 @@ public class Cas20ProxyReceivingTicketValidationFilterTests extends TestCase {
final MockServletContext context = new MockServletContext();
context.addInitParameter("casServerUrlPrefix", "https://cas.example.com");
context.addInitParameter("renew", "true");
final TicketValidator validator = f.getTicketValidator(new MockFilterConfig(context));
context.addInitParameter("service", "http://www.jasig.org");
final MockFilterConfig config = new MockFilterConfig(context);
f.init(config);
final TicketValidator validator = f.getTicketValidator(config);
assertTrue(validator instanceof AbstractUrlBasedTicketValidator);
assertTrue(((AbstractUrlBasedTicketValidator) validator).isRenew());
}

View File

@ -16,13 +16,14 @@ specific language governing permissions and limitations
under the License.
This project includes:
"Java Concurrency in Practice" book annotations under Creative Commons Attribution License
AOP alliance under Public Domain
Apache Santuario under The Apache Software License, Version 2.0
Apache Velocity under The Apache Software License, Version 2.0
Atlassian Event under Atlassian End User License
Atlassian JIRA - Code - Core under Atlassian End User License
Atlassian Seraph under BSD License
atlassian-osuser under BSD License
Atlassian Seraph under Atlassian End User License
atlassian-osuser under Atlassian End User License
Bouncy Castle Provider under Bouncy Castle Licence
Codec under The Apache Software License, Version 2.0
commons-collections under Apache License, Version 2.0
@ -33,7 +34,6 @@ This project includes:
Jasig CAS Client for Java - Atlassian Integration under Apache License Version 2.0
Jasig CAS Client for Java - Core under Apache License Version 2.0
Java Servlet API under CDDL + GPLv2 with classpath exception
jcip-annotations under Creative Commons Attribution License
JCL 1.1.1 implemented over SLF4J under MIT License
Joda time under Apache 2
JUL to SLF4J bridge under MIT License

View File

@ -1,7 +1,7 @@
<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">
<parent>
<groupId>org.jasig.cas.client</groupId>
<version>3.3.2-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
<artifactId>cas-client</artifactId>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -46,6 +46,11 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<artifactId>atlassian-osuser</artifactId>
<groupId>com.atlassian.osuser</groupId>

View File

@ -16,6 +16,7 @@ specific language governing permissions and limitations
under the License.
This project includes:
"Java Concurrency in Practice" book annotations under Creative Commons Attribution License
Apache Santuario under The Apache Software License, Version 2.0
Apache Velocity under The Apache Software License, Version 2.0
Bouncy Castle Provider under Bouncy Castle Licence
@ -27,7 +28,6 @@ This project includes:
Jasig CAS Client for Java - JBoss Integration under Apache License Version 2.0
Java Servlet API under CDDL + GPLv2 with classpath exception
JBoss Application Server Tomcat under lgpl
jcip-annotations under Creative Commons Attribution License
JCL 1.1.1 implemented over SLF4J under MIT License
Joda time under Apache 2
JUL to SLF4J bridge under MIT License

View File

@ -1,7 +1,7 @@
<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">
<parent>
<groupId>org.jasig.cas.client</groupId>
<version>3.3.2-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
<artifactId>cas-client</artifactId>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -0,0 +1,34 @@
/*
* 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.configuration;
/**
* Simple extension to the {@link org.jasig.cas.client.configuration.JndiConfigurationStrategyImpl} to provide a JBoss 7 compatible prefix.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public final class JBossCompatibleJndiConfigurationStrategyImpl extends JndiConfigurationStrategyImpl {
private static final String ENVIRONMENT_PREFIX = "java:/comp/env/cas/";
public JBossCompatibleJndiConfigurationStrategyImpl() {
super(ENVIRONMENT_PREFIX);
}
}

View File

@ -27,6 +27,8 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.jaas.AssertionPrincipal;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
@ -50,12 +52,16 @@ import org.jboss.web.tomcat.security.login.WebAuthentication;
*/
public final class WebAuthenticationFilter extends AbstractCasFilter {
public WebAuthenticationFilter() {
super(Protocol.CAS2);
}
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
final FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final HttpSession session = request.getSession();
final String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());
final String ticket = CommonUtils.safeGetParameter(request, getProtocol().getArtifactParameterName());
if (session != null && session.getAttribute(CONST_CAS_ASSERTION) == null && ticket != null) {
try {

View File

@ -16,6 +16,7 @@ specific language governing permissions and limitations
under the License.
This project includes:
"Java Concurrency in Practice" book annotations under Creative Commons Attribution License
Apache Santuario under The Apache Software License, Version 2.0
Apache Velocity under The Apache Software License, Version 2.0
Bouncy Castle Provider under Bouncy Castle Licence
@ -26,7 +27,6 @@ This project includes:
Jasig CAS Client for Java - Common Tomcat Integration Support under Apache License Version 2.0
Jasig CAS Client for Java - Core under Apache License Version 2.0
Java Servlet API under CDDL + GPLv2 with classpath exception
jcip-annotations under Creative Commons Attribution License
JCL 1.1.1 implemented over SLF4J under MIT License
Joda time under Apache 2
JUL to SLF4J bridge under MIT License

View File

@ -3,7 +3,7 @@
<parent>
<artifactId>cas-client</artifactId>
<groupId>org.jasig.cas.client</groupId>
<version>3.3.2-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -16,6 +16,7 @@ specific language governing permissions and limitations
under the License.
This project includes:
"Java Concurrency in Practice" book annotations under Creative Commons Attribution License
Apache Santuario under The Apache Software License, Version 2.0
Apache Velocity under The Apache Software License, Version 2.0
Bouncy Castle Provider under Bouncy Castle Licence
@ -28,7 +29,6 @@ This project includes:
Jasig CAS Client for Java - Core under Apache License Version 2.0
Jasig CAS Client for Java - Tomcat 6.x Integration under Apache License Version 2.0
Java Servlet API under CDDL + GPLv2 with classpath exception
jcip-annotations under Creative Commons Attribution License
JCL 1.1.1 implemented over SLF4J under MIT License
Joda time under Apache 2
JUL to SLF4J bridge under MIT License

View File

@ -3,7 +3,7 @@
<parent>
<artifactId>cas-client</artifactId>
<groupId>org.jasig.cas.client</groupId>
<version>3.3.2-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -20,6 +20,14 @@
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-support-saml</artifactId>
<version>${project.version}</version>
<type>jar</type>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>catalina</artifactId>

View File

@ -16,6 +16,7 @@ specific language governing permissions and limitations
under the License.
This project includes:
"Java Concurrency in Practice" book annotations under Creative Commons Attribution License
Apache Santuario under The Apache Software License, Version 2.0
Apache Velocity under The Apache Software License, Version 2.0
Bouncy Castle Provider under Bouncy Castle Licence
@ -27,7 +28,6 @@ This project includes:
Jasig CAS Client for Java - Core under Apache License Version 2.0
Jasig CAS Client for Java - Tomcat 7.x Integration under Apache License Version 2.0
Java Servlet API under CDDL + GPLv2 with classpath exception
jcip-annotations under Creative Commons Attribution License
JCL 1.1.1 implemented over SLF4J under MIT License
Joda time under Apache 2
JUL to SLF4J bridge under MIT License

View File

@ -3,7 +3,7 @@
<parent>
<artifactId>cas-client</artifactId>
<groupId>org.jasig.cas.client</groupId>
<version>3.3.2-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -20,6 +20,14 @@
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-support-saml</artifactId>
<version>${project.version}</version>
<type>jar</type>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>

View File

@ -16,6 +16,7 @@ specific language governing permissions and limitations
under the License.
This project includes:
"Java Concurrency in Practice" book annotations under Creative Commons Attribution License
Apache Santuario under The Apache Software License, Version 2.0
Apache Velocity under The Apache Software License, Version 2.0
Bouncy Castle Provider under Bouncy Castle Licence
@ -27,7 +28,6 @@ This project includes:
Jasig CAS Client for Java - Core under Apache License Version 2.0
Jasig CAS Client for Java - Distributed Proxy Storage Support: EhCache under Apache License Version 2.0
Java Servlet API under CDDL + GPLv2 with classpath exception
jcip-annotations under Creative Commons Attribution License
JCL 1.1.1 implemented over SLF4J under MIT License
Joda time under Apache 2
JUL to SLF4J bridge under MIT License

View File

@ -3,7 +3,7 @@
<parent>
<artifactId>cas-client</artifactId>
<groupId>org.jasig.cas.client</groupId>
<version>3.3.2-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<name>Jasig CAS Client for Java - Distributed Proxy Storage Support: EhCache

View File

@ -16,6 +16,7 @@ specific language governing permissions and limitations
under the License.
This project includes:
"Java Concurrency in Practice" book annotations under Creative Commons Attribution License
Apache Santuario under The Apache Software License, Version 2.0
Apache Velocity under The Apache Software License, Version 2.0
Bouncy Castle Provider under Bouncy Castle Licence
@ -27,7 +28,6 @@ This project includes:
Jasig CAS Client for Java - Distributed Proxy Storage Support:
Memcached under Apache License Version 2.0
Java Servlet API under CDDL + GPLv2 with classpath exception
jcip-annotations under Creative Commons Attribution License
JCL 1.1.1 implemented over SLF4J under MIT License
Joda time under Apache 2
JUL to SLF4J bridge under MIT License

View File

@ -3,7 +3,7 @@
<parent>
<artifactId>cas-client</artifactId>
<groupId>org.jasig.cas.client</groupId>
<version>3.3.2-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -0,0 +1,44 @@
<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">
<parent>
<groupId>org.jasig.cas.client</groupId>
<version>3.4.0-SNAPSHOT</version>
<artifactId>cas-client</artifactId>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-support-saml</artifactId>
<packaging>jar</packaging>
<name>Jasig CAS Client for Java - SAML Protocol Support</name>
<dependencies>
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.7</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -18,13 +18,12 @@
*/
package org.jasig.cas.client.authentication;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import org.jasig.cas.client.Protocol;
/**
* Extension to the default Authentication filter that sets the required SAML1.1 artifact parameter name and service parameter name.
* <p>
* Note, as of 3.3, the final keyword was removed to allow you to override the method to retrieve tickets, per CASC-154s
* Note, as of 3.3, the final keyword was removed to allow you to override the method to retrieve tickets, per CASC-154
*
* @author Scott Battaglia
* @since 3.1.12
@ -32,13 +31,7 @@ import javax.servlet.ServletException;
*/
public class Saml11AuthenticationFilter extends AuthenticationFilter {
protected final void initInternal(final FilterConfig filterConfig) throws ServletException {
super.initInternal(filterConfig);
logger.warn("SAML1.1 compliance requires the [artifactParameterName] and [serviceParameterName] to be set to specified values.");
logger.warn("This filter will overwrite any user-provided values (if any are provided)");
setArtifactParameterName("SAMLart");
setServiceParameterName("TARGET");
public Saml11AuthenticationFilter() {
super(Protocol.SAML11);
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.util;
import java.util.Date;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
/**
* SAML utility class.
*
* @author Marvin S. Addison
* @since 3.4
*/
public final class SamlUtils {
private static final DateTimeFormatter ISO_FORMAT = ISODateTimeFormat.dateTimeNoMillis();
private SamlUtils() {
// nothing to do
}
public static String formatForUtcTime(final Date date) {
return ISO_FORMAT.print(new DateTime(date).withZone(DateTimeZone.UTC));
}
public static Date parseUtcDate(final String date) {
if (CommonUtils.isEmpty(date)) {
return null;
}
return ISODateTimeFormat.dateTimeParser().parseDateTime(date).toDate();
}
}

View File

@ -19,51 +19,39 @@
package org.jasig.cas.client.validation;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
import org.jasig.cas.client.ssl.HttpsURLConnectionFactory;
/**
* Implementation of TicketValidationFilter that can instanciate a SAML 1.1 Ticket Validator.
* <p>
* <p/>
* Deployers can provide the "casServerUrlPrefix" and "tolerance" properties of the Saml11TicketValidator via the
* context or filter init parameters.
*
* @author Scott Battaglia
* @author Marvin S. Addison
* @version $Revision$ $Date$
* @since 3.1
*/
public class Saml11TicketValidationFilter extends AbstractTicketValidationFilter {
public Saml11TicketValidationFilter() {
setArtifactParameterName("SAMLart");
setServiceParameterName("TARGET");
}
protected final void initInternal(final FilterConfig filterConfig) throws ServletException {
super.initInternal(filterConfig);
logger.warn("SAML1.1 compliance requires the [artifactParameterName] and [serviceParameterName] to be set to specified values.");
logger.warn("This filter will overwrite any user-provided values (if any are provided)");
setArtifactParameterName("SAMLart");
setServiceParameterName("TARGET");
super(Protocol.SAML11);
}
protected final TicketValidator getTicketValidator(final FilterConfig filterConfig) {
final Saml11TicketValidator validator = new Saml11TicketValidator(getPropertyFromInitParams(filterConfig,
"casServerUrlPrefix", null));
final String tolerance = getPropertyFromInitParams(filterConfig, "tolerance", "1000");
validator.setTolerance(Long.parseLong(tolerance));
validator.setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
final Saml11TicketValidator validator = new Saml11TicketValidator(getString(ConfigurationKeys.CAS_SERVER_URL_PREFIX));
final long tolerance = getLong(ConfigurationKeys.TOLERANCE);
validator.setTolerance(tolerance);
validator.setRenew(getBoolean(ConfigurationKeys.RENEW));
final HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(filterConfig),
getSSLConfig(filterConfig));
final HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(), getSSLConfig());
validator.setURLConnectionFactory(factory);
validator.setEncoding(getPropertyFromInitParams(filterConfig, "encoding", null));
validator.setDisableXmlSchemaValidation(parseBoolean(getPropertyFromInitParams(filterConfig,
"disableXmlSchemaValidation", "false")));
validator.setEncoding(getString(ConfigurationKeys.ENCODING));
return validator;
}
}

View File

@ -0,0 +1,238 @@
/*
* 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.validation;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.*;
import org.jasig.cas.client.authentication.AttributePrincipalImpl;
import org.jasig.cas.client.util.*;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.xml.namespace.NamespaceContext;
/**
* TicketValidator that can understand validating a SAML artifact. This includes the SOAP request/response.
*
* @author Scott Battaglia
* @author Marvin S. Addison
* @since 3.1
*/
public final class Saml11TicketValidator extends AbstractUrlBasedTicketValidator {
/** Authentication attribute containing SAML AuthenticationMethod attribute value. */
public static final String AUTH_METHOD_ATTRIBUTE = "samlAuthenticationStatement::authMethod";
/** SAML 1.1 request template. */
private static final String SAML_REQUEST_TEMPLATE;
/** SAML 1.1. namespace context. */
private static final NamespaceContext NS_CONTEXT = new MapNamespaceContext(
"soap->http://schemas.xmlsoap.org/soap/envelope/",
"sa->urn:oasis:names:tc:SAML:1.0:assertion",
"sp->urn:oasis:names:tc:SAML:1.0:protocol");
/** XPath expression to extract Assertion validity start date. */
private static final ThreadLocalXPathExpression XPATH_ASSERTION_DATE_START =
new ThreadLocalXPathExpression("//sa:Assertion/sa:Conditions/@NotBefore", NS_CONTEXT);
/** XPath expression to extract Assertion validity end date. */
private static final ThreadLocalXPathExpression XPATH_ASSERTION_DATE_END =
new ThreadLocalXPathExpression("//sa:Assertion/sa:Conditions/@NotOnOrAfter", NS_CONTEXT);
/** XPath expression to extract NameIdentifier. */
private static final ThreadLocalXPathExpression XPATH_NAME_ID =
new ThreadLocalXPathExpression("//sa:AuthenticationStatement/sa:Subject/sa:NameIdentifier", NS_CONTEXT);
/** XPath expression to extract authentication method. */
private static final ThreadLocalXPathExpression XPATH_AUTH_METHOD =
new ThreadLocalXPathExpression("//sa:AuthenticationStatement/@AuthenticationMethod", NS_CONTEXT);
/** XPath expression to extract attributes. */
private static final ThreadLocalXPathExpression XPATH_ATTRIBUTES =
new ThreadLocalXPathExpression("//sa:AttributeStatement/sa:Attribute", NS_CONTEXT);
private static final String HEX_CHARS = "0123456789abcdef";
/** Time tolerance to allow for time drifting. */
private long tolerance = 1000L;
private final Random random;
/** Class initializer. */
static {
try {
SAML_REQUEST_TEMPLATE = IOUtils.readString(
Saml11TicketValidator.class.getResourceAsStream("/META-INF/cas/samlRequestTemplate.xml"));
} catch (IOException e) {
throw new IllegalStateException("Cannot load SAML request template from classpath", e);
}
}
public Saml11TicketValidator(final String casServerUrlPrefix) {
super(casServerUrlPrefix);
try {
random = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Cannot find required SHA1PRNG algorithm");
}
}
protected String getUrlSuffix() {
return "samlValidate";
}
protected void populateUrlAttributeMap(final Map<String, String> urlParameters) {
final String service = urlParameters.get("service");
urlParameters.remove("service");
urlParameters.remove("ticket");
urlParameters.put("TARGET", service);
}
protected Assertion parseResponseFromServer(final String response) throws TicketValidationException {
try {
final Document document = XmlUtils.newDocument(response);
final Date assertionValidityStart = SamlUtils.parseUtcDate(
XPATH_ASSERTION_DATE_START.evaluateAsString(document));
final Date assertionValidityEnd = SamlUtils.parseUtcDate(
XPATH_ASSERTION_DATE_END.evaluateAsString(document));
if (!isValidAssertion(assertionValidityStart, assertionValidityEnd)) {
throw new TicketValidationException("Invalid SAML assertion");
}
final String nameId = XPATH_NAME_ID.evaluateAsString(document);
if (nameId == null) {
throw new TicketValidationException("SAML assertion does not contain NameIdentifier element");
}
final String authMethod = XPATH_AUTH_METHOD.evaluateAsString(document);
final NodeList attributes = XPATH_ATTRIBUTES.evaluateAsNodeList(document);
final Map<String, Object> principalAttributes = new HashMap<String, Object>(attributes.getLength());
Element attribute;
NodeList values;
String name;
for (int i = 0; i < attributes.getLength(); i++) {
attribute = (Element) attributes.item(i);
name = attribute.getAttribute("AttributeName");
logger.trace("Processing attribute {}", name);
values = attribute.getElementsByTagNameNS("*", "AttributeValue");
if (values.getLength() == 1) {
principalAttributes.put(name, values.item(0).getTextContent());
} else {
final Collection<Object> items = new ArrayList<Object>(values.getLength());
for (int j = 0; j < values.getLength(); j++) {
items.add(values.item(j).getTextContent());
}
principalAttributes.put(name, items);
}
}
return new AssertionImpl(
new AttributePrincipalImpl(nameId, principalAttributes),
assertionValidityStart,
assertionValidityEnd,
new Date(),
Collections.singletonMap(AUTH_METHOD_ATTRIBUTE, (Object) authMethod));
} catch (final Exception e) {
throw new TicketValidationException("Error processing SAML response", e);
}
}
private boolean isValidAssertion(final Date notBefore, final Date notOnOrAfter) {
if (notBefore == null || notOnOrAfter == null) {
logger.debug("Assertion is not valid because it does not have bounding dates.");
return false;
}
final DateTime currentTime = new DateTime(DateTimeZone.UTC);
final Interval validityRange = new Interval(
new DateTime(notBefore).minus(this.tolerance),
new DateTime(notOnOrAfter).plus(this.tolerance));
if (validityRange.contains(currentTime)) {
logger.debug("Current time is within the interval validity.");
return true;
}
if (currentTime.isBefore(validityRange.getStart())) {
logger.debug("Assertion is not yet valid");
} else {
logger.debug("Assertion is expired");
}
return false;
}
protected String retrieveResponseFromServer(final URL validationUrl, final String ticket) {
final String request = String.format(
SAML_REQUEST_TEMPLATE,
generateId(),
SamlUtils.formatForUtcTime(new Date()),
ticket);
HttpURLConnection conn = null;
try {
conn = this.getURLConnectionFactory().buildHttpURLConnection(validationUrl.openConnection());
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "text/xml");
conn.setRequestProperty("Content-Length", Integer.toString(request.length()));
conn.setRequestProperty("SOAPAction", "http://www.oasis-open.org/committees/security");
conn.setUseCaches(false);
conn.setDoInput(true);
conn.setDoOutput(true);
final Charset charset = CommonUtils.isNotBlank(getEncoding()) ?
Charset.forName(getEncoding()) : IOUtils.UTF8;
conn.getOutputStream().write(request.getBytes(charset));
conn.getOutputStream().flush();
return IOUtils.readString(conn.getInputStream(), charset);
} catch (final IOException e) {
throw new RuntimeException("IO error sending HTTP request to /samlValidate", e);
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
public void setTolerance(final long tolerance) {
this.tolerance = tolerance;
}
private String generateId() {
final byte[] data = new byte[16];
random.nextBytes(data);
final StringBuilder id = new StringBuilder(33);
id.append('_');
for (int i = 0; i < data.length; i++) {
id.append(HEX_CHARS.charAt((data[i] & 0xF0) >> 4));
id.append(HEX_CHARS.charAt(data[i] & 0x0F));
}
return id.toString();
}
}

View File

@ -0,0 +1,40 @@
/*
* 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.util;
import java.util.Date;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Test cases for {@link SamlUtils}.
*
* @author Marvin S. Addison
*/
public class SamlUtilsTest {
@Test
public void testParseUtcDate() {
final Date expected = new Date(1424437961025L);
assertEquals(expected, SamlUtils.parseUtcDate("2015-02-20T08:12:41.025-0500"));
}
}

View File

@ -20,6 +20,7 @@ package org.jasig.cas.client.validation;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Test;
import org.springframework.mock.web.MockFilterConfig;
import org.springframework.mock.web.MockServletContext;
@ -50,7 +51,10 @@ public class Saml11TicketValidationFilterTests {
final MockServletContext context = new MockServletContext();
context.addInitParameter("casServerUrlPrefix", "https://cas.example.com");
context.addInitParameter("renew", "true");
final TicketValidator validator = f.getTicketValidator(new MockFilterConfig(context));
context.addInitParameter("service", "http://www.jasig.org");
final MockFilterConfig config = new MockFilterConfig(context);
f.init(config);
final TicketValidator validator = f.getTicketValidator(config);
assertTrue(validator instanceof Saml11TicketValidator);
assertTrue(((Saml11TicketValidator) validator).isRenew());
}

View File

@ -21,9 +21,10 @@ package org.jasig.cas.client.validation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.Date;
import org.jasig.cas.client.PublicTestHttpServer;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.SamlUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Interval;
@ -45,7 +46,7 @@ public final class Saml11TicketValidatorTests extends AbstractTicketValidatorTes
@Before
public void setUp() throws Exception {
this.validator = new Saml11TicketValidator(CONST_CAS_SERVER_URL_PREFIX + "9051");
this.validator = new Saml11TicketValidator(AbstractTicketValidatorTests.CONST_CAS_SERVER_URL_PREFIX + "9051");
this.validator.setTolerance(1000L);
}
@ -82,13 +83,13 @@ public final class Saml11TicketValidatorTests extends AbstractTicketValidatorTes
final Interval range = currentTimeRangeInterval();
final Date now = new Date();
final String RESPONSE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"><SOAP-ENV:Header/><SOAP-ENV:Body><Response xmlns=\"urn:oasis:names:tc:SAML:1.0:protocol\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:samlp=\"urn:oasis:names:tc:SAML:1.0:protocol\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" IssueInstant=\""
+ CommonUtils.formatForUtcTime(now)
+ SamlUtils.formatForUtcTime(now)
+ "\" MajorVersion=\"1\" MinorVersion=\"1\" Recipient=\"test\" ResponseID=\"_e1e2124c08ab456eab0bbab3e1c0c433\"><Status><StatusCode Value=\"samlp:Success\"></StatusCode></Status><Assertion xmlns=\"urn:oasis:names:tc:SAML:1.0:assertion\" AssertionID=\"_d2fd0d6e4da6a6d7d2ba5274ab570d5c\" IssueInstant=\""
+ CommonUtils.formatForUtcTime(now)
+ SamlUtils.formatForUtcTime(now)
+ "\" Issuer=\"testIssuer\" MajorVersion=\"1\" MinorVersion=\"1\"><Conditions NotBefore=\""
+ CommonUtils.formatForUtcTime(range.getStart().toDate())
+ SamlUtils.formatForUtcTime(range.getStart().toDate())
+ "\" NotOnOrAfter=\""
+ CommonUtils.formatForUtcTime(range.getEnd().toDate())
+ SamlUtils.formatForUtcTime(range.getEnd().toDate())
+ "\"><AudienceRestrictionCondition><Audience>test</Audience></AudienceRestrictionCondition></Conditions><AuthenticationStatement AuthenticationInstant=\"2008-06-19T14:34:44.426Z\" AuthenticationMethod=\"urn:ietf:rfc:2246\"><Subject><NameIdentifier>testPrincipal</NameIdentifier><SubjectConfirmation><ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:artifact</ConfirmationMethod></SubjectConfirmation></Subject></AuthenticationStatement></Assertion></Response></SOAP-ENV:Body></SOAP-ENV:Envelope>";
server.content = RESPONSE.getBytes(server.encoding);
try {
@ -106,22 +107,28 @@ public final class Saml11TicketValidatorTests extends AbstractTicketValidatorTes
final String response = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><soap11:Envelope xmlns:soap11=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap11:Body>"
+ "<saml1p:Response xmlns:saml1p=\"urn:oasis:names:tc:SAML:1.0:protocol\" InResponseTo=\"_fd1632b5dfa921623e7ca6f9ab727161\" IssueInstant=\""
+ CommonUtils.formatForUtcTime(now)
+ SamlUtils.formatForUtcTime(now)
+ "\" MajorVersion=\"1\" MinorVersion=\"1\" Recipient=\"https://example.com/test-client/secure/?TARGET=https%3A%2F%2Fexample.com%2Ftest-client%2Fsecure%2F\" ResponseID=\"_436dbb2cca5166af29250f431a07888f\">"
+ "<saml1p:Status><saml1p:StatusCode Value=\"saml1p:Success\"/></saml1p:Status>"
+ "<saml1:Assertion xmlns:saml1=\"urn:oasis:names:tc:SAML:1.0:assertion\" IssueInstant=\""
+ CommonUtils.formatForUtcTime(now)
+ SamlUtils.formatForUtcTime(now)
+ "\" Issuer=\"localhost\" MajorVersion=\"1\" MinorVersion=\"1\">"
+ "<saml1:Conditions NotBefore=\""
+ CommonUtils.formatForUtcTime(range.getStart().toDate())
+ SamlUtils.formatForUtcTime(range.getStart().toDate())
+ "\" NotOnOrAfter=\""
+ CommonUtils.formatForUtcTime(range.getEnd().toDate())
+ SamlUtils.formatForUtcTime(range.getEnd().toDate())
+ "\">"
+ "<saml1:AudienceRestrictionCondition><saml1:Audience>https://example.com/test-client/secure/</saml1:Audience></saml1:AudienceRestrictionCondition></saml1:Conditions>"
+ "<saml1:AudienceRestrictionCondition><saml1:Audience>https://example.com/test-client/secure/</saml1:Audience>"
+ "</saml1:AudienceRestrictionCondition></saml1:Conditions>"
+ "<saml1:AuthenticationStatement AuthenticationInstant=\""
+ CommonUtils.formatForUtcTime(now)
+ SamlUtils.formatForUtcTime(now)
+ "\" AuthenticationMethod=\"urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport\">"
+ "<saml1:Subject><saml1:NameIdentifier>testPrincipal</saml1:NameIdentifier><saml1:SubjectConfirmation><saml1:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:artifact</saml1:ConfirmationMethod></saml1:SubjectConfirmation></saml1:Subject></saml1:AuthenticationStatement><saml1:AttributeStatement><saml1:Subject><saml1:NameIdentifier>testPrincipal</saml1:NameIdentifier><saml1:SubjectConfirmation><saml1:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:artifact</saml1:ConfirmationMethod></saml1:SubjectConfirmation></saml1:Subject><saml1:Attribute AttributeName=\"uid\" AttributeNamespace=\"http://www.ja-sig.org/products/cas/\"><saml1:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">12345</saml1:AttributeValue>"
+ "<saml1:Subject><saml1:NameIdentifier>testPrincipal</saml1:NameIdentifier>"
+ "<saml1:SubjectConfirmation><saml1:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:artifact</saml1:ConfirmationMethod></saml1:SubjectConfirmation>"
+ "</saml1:Subject></saml1:AuthenticationStatement>"
+ "<saml1:AttributeStatement><saml1:Subject><saml1:NameIdentifier>testPrincipal</saml1:NameIdentifier>"
+ "<saml1:SubjectConfirmation><saml1:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:artifact</saml1:ConfirmationMethod></saml1:SubjectConfirmation></saml1:Subject>"
+ "<saml1:Attribute AttributeName=\"uid\" AttributeNamespace=\"http://www.ja-sig.org/products/cas/\"><saml1:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">12345</saml1:AttributeValue>"
+ "</saml1:Attribute><saml1:Attribute AttributeName=\"accountState\" AttributeNamespace=\"http://www.ja-sig.org/products/cas/\">"
+ "<saml1:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">ACTIVE</saml1:AttributeValue>"
+ "</saml1:Attribute><saml1:Attribute AttributeName=\"eduPersonAffiliation\" AttributeNamespace=\"http://www.ja-sig.org/products/cas/\">"
@ -132,7 +139,13 @@ public final class Saml11TicketValidatorTests extends AbstractTicketValidatorTes
server.content = response.getBytes(server.encoding);
try {
final Assertion a = this.validator.validate("test", "test");
assertEquals(
"urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
a.getAttributes().get(Saml11TicketValidator.AUTH_METHOD_ATTRIBUTE));
assertEquals("testPrincipal", a.getPrincipal().getName());
assertEquals("12345", a.getPrincipal().getAttributes().get("uid"));
assertEquals("ACTIVE", a.getPrincipal().getAttributes().get("accountState"));
assertEquals(3, ((Collection) a.getPrincipal().getAttributes().get("eduPersonAffiliation")).size());
} catch (final TicketValidationException e) {
fail(e.toString());
}

54
pom.xml
View File

@ -6,7 +6,7 @@
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.jasig.cas.client</groupId>
<version>3.3.2-SNAPSHOT</version>
<version>3.4.0-SNAPSHOT</version>
<artifactId>cas-client</artifactId>
<packaging>pom</packaging>
@ -159,6 +159,56 @@
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>test</scope>
<version>1.2.15</version>
<exclusions>
<exclusion>
<artifactId>jmxri</artifactId>
<groupId>com.sun.jmx</groupId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
@ -198,12 +248,14 @@
<module>cas-client-integration-jboss</module>
<module>cas-client-support-distributed-ehcache</module>
<module>cas-client-support-distributed-memcached</module>
<module>cas-client-support-saml</module>
<module>cas-client-integration-tomcat-common</module>
<module>cas-client-integration-tomcat-v6</module>
<module>cas-client-integration-tomcat-v7</module>
</modules>
<properties>
<spring.version>3.1.3.RELEASE</spring.version>
<ehcache.version>2.2.0</ehcache.version>
<clover.version>3.0.2</clover.version>
<slf4j.version>1.7.1</slf4j.version>