diff --git a/README.md b/README.md
index 03a63d2..65785ce 100644
--- a/README.md
+++ b/README.md
@@ -122,6 +122,16 @@ files in the modules (`cas-client-integration-jboss` and `cas-client-support-dis
```
+- Tomcat 9.0.x is provided by this dependency:
+
+```xml
+Realm implementation for all CAS realms.
+ *
+ * @author Marvin S. Addison
+ * @version $Revision$
+ *
+ */
+public abstract class AbstractCasRealm extends RealmBase implements CasRealm {
+
+ /** {@inheritDoc} */
+ @Override
+ public Principal authenticate(final Principal p) {
+ return getDelegate().authenticate(p);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String[] getRoles(final Principal p) {
+ return getDelegate().getRoles(p);
+ }
+
+ @Override
+ public boolean hasRole(final Principal principal, final String role) {
+ return getDelegate().hasRole(principal, role);
+ }
+
+ @Override
+ public boolean hasRole(final Wrapper wrapper, final Principal principal, final String role) {
+ return hasRole(principal, role);
+ }
+
+ public String getInfo() {
+ return getClass().getName() + "/1.0";
+ }
+
+ @Override
+ protected String getPassword(final String userName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected Principal getPrincipal(final String userName) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @return Delegate that all {@link CasRealm} operations are delegated to.
+ */
+ protected abstract CasRealm getDelegate();
+}
diff --git a/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/AbstractLogoutValve.java b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/AbstractLogoutValve.java
new file mode 100644
index 0000000..549a3af
--- /dev/null
+++ b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/AbstractLogoutValve.java
@@ -0,0 +1,58 @@
+/**
+ * Licensed to Apereo under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work
+ * for additional information regarding copyright ownership.
+ * Apereo licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a
+ * copy of the License at the following location:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0 + *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jasig.cas.client.tomcat.v90;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.valves.ValveBase;
+import org.jasig.cas.client.tomcat.LogoutHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletException;
+
+import java.io.*;
+
+/**
+ * Abstract base class for Container-managed log out. Removes the attributes
+ * from the session.
+ *
+ * @author Scott Battaglia
+ * @author Marvin S. Addison
+ * @version $Revision$ $Date$
+ * @since 3.1.12
+ */
+public abstract class AbstractLogoutValve extends ValveBase {
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ @Override
+ public final void invoke(final Request request, final Response response) throws IOException, ServletException {
+ if (getLogoutHandler().isLogoutRequest(request)) {
+ getLogoutHandler().logout(request, response);
+ // Do not proceed up valve chain
+ return;
+ }
+
+ logger.debug("URI is not a logout request: {}", request.getRequestURI());
+ getNext().invoke(request, response);
+ }
+
+ protected abstract LogoutHandler getLogoutHandler();
+}
diff --git a/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/AssertionCasRealm.java b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/AssertionCasRealm.java
new file mode 100644
index 0000000..5a8659b
--- /dev/null
+++ b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/AssertionCasRealm.java
@@ -0,0 +1,50 @@
+/**
+ * Licensed to Apereo under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work
+ * for additional information regarding copyright ownership.
+ * Apereo licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a
+ * copy of the License at the following location:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jasig.cas.client.tomcat.v90;
+
+import org.jasig.cas.client.tomcat.AssertionCasRealmDelegate;
+import org.jasig.cas.client.tomcat.CasRealm;
+
+/**
+ * Tomcat Realm that implements {@link CasRealm} for principal and
+ * role data backed by the CAS {@link org.jasig.cas.client.validation.Assertion}.
+ *
+ * Authentication always succeeds and simply returns the given principal.
+ *
+ * @author Marvin S. Addison
+ * @version $Revision$
+ *
+ */
+public class AssertionCasRealm extends AbstractCasRealm {
+
+ private final AssertionCasRealmDelegate delegate = new AssertionCasRealmDelegate();
+
+ /**
+ * @param name Name of the attribute in the principal that contains role data.
+ */
+ public void setRoleAttributeName(final String name) {
+ delegate.setRoleAttributeName(name);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected CasRealm getDelegate() {
+ return delegate;
+ }
+}
diff --git a/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/Cas10CasAuthenticator.java b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/Cas10CasAuthenticator.java
new file mode 100644
index 0000000..d7d68a8
--- /dev/null
+++ b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/Cas10CasAuthenticator.java
@@ -0,0 +1,60 @@
+/**
+ * Licensed to Apereo under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work
+ * for additional information regarding copyright ownership.
+ * Apereo licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a
+ * copy of the License at the following location:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jasig.cas.client.tomcat.v90;
+
+import org.apache.catalina.LifecycleException;
+import org.jasig.cas.client.validation.Cas10TicketValidator;
+import org.jasig.cas.client.validation.TicketValidator;
+
+/**
+ * Authenticator that handles CAS 1.0 protocol.
+ *
+ * @author Scott Battaglia
+ * @version $Revision$ $Date$
+ * @since 3.1.12
+ */
+public final class Cas10CasAuthenticator extends AbstractCasAuthenticator {
+
+ public static final String AUTH_METHOD = "CAS10";
+
+ private static final String NAME = Cas10CasAuthenticator.class.getName();
+
+ private Cas10TicketValidator ticketValidator;
+
+ @Override
+ protected TicketValidator getTicketValidator() {
+ return this.ticketValidator;
+ }
+
+ @Override
+ protected String getAuthenticationMethod() {
+ return AUTH_METHOD;
+ }
+
+ @Override
+ protected String getName() {
+ return NAME;
+ }
+
+ @Override
+ protected void startInternal() throws LifecycleException {
+ super.startInternal();
+ this.ticketValidator = new Cas10TicketValidator(getCasServerUrlPrefix());
+ }
+}
diff --git a/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/Cas20CasAuthenticator.java b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/Cas20CasAuthenticator.java
new file mode 100644
index 0000000..3321f1c
--- /dev/null
+++ b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/Cas20CasAuthenticator.java
@@ -0,0 +1,66 @@
+/**
+ * Licensed to Apereo under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work
+ * for additional information regarding copyright ownership.
+ * Apereo licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a
+ * copy of the License at the following location:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jasig.cas.client.tomcat.v90;
+
+import org.apache.catalina.LifecycleException;
+import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
+import org.jasig.cas.client.validation.TicketValidator;
+
+/**
+ * Authenticator that handles the CAS 2.0 protocol.
+ *
+ * @author Scott Battaglia
+ * @version $Revision$ $Date$
+ * @since 3.1.12
+ */
+public final class Cas20CasAuthenticator extends AbstractCasAuthenticator {
+
+ public static final String AUTH_METHOD = "CAS20";
+
+ private static final String NAME = Cas20CasAuthenticator.class.getName();
+
+ private Cas20ServiceTicketValidator ticketValidator;
+
+ @Override
+ protected TicketValidator getTicketValidator() {
+ return this.ticketValidator;
+ }
+
+ @Override
+ protected String getAuthenticationMethod() {
+ return AUTH_METHOD;
+ }
+
+ @Override
+ protected String getName() {
+ return NAME;
+ }
+
+ @Override
+ protected void startInternal() throws LifecycleException {
+ super.startInternal();
+ this.ticketValidator = new Cas20ServiceTicketValidator(getCasServerUrlPrefix());
+ if (getEncoding() != null) {
+ this.ticketValidator.setEncoding(getEncoding());
+ }
+ this.ticketValidator.setProxyCallbackUrl(getProxyCallbackUrl());
+ this.ticketValidator.setProxyGrantingTicketStorage(ProxyCallbackValve.getProxyGrantingTicketStorage());
+ this.ticketValidator.setRenew(isRenew());
+ }
+}
diff --git a/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/Cas20ProxyCasAuthenticator.java b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/Cas20ProxyCasAuthenticator.java
new file mode 100644
index 0000000..85a33c3
--- /dev/null
+++ b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/Cas20ProxyCasAuthenticator.java
@@ -0,0 +1,81 @@
+/**
+ * Licensed to Apereo under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work
+ * for additional information regarding copyright ownership.
+ * Apereo licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a
+ * copy of the License at the following location:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jasig.cas.client.tomcat.v90;
+
+import org.apache.catalina.LifecycleException;
+import org.jasig.cas.client.util.CommonUtils;
+import org.jasig.cas.client.validation.Cas20ProxyTicketValidator;
+import org.jasig.cas.client.validation.TicketValidator;
+
+/**
+ * Authenticator that handles the CAS 2.0 protocol with proxying support.
+ *
+ * @author Scott Battaglia
+ * @version $Revision$ $Date$
+ * @since 3.1.12
+ */
+public final class Cas20ProxyCasAuthenticator extends AbstractCasAuthenticator {
+
+ public static final String AUTH_METHOD = "CAS20-PROXY";
+
+ private static final String NAME = Cas20ProxyCasAuthenticator.class.getName();
+
+ private Cas20ProxyTicketValidator ticketValidator;
+
+ private boolean acceptAnyProxy;
+
+ private String allowedProxyChains;
+
+ public void setAcceptAnyProxy(final boolean acceptAnyProxy) {
+ this.acceptAnyProxy = acceptAnyProxy;
+ }
+
+ public void setAllowedProxyChains(final String allowedProxyChains) {
+ this.allowedProxyChains = allowedProxyChains;
+ }
+
+ @Override
+ protected TicketValidator getTicketValidator() {
+ return this.ticketValidator;
+ }
+
+ @Override
+ protected String getAuthenticationMethod() {
+ return AUTH_METHOD;
+ }
+
+ @Override
+ protected String getName() {
+ return NAME;
+ }
+
+ @Override
+ protected void startInternal() throws LifecycleException {
+ super.startInternal();
+ this.ticketValidator = new Cas20ProxyTicketValidator(getCasServerUrlPrefix());
+ this.ticketValidator.setRenew(isRenew());
+ this.ticketValidator.setProxyCallbackUrl(getProxyCallbackUrl());
+ this.ticketValidator.setProxyGrantingTicketStorage(ProxyCallbackValve.getProxyGrantingTicketStorage());
+ this.ticketValidator.setAcceptAnyProxy(this.acceptAnyProxy);
+ this.ticketValidator.setAllowedProxyChains(CommonUtils.createProxyList(this.allowedProxyChains));
+ if (getEncoding() != null) {
+ this.ticketValidator.setEncoding(getEncoding());
+ }
+ }
+}
diff --git a/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/PropertiesCasRealm.java b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/PropertiesCasRealm.java
new file mode 100644
index 0000000..973f4da
--- /dev/null
+++ b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/PropertiesCasRealm.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed to Apereo under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work
+ * for additional information regarding copyright ownership.
+ * Apereo licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a
+ * copy of the License at the following location:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jasig.cas.client.tomcat.v90;
+
+import org.apache.catalina.LifecycleException;
+import org.jasig.cas.client.tomcat.CasRealm;
+import org.jasig.cas.client.tomcat.PropertiesCasRealmDelegate;
+
+/**
+ * Tomcat Realm that implements {@link CasRealm} backed by properties file
+ * containing usernames/and roles of the following format:
+ *
+ * username1=role1,role2,role3 + * username2=role1 + * username3=role2,role3 + *+ * User authentication succeeds if the name of the given principal exists as + * a username in the properties file. + * + * @author Marvin S. Addison + * @version $Revision$ + * @since 3.1.12 + * + */ +public class PropertiesCasRealm extends AbstractCasRealm { + + private final PropertiesCasRealmDelegate delegate = new PropertiesCasRealmDelegate(); + + /** + * @param path Path to properties file container username/role data. + */ + public void setPropertiesFilePath(final String path) { + this.delegate.setPropertiesFilePath(path); + } + + /** {@inheritDoc} */ + @Override + protected void startInternal() throws LifecycleException { + super.startInternal(); + this.delegate.readProperties(); + } + + /** {@inheritDoc} */ + @Override + protected CasRealm getDelegate() { + return this.delegate; + } + +} diff --git a/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/ProxyCallbackValve.java b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/ProxyCallbackValve.java new file mode 100644 index 0000000..2f1ad99 --- /dev/null +++ b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/ProxyCallbackValve.java @@ -0,0 +1,94 @@ +/** + * Licensed to Apereo under one or more contributor license + * agreements. See the NOTICE file distributed with this work + * for additional information regarding copyright ownership. + * Apereo licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a + * copy of the License at the following location: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jasig.cas.client.tomcat.v90; + +import org.apache.catalina.LifecycleException; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.catalina.valves.ValveBase; +import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; +import org.jasig.cas.client.util.CommonUtils; +import org.jasig.cas.client.util.ReflectUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletException; + +import java.io.*; + +/** + * Handles watching a url for the proxy callback. + *
+ * Because its tough to share state between valves, we expose the storage mechanism via a static variable. + *
+ * This valve should be ordered before the authentication valves.
+ *
+ * @author Scott Battaglia
+ * @version $Revision$ $Date$
+ * @since 3.1.12
+ */
+public final class ProxyCallbackValve extends ValveBase {
+
+ private static ProxyGrantingTicketStorage PROXY_GRANTING_TICKET_STORAGE;
+
+ /** Logger instance */
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private String proxyGrantingTicketStorageClass;
+
+ private String proxyCallbackUrl;
+
+ public static ProxyGrantingTicketStorage getProxyGrantingTicketStorage() {
+ return PROXY_GRANTING_TICKET_STORAGE;
+ }
+
+ public void setProxyGrantingTicketStorageClass(final String proxyGrantingTicketStorageClass) {
+ this.proxyGrantingTicketStorageClass = proxyGrantingTicketStorageClass;
+ }
+
+ public void setProxyCallbackUrl(final String proxyCallbackUrl) {
+ this.proxyCallbackUrl = proxyCallbackUrl;
+ }
+
+ @Override
+ protected void startInternal() throws LifecycleException {
+ super.startInternal();
+
+ try {
+ CommonUtils.assertNotNull(this.proxyCallbackUrl, "the proxy callback url cannot be null");
+ CommonUtils.assertTrue(this.proxyCallbackUrl.startsWith("/"), "proxy callback url must start with \"/\"");
+
+ PROXY_GRANTING_TICKET_STORAGE = ReflectUtils.newInstance(proxyGrantingTicketStorageClass);
+ } catch (final Exception e) {
+ throw new LifecycleException(e);
+ }
+ logger.info("Startup completed.");
+ }
+
+ @Override
+ public void invoke(final Request request, final Response response) throws IOException, ServletException {
+ if (this.proxyCallbackUrl.equals(request.getRequestURI())) {
+ logger.debug("Processing proxy callback request.");
+ CommonUtils.readAndRespondToProxyReceptorRequest(request, response, PROXY_GRANTING_TICKET_STORAGE);
+ return;
+ }
+
+ getNext().invoke(request, response);
+ }
+}
diff --git a/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/RegexUriLogoutValve.java b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/RegexUriLogoutValve.java
new file mode 100644
index 0000000..0447bbe
--- /dev/null
+++ b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/RegexUriLogoutValve.java
@@ -0,0 +1,57 @@
+/**
+ * Licensed to Apereo under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work
+ * for additional information regarding copyright ownership.
+ * Apereo licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a
+ * copy of the License at the following location:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jasig.cas.client.tomcat.v90;
+
+import org.apache.catalina.LifecycleException;
+import org.jasig.cas.client.tomcat.LogoutHandler;
+import org.jasig.cas.client.tomcat.RegexUriLogoutHandler;
+
+/**
+ * Performs CAS logout when the request URI matches a regular expression.
+ *
+ * @author Scott Battaglia
+ * @author Marvin S. Addison
+ * @version $Revision$ $Date$
+ * @since 3.1.12
+ */
+public final class RegexUriLogoutValve extends AbstractLogoutValve {
+
+ private final RegexUriLogoutHandler logoutHandler = new RegexUriLogoutHandler();
+
+ public void setRedirectUrl(final String redirectUrl) {
+ this.logoutHandler.setRedirectUrl(redirectUrl);
+ }
+
+ public void setLogoutUriRegex(final String regex) {
+ this.logoutHandler.setLogoutUriRegex(regex);
+ }
+
+ @Override
+ protected void startInternal() throws LifecycleException {
+ super.startInternal();
+ this.logoutHandler.init();
+ logger.info("Startup completed.");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected LogoutHandler getLogoutHandler() {
+ return this.logoutHandler;
+ }
+}
diff --git a/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/Saml11Authenticator.java b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/Saml11Authenticator.java
new file mode 100644
index 0000000..0004e5c
--- /dev/null
+++ b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/Saml11Authenticator.java
@@ -0,0 +1,90 @@
+/**
+ * Licensed to Apereo under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work
+ * for additional information regarding copyright ownership.
+ * Apereo licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a
+ * copy of the License at the following location:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jasig.cas.client.tomcat.v90;
+
+import org.apache.catalina.LifecycleException;
+import org.jasig.cas.client.validation.Saml11TicketValidator;
+import org.jasig.cas.client.validation.TicketValidator;
+
+/**
+ * CAS authenticator that uses the SAML 1.1 protocol.
+ *
+ * @author Marvin S. Addison
+ * @version $Revision$
+ * @since 3.1.12
+ *
+ */
+public final class Saml11Authenticator extends AbstractAuthenticator {
+
+ public static final String AUTH_METHOD = "SAML11";
+
+ private static final String NAME = Saml11Authenticator.class.getName();
+
+ private Saml11TicketValidator ticketValidator;
+
+ /** SAML protocol clock drift tolerance in ms */
+ private int tolerance = -1;
+
+ /**
+ * @param ms SAML clock drift tolerance in milliseconds.
+ */
+ public void setTolerance(final int ms) {
+ this.tolerance = ms;
+ }
+
+ @Override
+ protected void startInternal() throws LifecycleException {
+ super.startInternal();
+ this.ticketValidator = new Saml11TicketValidator(getCasServerUrlPrefix());
+ if (this.tolerance > -1) {
+ this.ticketValidator.setTolerance(this.tolerance);
+ }
+ if (getEncoding() != null) {
+ this.ticketValidator.setEncoding(getEncoding());
+ }
+ this.ticketValidator.setRenew(isRenew());
+ }
+
+ @Override
+ protected TicketValidator getTicketValidator() {
+ return this.ticketValidator;
+ }
+
+ @Override
+ protected String getAuthenticationMethod() {
+ return AUTH_METHOD;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String getArtifactParameterName() {
+ return "SAMLart";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected String getServiceParameterName() {
+ return "TARGET";
+ }
+
+ @Override
+ protected String getName() {
+ return NAME;
+ }
+}
diff --git a/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/SingleSignOutValve.java b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/SingleSignOutValve.java
new file mode 100644
index 0000000..04326f7
--- /dev/null
+++ b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/SingleSignOutValve.java
@@ -0,0 +1,98 @@
+/**
+ * Licensed to Apereo under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work
+ * for additional information regarding copyright ownership.
+ * Apereo licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a
+ * copy of the License at the following location:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jasig.cas.client.tomcat.v90;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Session;
+import org.apache.catalina.SessionEvent;
+import org.apache.catalina.SessionListener;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.valves.ValveBase;
+import org.jasig.cas.client.session.SessionMappingStorage;
+import org.jasig.cas.client.session.SingleSignOutHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletException;
+
+import java.io.*;
+
+/**
+ * Handles logout request messages sent from the CAS server by ending the current
+ * HTTP session.
+ *
+ * @author Marvin S. Addison
+ * @version $Revision$ $Date$
+ * @since 3.1.12
+ *
+ */
+public class SingleSignOutValve extends ValveBase implements SessionListener {
+
+ /** Logger instance */
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final SingleSignOutHandler handler = new SingleSignOutHandler();
+
+ public void setArtifactParameterName(final String name) {
+ this.handler.setArtifactParameterName(name);
+ }
+
+ public void setLogoutParameterName(final String name) {
+ this.handler.setLogoutParameterName(name);
+ }
+
+ public void setRelayStateParameterName(final String name) {
+ this.handler.setRelayStateParameterName(name);
+ }
+
+ public void setLogoutCallbackPath(final String logoutCallbackPath) {
+ this.handler.setLogoutCallbackPath(logoutCallbackPath);
+ }
+
+ public void setSessionMappingStorage(final SessionMappingStorage storage) {
+ this.handler.setSessionMappingStorage(storage);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void invoke(final Request request, final Response response) throws IOException, ServletException {
+ if (this.handler.process(request, response)) {
+ getNext().invoke(request, response);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void sessionEvent(final SessionEvent event) {
+ if (Session.SESSION_DESTROYED_EVENT.equals(event.getType())) {
+ logger.debug("Cleaning up SessionMappingStorage on destroySession event");
+ this.handler.getSessionMappingStorage().removeBySessionById(event.getSession().getId());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void startInternal() throws LifecycleException {
+ super.startInternal();
+ logger.info("Starting...");
+ this.handler.init();
+ logger.info("Startup completed.");
+ }
+}
diff --git a/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/StaticUriLogoutValve.java b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/StaticUriLogoutValve.java
new file mode 100644
index 0000000..5c28fe1
--- /dev/null
+++ b/cas-client-integration-tomcat-v90/src/main/java/org/jasig/cas/client/tomcat/v90/StaticUriLogoutValve.java
@@ -0,0 +1,57 @@
+/**
+ * Licensed to Apereo under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work
+ * for additional information regarding copyright ownership.
+ * Apereo licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a
+ * copy of the License at the following location:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jasig.cas.client.tomcat.v90;
+
+import org.apache.catalina.LifecycleException;
+import org.jasig.cas.client.tomcat.LogoutHandler;
+import org.jasig.cas.client.tomcat.StaticUriLogoutHandler;
+
+/**
+ * Monitors a specific request URI for logout requests.
+ *
+ * @author Scott Battaglia
+ * @author Marvin S. Addison
+ * @version $Revision$ $Date$
+ * @since 3.1.12
+ */
+public final class StaticUriLogoutValve extends AbstractLogoutValve {
+
+ private final StaticUriLogoutHandler logoutHandler = new StaticUriLogoutHandler();
+
+ public void setRedirectUrl(final String redirectUrl) {
+ this.logoutHandler.setRedirectUrl(redirectUrl);
+ }
+
+ public void setLogoutUri(final String logoutUri) {
+ this.logoutHandler.setLogoutUri(logoutUri);
+ }
+
+ @Override
+ protected void startInternal() throws LifecycleException {
+ super.startInternal();
+ this.logoutHandler.init();
+ logger.info("Startup completed.");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected LogoutHandler getLogoutHandler() {
+ return this.logoutHandler;
+ }
+}
diff --git a/pom.xml b/pom.xml
index 9ec4330..b97fe99 100644
--- a/pom.xml
+++ b/pom.xml
@@ -304,6 +304,7 @@