diff --git a/.gitignore b/.gitignore index c9f4a19fa..7c1ccf4cd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ !/.project .project .settings +bin target *.ipr *.iml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..e5c3fda62 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,38 @@ +# +# 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 + +language: java + +script: "mvn install --settings travis/settings.xml" + +jdk: + - oraclejdk7 + +env: + global: + - secure: "JM/FMiec3GYShrMlJQSW2QG208+V0GCAj2bsP5eF8q4yzgp6o4rT+r57KDIDD6MapRN+G1Pnl3WPcS0aQYnwOhPg4tA2De1bFUPaJltP47eHFfblpjZeHMxcauCQ6BwFFr8yuC0ORsYCW3TOK00Mxq4CRlTlg5iclzHyS/pnkLI=" + - secure: "eXfgf3v8Kw/L22DO39Y61os13bfNpop8Xpkmz+HZ1djQWavOkRn58gSg8EVjBYRPOrTuEbhEWb+s3qpx8j3qRdi6roMs9MTr5gEPTAyjTtJ/Zv1qhJ6OlEl2w5c2fRMsk5cB//mtxtZ+qMaz6sdZI2csbQ2xlhjz4AbGQL5i1lY=" + +after_success: +- chmod -R 777 ./travis/deploy-to-sonatype.sh +- ./travis/deploy-to-sonatype.sh diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..1011bab71 --- /dev/null +++ b/NOTICE @@ -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. + +This project includes: + AOP alliance under Public Domain + Apache Log4j under The Apache Software License, Version 2.0 + Apache XML Security 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 Atlassian End User License + atlassian-osuser under Atlassian End User License + catalina under Apache License, Version 2.0 + Commons Codec under The Apache Software License, Version 2.0 + Confluence Core under Atlassian End User License + Ehcache Core under The Apache Software License, Version 2.0 + Google Collections Library under The Apache Software License, Version 2.0 + Jasig CAS Client for Java under Apache License Version 2.0 + Jasig CAS Client for Java - Atlassian Integration under Apache License Version 2.0 + 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 + Jasig CAS Client for Java - Distributed Proxy Storage Support: + Memcached under Apache License Version 2.0 + Jasig CAS Client for Java - Distributed Proxy Storage Support: EhCache under Apache License Version 2.0 + Jasig CAS Client for Java - JBoss Integration under Apache License Version 2.0 + Jasig CAS Client for Java - SAML Protocol Support under Apache License Version 2.0 + Jasig CAS Client for Java - Tomcat 6.x Integration 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 + 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 + JCL 1.1.1 implemented over SLF4J under MIT License + Joda-Time under Apache 2 + JUnit under Common Public License Version 1.0 + SLF4J API Module under MIT License + SLF4J Simple Binding under MIT License + spring-aop under The Apache Software License, Version 2.0 + spring-asm under The Apache Software License, Version 2.0 + spring-beans under The Apache Software License, Version 2.0 + spring-context under The Apache Software License, Version 2.0 + spring-core under The Apache Software License, Version 2.0 + spring-expression under The Apache Software License, Version 2.0 + spring-test under The Apache Software License, Version 2.0 + Spymemcached under The Apache Software License, Version 2.0 + tomcat-catalina under Apache License, Version 2.0 + diff --git a/README.md b/README.md new file mode 100644 index 000000000..91d9fbb7d --- /dev/null +++ b/README.md @@ -0,0 +1,1231 @@ +# Java Apereo CAS Client [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.jasig.cas.client/cas-client-core/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/org.jasig.cas/cas-server) + + +## Intro +This is the official home of the Java Apereo CAS client. The client consists of a collection of Servlet filters that are suitable for most Java-based web applications. It also serves as an API platform to interact with the CAS server programmatically to make authentication requests, validate tickets and consume principal attributes. + +All client artifacts are published to Maven central. Depending on functionality, applications will need include one or more of the listed dependencies in their configuration. + + +## Build [![Build Status](https://travis-ci.org/Jasig/java-cas-client.png?branch=master)](https://travis-ci.org/Jasig/java-cas-client) + +```bash +git clone git@github.com:Jasig/java-cas-client.git +cd java-cas-client +mvn clean package +``` + +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. + + +## Components + +- Core functionality, which includes CAS authentication/validation filters. + +```xml + + org.jasig.cas.client + cas-client-core + ${java.cas.client.version} + +``` + +- Support for SAML functionality is provided by this dependency: + +```xml + + org.jasig.cas + cas-client-support-saml + ${java.cas.client.version} + +``` + +- Distributed proxy ticket caching with Ehcache is provided by this dependency: + +```xml + + org.jasig.cas + cas-client-support-distributed-ehcache + ${java.cas.client.version} + +``` + +- Distributed proxy ticket caching with Memcached is provided by this dependency: + +```xml + + org.jasig.cas + cas-client-support-distributed-memcached + ${java.cas.client.version} + +``` + +- Atlassian integration is provided by this dependency: + +```xml + + org.jasig.cas + cas-client-integration-atlassian + ${java.cas.client.version} + +``` + +- JBoss integration is provided by this dependency: + +```xml + + org.jasig.cas + cas-client-integration-jboss + ${java.cas.client.version} + +``` + +- Tomcat 6 integration is provided by this dependency: + +```xml + + org.jasig.cas + cas-client-integration-tomcat-v6 + ${java.cas.client.version} + +``` + +- Tomcat 7 is provided by this dependency: + +```xml + + org.jasig.cas + cas-client-integration-tomcat-v7 + ${java.cas.client.version} + +``` + + +## Configuration + +### Strategies +The client provides multiple strategies for the deployer to provide client settings. The following strategies are supported: + +- JNDI (`JNDI`) +- Properties File (`PROPERTY_FILE`). The configuration is provided via an external properties file. The path may be specified in the web context as such: + +```xml + + configFileLocation + /etc/cas/file.properties + +``` +If no location is specified, by default `/etc/java-cas-client.properties` will be used. + +- System Properties (`SYSTEM_PROPERTIES`) +- Web Context (`WEB_XML`) +- Default (`DEFAULT`) + +In order to instruct the client to pick a strategy, strategy name must be specified in the web application's context: + +```xml + + configurationStrategy + DEFAULT + +``` + +If no `configurationStrategy` is defined, `DEFAULT` is used which is a combination of `WEB_XML` and `JNDI`. + + +### Client Configuration Using `web.xml` + +The client can be configured in `web.xml` via a series of `context-param`s and filter `init-param`s. Each filter for the client has a required (and optional) set of properties. The filters are designed to look for these properties in the following way: + +- Check the filter's local `init-param`s for a parameter matching the required property name. +- Check the `context-param`s for a parameter matching the required property name. +- If two properties are found with the same name in the `init-param`s and the `context-param`s, the `init-param` takes precedence. + +**Note**: If you're using the `serverName` property, you should note well that the fragment-URI (the stuff after the #) is not sent to the server by all browsers, thus the CAS client can't capture it as part of the URL. + +An example application that is protected by the client is [available here](https://github.com/UniconLabs/cas-sample-java-webapp). + + +#### org.jasig.cas.client.authentication.AuthenticationFilter +The `AuthenticationFilter` is what detects whether a user needs to be authenticated or not. If a user needs to be authenticated, it will redirect the user to the CAS server. + +```xml + + CAS Authentication Filter + org.jasig.cas.client.authentication.AuthenticationFilter + + casServerLoginUrl + https://battags.ad.ess.rutgers.edu:8443/cas/login + + + serverName + http://www.acme-client.com + + + + CAS Authentication Filter + /* + +``` + +| Property | Description | Required +|----------|-------|----------- +| `casServerLoginUrl` | Defines the location of the CAS server login URL, i.e. `https://localhost:8443/cas/login` | Yes +| `serverName` | The name of the server this application is hosted on. Service URL will be dynamically constructed using this, i.e. https://localhost:8443 (you must include the protocol, but port is optional if it's a standard port). | Yes +| `service` | The service URL to send to the CAS server, i.e. `https://localhost:8443/yourwebapp/index.html` | No +| `renew` | specifies whether `renew=true` should be sent to the CAS server. Valid values are either `true/false` (or no value at all). Note that `renew` cannot be specified as local `init-param` setting. | No +| `gateway ` | specifies whether `gateway=true` should be sent to the CAS server. Valid values are either `true/false` (or no value at all) | No +| `artifactParameterName ` | specifies the name of the request parameter on where to find the artifact (i.e. `ticket`). | No +| `serviceParameterName ` | specifies the name of the request parameter on where to find the service (i.e. `service`) | No +| `encodeServiceUrl ` | Whether the client should auto encode the service url. Defaults to `true` | No +| `ignorePattern` | Defines the url pattern to ignore, when intercepting authentication requests. | No +| `ignoreUrlPatternType` | Defines the type of the pattern specified. Defaults to `REGEX`. Other types are `CONTAINS`, `EXACT`. | No +| `gatewayStorageClass` | The storage class used to record gateway requests | No +| `authenticationRedirectStrategyClass` | The class name of the component to decide how to handle authn redirects to CAS | No + + +#### org.jasig.cas.client.authentication.Saml11AuthenticationFilter +The SAML 1.1 `AuthenticationFilter` is what detects whether a user needs to be authenticated or not. If a user needs to be authenticated, it will redirect the user to the CAS server. + +```xml + + CAS Authentication Filter + org.jasig.cas.client.authentication.Saml11AuthenticationFilter + + casServerLoginUrl + https://somewhere.cas.edu:8443/cas/login + + + serverName + http://www.the-client.com + + + + CAS Authentication Filter + /* + +``` + +| Property | Description | Required +|----------|-------|----------- +| `casServerLoginUrl` | Defines the location of the CAS server login URL, i.e. `https://localhost:8443/cas/login` | Yes +| `serverName` | The name of the server this application is hosted on. Service URL will be dynamically constructed using this, i.e. https://localhost:8443 (you must include the protocol, but port is optional if it's a standard port). | Yes +| `service` | The service URL to send to the CAS server, i.e. `https://localhost:8443/yourwebapp/index.html` | No +| `renew` | specifies whether `renew=true` should be sent to the CAS server. Valid values are either `true/false` (or no value at all). Note that `renew` cannot be specified as local `init-param` setting. | No +| `gateway ` | specifies whether `gateway=true` should be sent to the CAS server. Valid values are either `true/false` (or no value at all) | No +| `artifactParameterName ` | specifies the name of the request parameter on where to find the artifact (i.e. `SAMLart`). | No +| `serviceParameterName ` | specifies the name of the request parameter on where to find the service (i.e. `TARGET`) | No +| `encodeServiceUrl ` | Whether the client should auto encode the service url. Defaults to `true` | No + + +####org.jasig.cas.client.validation.Cas10TicketValidationFilter +Validates tickets using the CAS 1.0 Protocol. + +```xml + + CAS Validation Filter + org.jasig.cas.client.validation.Cas10TicketValidationFilter + + casServerUrlPrefix + https://somewhere.cas.edu:8443/cas + + + + CAS Validation Filter + /* + +``` + +| Property | Description | Required +|----------|-------|----------- +| `casServerUrlPrefix ` | The start of the CAS server URL, i.e. `https://localhost:8443/cas` | Yes +| `serverName` | The name of the server this application is hosted on. Service URL will be dynamically constructed using this, i.e. `https://localhost:8443` (you must include the protocol, but port is optional if it's a standard port). | Yes +| `renew` | Specifies whether `renew=true` should be sent to the CAS server. Valid values are either `true/false` (or no value at all). Note that `renew` cannot be specified as local `init-param` setting. | No +| `redirectAfterValidation ` | Whether to redirect to the same URL after ticket validation, but without the ticket in the parameter. Defaults to `true`. | No +| `useSession ` | Whether to store the Assertion in session or not. If sessions are not used, tickets will be required for each request. Defaults to `true`. | No +| `exceptionOnValidationFailure ` | Whether to throw an exception or not on ticket validation failure. Defaults to `true`. | No +| `sslConfigFile` | A reference to a properties file that includes SSL settings for client-side SSL config, used during back-channel calls. The configuration includes keys for `protocol` which defaults to `SSL`, `keyStoreType`, `keyStorePath`, `keyStorePass`, `keyManagerType` which defaults to `SunX509` and `certificatePassword`. | No. +| `encoding` | Specifies the encoding charset the client should use | No +| `hostnameVerifier` | Hostname verifier class name, used when making back-channel calls | No + + +#### org.jasig.cas.client.validation.Saml11TicketValidationFilter +Validates tickets using the SAML 1.1 protocol. + +```xml + + CAS Validation Filter + org.jasig.cas.client.validation.Saml11TicketValidationFilter + + casServerUrlPrefix + https://battags.ad.ess.rutgers.edu:8443/cas + + + serverName + http://www.acme-client.com + + + + CAS Validation Filter + /* + +``` + +| Property | Description | Required +|----------|-------|----------- +| `casServerUrlPrefix ` | The start of the CAS server URL, i.e. `https://localhost:8443/cas` | Yes +| `serverName` | The name of the server this application is hosted on. Service URL will be dynamically constructed using this, i.e. `https://localhost:8443` (you must include the protocol, but port is optional if it's a standard port). | Yes +| `renew` | Specifies whether `renew=true` should be sent to the CAS server. Valid values are either `true/false` (or no value at all). Note that `renew` cannot be specified as local `init-param` setting. | No +| `redirectAfterValidation ` | Whether to redirect to the same URL after ticket validation, but without the ticket in the parameter. Defaults to `true`. | No +| `useSession ` | Whether to store the Assertion in session or not. If sessions are not used, tickets will be required for each request. Defaults to `true`. | No +| `exceptionOnValidationFailure ` | whether to throw an exception or not on ticket validation failure. Defaults to `true` | No +| `tolerance ` | The tolerance for drifting clocks when validating SAML tickets. Note that 10 seconds should be more than enough for most environments that have NTP time synchronization. Defaults to `1000 msec` | No +| `sslConfigFile` | A reference to a properties file that includes SSL settings for client-side SSL config, used during back-channel calls. The configuration includes keys for `protocol` which defaults to `SSL`, `keyStoreType`, `keyStorePath`, `keyStorePass`, `keyManagerType` which defaults to `SunX509` and `certificatePassword`. | No. +| `encoding` | Specifies the encoding charset the client should use | No +| `hostnameVerifier` | Hostname verifier class name, used when making back-channel calls | No + + +#### org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter +Validates the tickets using the CAS 2.0 protocol. If you provide either the `acceptAnyProxy` or the `allowedProxyChains` parameters, a `Cas20ProxyTicketValidator` will be constructed. Otherwise a general `Cas20ServiceTicketValidator` will be constructed that does not accept proxy tickets. + +**Note**: If you are using proxy validation, you should place the `filter-mapping` of the validation filter before the authentication filter. + +```xml + + CAS Validation Filter + org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter + + casServerUrlPrefix + https://battags.ad.ess.rutgers.edu:8443/cas + + + serverName + http://www.acme-client.com + + + + CAS Validation Filter + /* + +``` + +| Property | Description | Required +|----------|-------|----------- +| `casServerUrlPrefix ` | The start of the CAS server URL, i.e. `https://localhost:8443/cas` | Yes +| `serverName` | The name of the server this application is hosted on. Service URL will be dynamically constructed using this, i.e. `https://localhost:8443` (you must include the protocol, but port is optional if it's a standard port). | Yes +| `renew` | Specifies whether `renew=true` should be sent to the CAS server. Valid values are either `true/false` (or no value at all). Note that `renew` cannot be specified as local `init-param` setting. | No +| `redirectAfterValidation ` | Whether to redirect to the same URL after ticket validation, but without the ticket in the parameter. Defaults to `true`. | No +| `useSession ` | Whether to store the Assertion in session or not. If sessions are not used, tickets will be required for each request. Defaults to `true`. | No +| `exceptionOnValidationFailure ` | whether to throw an exception or not on ticket validation failure. Defaults to `true` | No +| `proxyReceptorUrl ` | The URL to watch for `PGTIOU/PGT` responses from the CAS server. Should be defined from the root of the context. For example, if your application is deployed in `/cas-client-app` and you want the proxy receptor URL to be `/cas-client-app/my/receptor` you need to configure proxyReceptorUrl to be `/my/receptor`. | No +| `acceptAnyProxy ` | Specifies whether any proxy is OK. Defaults to `false`. | No +| `allowedProxyChains ` | Specifies the proxy chain. Each acceptable proxy chain should include a space-separated list of URLs (for exact match) or regular expressions of URLs (starting by the `^` character). Each acceptable proxy chain should appear on its own line. | No +| `proxyCallbackUrl` | The callback URL to provide the CAS server to accept Proxy Granting Tickets. | No +| `proxyGrantingTicketStorageClass ` | Specify an implementation of the ProxyGrantingTicketStorage class that has a no-arg constructor. | No +| `sslConfigFile` | A reference to a properties file that includes SSL settings for client-side SSL config, used during back-channel calls. The configuration includes keys for `protocol` which defaults to `SSL`, `keyStoreType`, `keyStorePath`, `keyStorePass`, `keyManagerType` which defaults to `SunX509` and `certificatePassword`. | No. +| `encoding` | Specifies the encoding charset the client should use | No +| `secretKey` | The secret key used by the `proxyGrantingTicketStorageClass` if it supports encryption. | No +| `cipherAlgorithm` | The algorithm used by the `proxyGrantingTicketStorageClass` if it supports encryption. Defaults to `DESede` | No +| `millisBetweenCleanUps` | Startup delay for the cleanup task to remove expired tickets from the storage. Defaults to `60000 msec` | No +| `ticketValidatorClass` | Ticket validator class to use/create | No +| `hostnameVerifier` | Hostname verifier class name, used when making back-channel calls | No + +#### org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter +Validates the tickets using the CAS 3.0 protocol. If you provide either the `acceptAnyProxy` or the `allowedProxyChains` parameters, a `Cas30ProxyTicketValidator` will be constructed. Otherwise a general `Cas30ServiceTicketValidator` will be constructed that does not accept proxy tickets. Supports all configurations that are available for `Cas20ProxyReceivingTicketValidationFilter`. + +##### Proxy Authentication vs. Distributed Caching +The client has support for clustering and distributing the TGT state among application nodes that are behind a load balancer. In order to do so, the parameter needs to be defined as such for the filter. + +###### Ehcache + +Configure the client: + +```xml + + proxyGrantingTicketStorageClass + org.jasig.cas.client.proxy.EhcacheBackedProxyGrantingTicketStorageImpl + +``` +The setting provides an implementation for proxy storage using EhCache to take advantage of its replication features so that the PGT is successfully replicated and shared among nodes, regardless which node is selected as the result of the load balancer rerouting. + +Configuration of this parameter is not enough. The EhCache configuration needs to enable the replication mechanism through once of its suggested ways. A sample of that configuration based on RMI replication can be found here. Please note that while the sample is done for a distributed ticket registry implementation, the basic idea and configuration should easily be transferable. + +When loading from the `web.xml`, the Jasig CAS Client relies on a series of default values, one of which being that the cache must be configured in the default location (i.e. `classpath:ehcache.xml`). + +```xml + + + + + + + +``` + +###### Memcached +A similar implementation based on Memcached is also available. + +Configure the client: + +```xml + + proxyGrantingTicketStorageClass + org.jasig.cas.client.proxy. MemcachedBackedProxyGrantingTicketStorageImpl + +``` + +When loading from the `web.xml`, the Client relies on a series of default values, one of which being that the list of memcached servers must be defined in `/cas/casclient_memcached_hosts.txt` on the classpath). The file is a simple list of `:` on separate lines. **BE SURE NOT TO HAVE EXTRA LINE BREAKS**. + + +#### org.jasig.cas.client.util.HttpServletRequestWrapperFilter +Wraps an `HttpServletRequest` so that the `getRemoteUser` and `getPrincipal` return the CAS related entries. + +```xml + + CAS HttpServletRequest Wrapper Filter + org.jasig.cas.client.util.HttpServletRequestWrapperFilter + + + CAS HttpServletRequest Wrapper Filter + /* + +``` + +| Property | Description | Required +|----------|-------|----------- +| `roleAttribute` | Used to determine the principal role. | No +| `ignoreCase` | Whether role checking should ignore case. Defaults to `false` | No + + +#### org.jasig.cas.client.util.AssertionThreadLocalFilter +Places the `Assertion` in a `ThreadLocal` for portions of the application that need access to it. This is useful when the Web application that this filter "fronts" needs to get the Principal name, but it has no access to the `HttpServletRequest`, hence making `getRemoteUser()` call impossible. + +```xml + + CAS Assertion Thread Local Filter + org.jasig.cas.client.util.AssertionThreadLocalFilter + + + CAS Assertion Thread Local Filter + /* + +``` + + +### Client Configuration Using Spring + +Configuration via Spring IoC will depend heavily on `DelegatingFilterProxy` class. For each filter that will be configured for CAS via Spring, a corresponding `DelegatingFilterProxy` is needed in the web.xml. + +As the `SingleSignOutFilter`, `HttpServletRequestWrapperFilter` and `AssertionThreadLocalFilter` have no configuration options, we recommend you just configure them in the `web.xml` + +```xml + + CAS Authentication Filter + org.springframework.web.filter.DelegatingFilterProxy + + targetBeanName + authenticationFilter + + + + CAS Authentication Filter + /* + +``` + + +#### Bean Configuration + +##### AuthenticationFilter +```xml + +``` + +##### Cas10TicketValidationFilter +```xml + + + + + + + +``` + +##### Saml11TicketValidationFilter +```xml + + + + + + + +``` + +##### Cas20ProxyReceivingTicketValidationFilter +Configuration to validate tickets: +```xml + + + + + + + +``` + +Configuration to accept a Proxy Granting Ticket: +```xml + + + + + + + +``` + +Configuration to accept any Proxy Ticket (and Proxy Granting Tickets): + +```xml + + + + + + + +``` + +Configuration to accept Proxy Ticket from a chain (and Proxy Granting Tickets): + +```xml + + + + + + + http://proxy1 http://proxy2 + + + + + +``` + +The specific filters can be configured in the following ways. Please see the JavaDocs included in the distribution for specific required and optional properties: + + + +### Client Configuration Using JNDI + +Configuring the CAS client via JNDI is essentially the same as configuring the client via the `web.xml`, except the properties will reside in JNDI and not in the `web.xml`. +All properties that are placed in JNDI should be placed under `java:comp/env/cas` + +We use the following conventions: +1. JNDI will first look in `java:comp/env/cas/{SHORT FILTER NAME}/{PROPERTY NAME}` (i.e. `java:comp/env/cas/AuthenticationFilter/serverName`) +2. JNDI will as a last resort look in `java:comp/env/cas/{PROPERTY NAME}` (i.e. `java:comp/env/cas/serverName`) + + +#### Example +This is an update to the `META-INF/context.xml` that is included in Tomcat's Manager application: + +```xml + + + + + + + + + +``` + + +### Configuring Single Sign Out +The Single Sign Out support in CAS consists of configuring one `SingleSignOutFilter` and one `ContextListener`. Please note that if you have configured the CAS Client for Java as Web filters, this filter must come before the other filters as described. + +The `SingleSignOutFilter` can affect character encoding. This becomes most obvious when used in conjunction with applications such as Atlassian Confluence. Its recommended you explicitly configure either the [VT Character Encoding Filter](http://code.google.com/p/vt-middleware/wiki/vtservletfilters#CharacterEncodingFilter) or the [Spring Character Encoding Filter](http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/filter/CharacterEncodingFilter.html) with explicit encodings. + +#### Configuration + +| Property | Description | Required +|----------|-------|----------- +| `artifactParameterName` | The ticket artifact parameter name. Defaults to `ticket`| No +| `logoutParameterName` | Defaults to `logoutRequest` | No +| `frontLogoutParameterName` | Defaults to `SAMLRequest` | No +| `relayStateParameterName` | Defaults to `RelayState` | No +| `eagerlyCreateSessions` | Defaults to `true` | No +| `artifactParameterOverPost` | Defaults to `false` | No +| `casServerUrlPrefix` | URL to root of CAS Web application context. | Yes + + +#### CAS Protocol + +```xml + + CAS Single Sign Out Filter + org.jasig.cas.client.session.SingleSignOutFilter + +... + + CAS Single Sign Out Filter + /* + +... + + org.jasig.cas.client.session.SingleSignOutHttpSessionListener + +``` + + +#### SAML Protocol + +```xml + + CAS Single Sign Out Filter + org.jasig.cas.client.session.SingleSignOutFilter + + artifactParameterName + SAMLart + + +... + + CAS Single Sign Out Filter + /* + +... + + org.jasig.cas.client.session.SingleSignOutHttpSessionListener + +``` + + + + +#### Recommend Logout Procedure +The client has no code to help you handle log out. The client merely places objects in session. Therefore, we recommend you do a `session.invalidate()` call when you log a user out. However, that's entirely your application's responsibility. We recommend that text similar to the following appear when the application's session is ended. + +```html +You have been logged out of [APPLICATION NAME GOES HERE]. +To log out of all applications, click here. (provide link to CAS server's logout) +``` + + +## JAAS +The client supports the Java Authentication and Authorization Service (JAAS) framework, which provides authnz facilities to CAS-enabled JEE applications. + +A general JAAS authentication module, `CasLoginModule`, is available with the specific purpose of providing authentication and authorization services to CAS-enabled JEE applications. The design of the module is simple: given a service URL and a service ticket in a `NameCallback` and `PasswordCallback`, respectively, the module contacts the CAS server and attempts to validate the ticket. In keeping with CAS integration for Java applications, a JEE container-specific servlet filter is needed to protect JEE Web applications. The JAAS support should be extensible to any JEE container. + + +### Configure CasLoginModule +It is expected that for JEE applications both authentication and authorization services will be required for CAS integration. The following JAAS module configuration file excerpt demonstrates how to leverage SAML 1.1 attribute release in CAS to provide authorization data in addition to authentication: + +``` +cas { + org.jasig.cas.client.jaas.CasLoginModule required + ticketValidatorClass="org.jasig.cas.client.validation.Saml11TicketValidator" + casServerUrlPrefix="https://cas.example.com/cas" + tolerance="20000" + service="https://webapp.example.com/webapp" + defaultRoles="admin,operator" + roleAttributeNames="memberOf,eduPersonAffiliation" + principalGroupName="CallerPrincipal" + roleGroupName="Roles" + cacheAssertions="true" + cacheTimeout="480"; +} +``` + + +| Property | Description | Required +|----------|-------|-----------| +| `ticketValidatorClass ` | Fully-qualified class name of CAS ticket validator class. | Yes +| `casServerUrlPrefix` | URL to root of CAS Web application context. | Yes +| `service` | CAS service parameter that may be overridden by callback handler. **Note**: service must be specified by at least one component such that it is available at service ticket validation time. | No +| `defaultRoles` | Comma-delimited list of static roles applied to all authenticated principals. | No +| `roleAttributeNames` | Comma-delimited list of attribute names that describe role data delivered to CAS in the service-ticket validation response that should be applied to the current authenticated principal. | No +| `principalGroupName` | The name of a group principal containing the primary principal name of the current JAAS subject. The default value is `CallerPrincipal`. | No +| `roleGroupName` | The name of a group principal containing all role data. The default value is `Roles`. | No +| `cacheAssertions` | Flag to enable assertion caching. This may be required for JAAS providers that attempt to periodically reauthenticate to renew principal. Since CAS tickets are one-time-use, a cached assertion must be provided on reauthentication. | No +| `cacheTimeout` | Assertion cache timeout in minutes. | No +| `tolerance` | The tolerance for drifting clocks when validating SAML tickets. | No + +### Programmatic JAAS login using the Servlet 3 +A `org.jasig.cas.client.jaas.Servlet3AuthenticationFilter` servlet filter that performs a programmatic JAAS login using the Servlet 3.0 `HttpServletRequest#login()` facility. This component should be compatible with any servlet container that supports the Servlet 3.0/JEE6 specification. + +The filter executes when it receives a CAS ticket and expects the +`CasLoginModule` JAAS module to perform the CAS ticket validation in order to produce an `AssertionPrincipal` from which the CAS assertion is obtained and inserted into the session to enable SSO. + +If a `service` init-param is specified for this filter, it supersedes +the service defined for the `CasLoginModule`. + + +## JBoss Integration + +In keeping with CAS integration for Java applications, a JEE container-specific servlet filter is needed to protect JEE Web applications. The JBoss `WebAuthenticationFilter` component provided a convenient integration piece between a servlet filter and the JAAS framework, so a complete integration solution is available only for JBoss AS versions that provide the `WebAuthenticationFilter` class. The JAAS support should be extensible to any JEE container with additional development. + +For JBoss it is vitally important to use the correct values for `principalGroupName` and `roleGroupName`. Additionally, the `cacheAssertions` and `cacheTimeout` are required since JBoss by default attempts to reauthenticate the JAAS principal with a fairly aggressive default timeout. Since CAS tickets are single-use authentication tokens by default, assertion caching is required to support periodic reauthentication. + + +### Configure Servlet Filters + +Integration with the servlet pipeline is required for a number of purposes: + +1. Examine servlet request for an authenticated session +2. Redirect to CAS server for unauthenticated sessions +3. Provide service URL and CAS ticket to JAAS pipeline for validation + +The `WebAuthenticationFilter` performs these operations for the JBoss AS container. It is important to note that this filter simply collects the service URL and CAS ticket from the request and passes it to the JAAS pipeline. It is assumed that the `CasLoginModule` will be present in the JAAS pipeline to consume the data and perform ticket validation. The following web.xml excerpts demonstrate how to integrate WebAuthenticationFilter into a JEE Web application. + + +```xml +... + + CASWebAuthenticationFilter + org.jasig.cas.client.jboss.authentication.WebAuthenticationFilter + + + + CASAuthenticationFilter + org.jasig.cas.client.authentication.AuthenticationFilter + + casServerLoginUrl + https://cas.example.com/cas/login + + +... + +... +``` + +The JAAS LoginModule configuration in `conf/login-config.xml` may require the following changes in a JBoss environment: + +```xml + + + + org.jasig.cas.client.validation.Saml11TicketValidator + http://yourcasserver/cas + 20000 + admin,user + memberOf,eduPersonAffiliation,authorities + CallerPrincipal + Roles + true + 480 + + + +``` +It may be necessary to modify the JBoss `server.xml` and uncomment: + +```xml + +``` + +Remember not to add `` and `` elements in your `web.xml`. + +If you have any trouble, you can enable the log of cas in `jboss-logging.xml` by adding: + +```xml + + + +``` + + +## Tomcat 6/7 Integration +The client supports container-based CAS authentication and authorization support for the Tomcat servlet container. + +Suppose a single Tomcat container hosts multiple Web applications with similar authentication and authorization needs. Prior to Tomcat container support, each application would require a similar configuration of CAS servlet filters and authorization configuration in the `web.xml` servlet descriptor. Using the new container-based authentication/authorization feature, a single CAS configuration can be applied to the container and leveraged by all Web applications hosted by the container. + +CAS authentication support for Tomcat is based on the Tomcat-specific Realm component. The Realm component has a fairly broad surface area and RealmBase is provided as a convenient superclass for custom implementations; the CAS realm implementations derive from `RealmBase`. Unfortunately RealmBase and related components have proven to change over both major and minor number releases, which requires version-specific CAS components for integration. We have provided two packages with similar components with the hope of supporting all 6.x and 7.x versions. **No support for 5.x is provided.** + + +### Component Overview +In the following discussion of components, only the Tomcat 6.x components are mentioned. The Tomcat 7.0.x components have exactly the same name, but **are in the tomcat.v7 package**, e.g. `org.jasig.cas.client.tomcat.v7.Cas20CasAuthenticator`. + + +#### Authenticators +Authenticators are responsible for performing CAS authentication using a particular protocol. All protocols supported by the Jasig Java CAS client are supported: CAS 1.0, CAS 2.0, and SAML 1.1. The following components provide protocol-specific support: + +``` +org.jasig.cas.client.tomcat.v6.Cas10CasAuthenticator +org.jasig.cas.client.tomcat.v6.Cas20CasAuthenticator +org.jasig.cas.client.tomcat.v6.Cas20ProxyCasAuthenticator +org.jasig.cas.client.tomcat.v6.Saml11Authenticator +``` + + +#### Realms +In terms of CAS configuration, Tomcat realms serve as containers for users and role definitions. The roles defined in a Tomcat realm may be referenced in the web.xml servlet descriptor to define authorization constraints on Web applications hosted by the container. Two sources of user/role data are supported: + +``` +org.jasig.cas.client.tomcat.v6.PropertiesCasRealm +org.jasig.cas.client.tomcat.v6.AssertionCasRealm +``` + +`PropertiesCasRealm` uses a Java properties file as a source of static user/role information. This component is conceptually similar to the `MemoryRealm` component that ships with Tomcat and defines user/role data via XML configuration. The PropertiesCasRealm is different in that it explicitly lacks support for passwords, which have no use with CAS. + +`AssertionCasRealm` is designed to be used in conjunction with the SAML 1.1. protocol to take advantage of CAS attribute release to provide for dynamic user/role data driven by the CAS server. With this component the deployer may define a role attribute, e.g. memberOf, which could be backed by LDAP group membership information. In that case the user would be added to all roles defined in the SAML attribute assertion for values of the the `memberOf` attribute. + + +#### Valves +A number of Tomcat valves are provided to handle functionality outside Realms and Authenticators. + +##### Logout Valves +Logout valves provide a way of destroying the CAS authentication state bound to the container for a particular user/session; the destruction of authenticated state is synonymous with logout for the container and its hosted applications. (Note this does not destroy the CAS SSO session.) The implementations provide various strategies to map a URI onto the state-destroying logout function. + +``` +org.jasig.cas.client.tomcat.v6.StaticUriLogoutValve +org.jasig.cas.client.tomcat.v6.RegexUriLogoutValve +``` + +##### SingleSignOutValve +The `org.jasig.cas.client.tomcat.v6.SingleSignOutValve` allows the container to participate in CAS single sign-out. In particular this valve handles the SAML LogoutRequest message sent from the CAS server that is delivered when the CAS SSO session ends. + +##### ProxyCallbackValve +The `org.jasig.cas.client.tomcat.v6.ProxyCallbackValve` provides a handler for watching request URIs for requests that contain a proxy callback request in support of the CAS 2.0 protocol proxy feature. + + +### Container Setup +The version-specific CAS libraries must be placed on the container classpath, `$CATALINA_HOME/lib`. + + +### Context Configuration +The Realm, Authenticator, and Valve components are wired together inside a Tomcat Context configuration element. The location and scope of the Context determines the scope of the applied configuration. To apply a CAS configuration to every Web application hosted in the container, configure the default Context at `$CATALINA_HOME/conf/context.xml`. Note that individual Web applications/servlets can override the default context; see the Context Container reference for more information. + +Alternatively, CAS configuration can be applied to individual Web applications through a Context configuration element located in a `$CONTEXT_NAME.xml` file placed in `$CATALINA_HOME/conf/$ENGINE/$HOST`, where `$ENGINE` is typically Catalina and `$HOST` is `localhost`, `$CATALINA_HOME/conf/Catalina/localhost`. For example, to configure the Tomcat manager servlet, a `manager.xml` file contains Context configuration elements. + +```xml + + + + + + + + + + + + +``` + +The following example shows how to configure a Context for dynamic role data provided by the CAS attribute release feature. + +```xml + + + + + + + + + +``` + + +## Atlassian Integration +The clien includes Atlassian Confluence and JIRA support. Support is enabled by a custom CAS authenticator that extends the default authenticators. + + +### Configuration + + +#### $JIRA_HOME Location + +- WAR/EAR Installation: /webapp +`/opt/atlassian/jira/atlassian-jira-enterprise-x.y.z/webapp` + +- Standalone: /atlassian-jira +`/opt/atlassian/jira/atlassian-jira-enterprise-x.y.z-standalone/atlassian-jira` + + +#### $CONFLUENCE_INSTALL Description + +- /confluence +`/opt/atlassian/confluence/confluence-x.y.z/confluence` + + +#### Changes to web.xml +Add the CAS filters to the end of the filter list. See `web.xml` configuration of the client. + + + +#### Modify the seraph-config.xml +To rely on the Single Sign Out functionality to sign off of Jira, comment out the normal logout URL and replace it with the CAS logout URL. Also, change the login links to point to the CAS login service. + +```xml + + + login.url + + http://cas.institution.edu/cas/login?service=${originalurl} + + + + link.login.url + + + http://cas.institution.edu/cas/login?service=${originalurl} + + + + logout.url + + https://cas.institution.edu/cas/logout + +``` + + +#### CAS Authenticator +Comment out the `DefaultAuthenticator` like so in `[$JIRA_HOME|$CONFLUENCE_INSTALL]/WEB-INF/classes/seraph-config.xml`: + +```xml + + + +``` + +For JIRA, add in the Client Jira Authenticator: + +```xml + + + +``` + +For Confluence, add in the Client Confluence Authenticator: + +```xml + + + +``` + + +#### Confluence CAS Logout + +As of this writing, Atlassian doesn't support a config option yet (like Jira). To rely on the Single Sign Out functionality to sign off of Confluence we need to modify the logout link. + + +- Copy `$CONFLUENCE_INSTALL/WEB-INF/lib/confluence-x.x.x.jar` to a temporary directory +- `mkdir /tmp/confluence-jar && cp WEB-INF/lib/confluence-x.y.z.jar /tmp/confluence-jar` +- Unpack the jar +- `cd /tmp/confluence-jar && jar xvf confluence-x.y.z.jar` +- `cp xwork.xml $CONFLUENCE_INSTALL/WEB-INF/classes` +- `cp xwork.xml $CONFLUENCE_INSTALL/WEB-INF/classes/ && cd $CONFLUENCE_INSTALL/WEB-INF/classes/` +- Edit `$CONFLUENCE_INSTALL/WEB-INF/classes/xwork.xml`, find the logout action and comment out the success result and replace it with this one: + +```xml + + +https://cas.institution.edu/cas/logout + +``` + + +#### Copy Jars +Copy cas-client-core-x.y.x.jar and cas-client-integration-atlassian-x.y.x.jar to `$JIRA_HOME/WEB-INF/lib` + + +## Spring Security Integration +This configuration tested against the sample application that is included with Spring Security. As of this writing, replacing the `applicationContext-security.xml` in the sample application with the one below would enable this alternative configuration. We can not guarantee this version will work without modification in future versions of Spring Security. + + +### Changes to web.xml + +```xml +... + + contextConfigLocation + + /WEB-INF/applicationContext-security.xml + + + + + log4jConfigLocation + /WEB-INF/classes/log4j.properties + + + + webAppRootKey + cas.root + + + + CAS Single Sign Out Filter + org.jasig.cas.client.session.SingleSignOutFilter + + + + springSecurityFilterChain + org.springframework.web.filter.DelegatingFilterProxy + + + + CAS Single Sign Out Filter + /* + + + + springSecurityFilterChain + /* + + + + org.jasig.cas.client.session.SingleSignOutHttpSessionListener + + + + org.springframework.web.context.ContextLoaderListener + + + + org.springframework.web.util.Log4jConfigListener + + + + 403 + /casfailed.jsp + +... +``` + +The important additions to the `web.xml` include the addition of the 403 error page. 403 is what the CAS Validation Filter will throw if it has a problem with the ticket. Also, if you want Single Log Out, you should enable the `SingleSignOutHttpSessionListener`. + + +### Changes to applicationContext-security.xml + +```xml +... + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +... +``` + +1. You should replace the `userService` with something that checks your user storage. +2. Replace the `serverName` and `casServerLoginUrl` with your values (or better yet, externalize them). +3. Replace the URLs with the URL configuration for your application. diff --git a/README.txt b/README.txt deleted file mode 100644 index 97a80ee2e..000000000 --- a/README.txt +++ /dev/null @@ -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. diff --git a/assembly.xml b/assembly.xml index 26f7cab12..3bf2928eb 100644 --- a/assembly.xml +++ b/assembly.xml @@ -5,81 +5,80 @@ 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: + 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 + 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 + 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. --> - - release - - zip - tar.gz - - true - - - unix - true - false - ${basedir} - - - *.xml - *.txt - - - - - - - - - - src - src - unix - true - + release + + zip + tar.gz + + true + + + unix + true + false + ${basedir} + + + *.xml + *.txt + + + + + + + + + + src + src + unix + true + - - unix - true - - *.xml - - + + unix + true + + *.xml + + - - unix - target/site/apidocs/ - true - docs - - **/* - - - + + unix + target/site/apidocs/ + true + docs + + **/* + + + - true - true - - - modules - true - false - true - - - - + true + true + + + modules + true + false + true + + + + \ No newline at end of file diff --git a/cas-client-core/NOTICE b/cas-client-core/NOTICE new file mode 100644 index 000000000..8b26daa03 --- /dev/null +++ b/cas-client-core/NOTICE @@ -0,0 +1,38 @@ +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. + +This project includes: + AOP alliance under Public Domain + Apache Log4j under The Apache Software License, Version 2.0 + Apache XML Security under The Apache Software License, Version 2.0 + Commons Codec under The Apache Software 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 + JavaBeans Activation Framework (JAF) under Common Development and Distribution License (CDDL) v1.0 + JavaMail API under Common Development and Distribution License (CDDL) v1.0 + JCL 1.1.1 implemented over SLF4J under MIT License + JUnit under Common Public License Version 1.0 + SLF4J API Module under MIT License + SLF4J Simple Binding under MIT License + spring-aop under The Apache Software License, Version 2.0 + spring-asm under The Apache Software License, Version 2.0 + spring-beans under The Apache Software License, Version 2.0 + spring-context under The Apache Software License, Version 2.0 + spring-core under The Apache Software License, Version 2.0 + spring-expression under The Apache Software License, Version 2.0 + spring-test under The Apache Software License, Version 2.0 + diff --git a/cas-client-core/pom.xml b/cas-client-core/pom.xml index 2b25337bb..522f05acc 100644 --- a/cas-client-core/pom.xml +++ b/cas-client-core/pom.xml @@ -1,94 +1,94 @@ - - + + + org.jasig.cas.client + 3.4.1-SNAPSHOT + cas-client + + 4.0.0 org.jasig.cas.client - 3.3-SNAPSHOT - cas-client - - 4.0.0 - org.jasig.cas.client - cas-client-core - jar - Jasig CAS Client for Java - Core + cas-client-core + jar + Jasig CAS Client for Java - Core - - - xml-security - xmlsec - 1.3.0 - runtime - true - + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + + test-jar + + + + + + - - org.opensaml - opensaml - 1.1 - jar - provided - true - + + + xml-security + xmlsec + 1.3.0 + runtime + true + - - commons-codec - commons-codec - 1.4 - jar - true - + + commons-codec + commons-codec + 1.4 + jar + true + - - org.springframework - spring-beans - ${spring.version} - provided - + + org.springframework + spring-beans + ${spring.version} + provided + - - org.springframework - spring-test - ${spring.version} - test - + + org.springframework + spring-test + test + - - org.springframework - spring-core - ${spring.version} - test - + + org.springframework + spring-core + test + - - org.springframework - spring-context - ${spring.version} - test - + + org.springframework + spring-context + test + - - log4j - log4j - test - 1.2.15 - - - jmxri - com.sun.jmx - - - com.sun.jdmk - jmxtools - - - javax.jms - jms - - - - + + log4j + log4j + test + 1.2.15 + + + jmxri + com.sun.jmx + + + com.sun.jdmk + jmxtools + + + javax.jms + jms + + + + - - 2.5.6.SEC01 - diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/Protocol.java b/cas-client-core/src/main/java/org/jasig/cas/client/Protocol.java new file mode 100644 index 000000000..bf259aaef --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/Protocol.java @@ -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; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/AttributePrincipal.java b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/AttributePrincipal.java index d17c97fd6..3a454501b 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/AttributePrincipal.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/AttributePrincipal.java @@ -1,22 +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: + * 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 + * 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 + * 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.authentication; import java.io.Serializable; @@ -48,6 +47,6 @@ public interface AttributePrincipal extends Principal, Serializable { * The Map of key/value pairs associated with this principal. * @return the map of key/value pairs associated with this principal. */ - Map getAttributes(); + Map getAttributes(); } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/AttributePrincipalImpl.java b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/AttributePrincipalImpl.java index e93978b39..d4f7d61f1 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/AttributePrincipalImpl.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/AttributePrincipalImpl.java @@ -1,48 +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: + * 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 + * 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 + * 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.authentication; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.jasig.cas.client.proxy.ProxyRetriever; -import org.jasig.cas.client.util.CommonUtils; - import java.util.Collections; import java.util.Map; +import org.jasig.cas.client.proxy.ProxyRetriever; +import org.jasig.cas.client.util.CommonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Concrete implementation of the AttributePrincipal interface. * * @author Scott Battaglia - * @version $Revision$ $Date$ * @since 3.1 */ public class AttributePrincipalImpl extends SimplePrincipal implements AttributePrincipal { - - private static final Log LOG = LogFactory.getLog(AttributePrincipalImpl.class); + + private static final Logger LOGGER = LoggerFactory.getLogger(AttributePrincipalImpl.class); /** Unique Id for Serialization */ private static final long serialVersionUID = -1443182634624927187L; /** Map of key/value pairs about this principal. */ - private final Map attributes; + private final Map attributes; /** The CAS 2 ticket used to retrieve a proxy ticket. */ private final String proxyGrantingTicket; @@ -56,7 +53,7 @@ public class AttributePrincipalImpl extends SimplePrincipal implements Attribute * @param name the unique identifier for the principal. */ public AttributePrincipalImpl(final String name) { - this(name, Collections.emptyMap()); + this(name, Collections. emptyMap()); } /** @@ -65,8 +62,8 @@ public AttributePrincipalImpl(final String name) { * @param name the unique identifier for the principal. * @param attributes the key/value pairs for this principal. */ - public AttributePrincipalImpl(final String name, final Map attributes) { - this(name, attributes, null, null); + public AttributePrincipalImpl(final String name, final Map attributes) { + this(name, attributes, null, null); } /** @@ -76,19 +73,21 @@ public AttributePrincipalImpl(final String name, final Map attrib * @param proxyGrantingTicket the ticket associated with this principal. * @param proxyRetriever the ProxyRetriever implementation to call back to the CAS server. */ - public AttributePrincipalImpl(final String name, final String proxyGrantingTicket, final ProxyRetriever proxyRetriever) { - this(name, Collections.emptyMap(), proxyGrantingTicket, proxyRetriever); + public AttributePrincipalImpl(final String name, final String proxyGrantingTicket, + final ProxyRetriever proxyRetriever) { + this(name, Collections. emptyMap(), proxyGrantingTicket, proxyRetriever); } /** - * Constructs a new principal witht he supplied name, attributes, and proxying capabilities. + * Constructs a new principal with the supplied name, attributes, and proxying capabilities. * * @param name the unique identifier for the principal. * @param attributes the key/value pairs for this principal. * @param proxyGrantingTicket the ticket associated with this principal. * @param proxyRetriever the ProxyRetriever implementation to call back to the CAS server. */ - public AttributePrincipalImpl(final String name, final Map attributes, final String proxyGrantingTicket, final ProxyRetriever proxyRetriever) { + public AttributePrincipalImpl(final String name, final Map attributes, + final String proxyGrantingTicket, final ProxyRetriever proxyRetriever) { super(name); this.attributes = attributes; this.proxyGrantingTicket = proxyGrantingTicket; @@ -97,7 +96,7 @@ public AttributePrincipalImpl(final String name, final Map attrib CommonUtils.assertNotNull(this.attributes, "attributes cannot be null."); } - public Map getAttributes() { + public Map getAttributes() { return this.attributes; } @@ -105,8 +104,8 @@ public String getProxyTicketFor(String service) { if (proxyGrantingTicket != null) { return this.proxyRetriever.getProxyTicketIdFor(this.proxyGrantingTicket, service); } - - LOG.debug("No ProxyGrantingTicket was supplied, so no Proxy Ticket can be retrieved."); + + LOGGER.debug("No ProxyGrantingTicket was supplied, so no Proxy Ticket can be retrieved."); return null; } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/AuthenticationFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/AuthenticationFilter.java index 67f36332b..d5e7fe452 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/AuthenticationFilter.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/AuthenticationFilter.java @@ -1,37 +1,38 @@ -/** +/* * 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: + * 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 + * 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 + * 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.authentication; -import org.jasig.cas.client.util.AbstractCasFilter; -import org.jasig.cas.client.util.CommonUtils; -import org.jasig.cas.client.validation.Assertion; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; +import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import java.io.IOException; + +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; +import org.jasig.cas.client.validation.Assertion; /** * Filter implementation to intercept all requests and attempt to authenticate @@ -47,11 +48,10 @@ *

Please see AbstractCasFilter for additional properties.

* * @author Scott Battaglia - * @version $Revision: 11768 $ $Date: 2007-02-07 15:44:16 -0500 (Wed, 07 Feb 2007) $ + * @author Misagh Moayyed * @since 3.0 */ public class AuthenticationFilter extends AbstractCasFilter { - /** * The URL to the CAS Server login. */ @@ -66,28 +66,67 @@ public class AuthenticationFilter extends AbstractCasFilter { * Whether to send the gateway request or not. */ private boolean gateway = false; - + private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl(); + private AuthenticationRedirectStrategy authenticationRedirectStrategy = new DefaultAuthenticationRedirectStrategy(); + + private UrlPatternMatcherStrategy ignoreUrlPatternMatcherStrategyClass = null; + + private static final Map> PATTERN_MATCHER_TYPES = + new HashMap>(); + + static { + PATTERN_MATCHER_TYPES.put("CONTAINS", ContainsPatternUrlPatternMatcherStrategy.class); + 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)); - log.trace("Loaded CasServerLoginUrl parameter: " + this.casServerLoginUrl); - setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false"))); - log.trace("Loaded renew parameter: " + this.renew); - setGateway(parseBoolean(getPropertyFromInitParams(filterConfig, "gateway", "false"))); - log.trace("Loaded gateway parameter: " + this.gateway); - - final String gatewayStorageClass = getPropertyFromInitParams(filterConfig, "gatewayStorageClass", null); + setCasServerLoginUrl(getString(ConfigurationKeys.CAS_SERVER_LOGIN_URL)); + setRenew(getBoolean(ConfigurationKeys.RENEW)); + setGateway(getBoolean(ConfigurationKeys.GATEWAY)); + + final String ignorePattern = getString(ConfigurationKeys.IGNORE_PATTERN); + final String ignoreUrlPatternType = getString(ConfigurationKeys.IGNORE_URL_PATTERN_TYPE); + + if (ignorePattern != null) { + final Class ignoreUrlMatcherClass = PATTERN_MATCHER_TYPES.get(ignoreUrlPatternType); + if (ignoreUrlMatcherClass != null) { + this.ignoreUrlPatternMatcherStrategyClass = ReflectUtils.newInstance(ignoreUrlMatcherClass.getName()); + } else { + try { + logger.trace("Assuming {} is a qualified class name...", ignoreUrlPatternType); + this.ignoreUrlPatternMatcherStrategyClass = ReflectUtils.newInstance(ignoreUrlPatternType); + } catch (final IllegalArgumentException e) { + logger.error("Could not instantiate class [{}]", ignoreUrlPatternType, e); + } + } + if (this.ignoreUrlPatternMatcherStrategyClass != null) { + this.ignoreUrlPatternMatcherStrategyClass.setPattern(ignorePattern); + } + } + + final Class gatewayStorageClass = getClass(ConfigurationKeys.GATEWAY_STORAGE_CLASS); if (gatewayStorageClass != null) { - try { - this.gatewayStorage = (GatewayResolver) Class.forName(gatewayStorageClass).newInstance(); - } catch (final Exception e) { - log.error(e,e); - throw new ServletException(e); - } + setGatewayStorage(ReflectUtils.newInstance(gatewayStorageClass)); + } + + final Class authenticationRedirectStrategyClass = getClass(ConfigurationKeys.AUTHENTICATION_REDIRECT_STRATEGY_CLASS); + + if (authenticationRedirectStrategyClass != null) { + this.authenticationRedirectStrategy = ReflectUtils.newInstance(authenticationRedirectStrategyClass); } } } @@ -97,9 +136,18 @@ public void init() { CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null."); } - public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { + public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, + final FilterChain filterChain) throws IOException, ServletException { + final HttpServletRequest request = (HttpServletRequest) servletRequest; final HttpServletResponse response = (HttpServletResponse) servletResponse; + + if (isRequestUrlExcluded(request)) { + logger.debug("Request is ignored."); + filterChain.doFilter(request, response); + return; + } + final HttpSession session = request.getSession(false); final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null; @@ -119,25 +167,21 @@ public final void doFilter(final ServletRequest servletRequest, final ServletRes final String modifiedServiceUrl; - log.debug("no ticket and no assertion found"); + logger.debug("no ticket and no assertion found"); if (this.gateway) { - log.debug("setting gateway attribute in session"); + logger.debug("setting gateway attribute in session"); modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl); } else { modifiedServiceUrl = serviceUrl; } - if (log.isDebugEnabled()) { - log.debug("Constructed service url: " + modifiedServiceUrl); - } + logger.debug("Constructed service url: {}", modifiedServiceUrl); - final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway); - - if (log.isDebugEnabled()) { - log.debug("redirecting to \"" + urlToRedirectTo + "\""); - } + final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, + getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway); - response.sendRedirect(urlToRedirectTo); + logger.debug("redirecting to \"{}\"", urlToRedirectTo); + this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo); } public final void setRenew(final boolean renew) { @@ -151,8 +195,21 @@ public final void setGateway(final boolean gateway) { public final void setCasServerLoginUrl(final String casServerLoginUrl) { this.casServerLoginUrl = casServerLoginUrl; } - + public final void setGatewayStorage(final GatewayResolver gatewayStorage) { - this.gatewayStorage = gatewayStorage; + this.gatewayStorage = gatewayStorage; + } + + private boolean isRequestUrlExcluded(final HttpServletRequest request) { + if (this.ignoreUrlPatternMatcherStrategyClass == null) { + return false; + } + + final StringBuffer urlBuffer = request.getRequestURL(); + if (request.getQueryString() != null) { + urlBuffer.append("?").append(request.getQueryString()); + } + final String requestUri = urlBuffer.toString(); + return this.ignoreUrlPatternMatcherStrategyClass.matches(requestUri); } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/AuthenticationRedirectStrategy.java b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/AuthenticationRedirectStrategy.java new file mode 100644 index 000000000..006458be5 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/AuthenticationRedirectStrategy.java @@ -0,0 +1,46 @@ +/* + * 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.authentication; + +import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Interface to abstract the authentication strategy for redirecting. The traditional method was to always just redirect, + * but due to AJAX, etc. we may need to support other strategies. This interface is designed to hold that logic such that + * authentication filter class does not get crazily complex. + * + * @author Scott Battaglia + * @since 3.3.0 + */ +public interface AuthenticationRedirectStrategy { + + /** + * Method name is a bit of a misnomer. This method handles "redirection" for a localized version of redirection (i.e. AJAX might mean an XML fragment that contains the url to go to). + * + * @param request the original HttpServletRequest. MAY NOT BE NULL. + * @param response the original HttpServletResponse. MAY NOT BE NULL. + * @param potentialRedirectUrl the url that might be used (there are no guarantees of course!) + * @throws IOException the exception to throw if there is some type of error. This will bubble up through the filter. + */ + void redirect(HttpServletRequest request, HttpServletResponse response, String potentialRedirectUrl) + throws IOException; + +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/ContainsPatternUrlPatternMatcherStrategy.java b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/ContainsPatternUrlPatternMatcherStrategy.java new file mode 100644 index 000000000..48c9f5ff9 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/ContainsPatternUrlPatternMatcherStrategy.java @@ -0,0 +1,38 @@ +/* + * 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.authentication; + +/** + * A pattern matcher that looks inside the url to find the exact pattern specified. + * + * @author Misagh Moayyed + * @since 3.3.1 + */ +public final class ContainsPatternUrlPatternMatcherStrategy implements UrlPatternMatcherStrategy { + + private String pattern; + + public boolean matches(final String url) { + return url.contains(this.pattern); + } + + public void setPattern(final String pattern) { + this.pattern = pattern; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/DefaultAuthenticationRedirectStrategy.java b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/DefaultAuthenticationRedirectStrategy.java new file mode 100644 index 000000000..d38daed81 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/DefaultAuthenticationRedirectStrategy.java @@ -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.authentication; + +import java.io.IOException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Implementation of the {@link AuthenticationRedirectStrategy} class that preserves the original behavior that existed prior to 3.3.0. + * + * @author Scott Battaglia + * @since 3.3.0 + */ +public final class DefaultAuthenticationRedirectStrategy implements AuthenticationRedirectStrategy { + + public void redirect(final HttpServletRequest request, final HttpServletResponse response, + final String potentialRedirectUrl) throws IOException { + response.sendRedirect(potentialRedirectUrl); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/DefaultGatewayResolverImpl.java b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/DefaultGatewayResolverImpl.java index 7b363690f..d0d24756d 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/DefaultGatewayResolverImpl.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/DefaultGatewayResolverImpl.java @@ -1,47 +1,44 @@ -/** +/* * 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: + * 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 + * 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 + * 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.authentication; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; public final class DefaultGatewayResolverImpl implements GatewayResolver { - + public static final String CONST_CAS_GATEWAY = "_const_cas_gateway_"; - public boolean hasGatewayedAlready(final HttpServletRequest request, - final String serviceUrl) { - final HttpSession session = request.getSession(false); - - if (session == null) { - return false; - } - - final boolean result = session.getAttribute(CONST_CAS_GATEWAY) != null; - session.removeAttribute(CONST_CAS_GATEWAY); - return result; - } + public boolean hasGatewayedAlready(final HttpServletRequest request, final String serviceUrl) { + final HttpSession session = request.getSession(false); + + if (session == null) { + return false; + } + + final boolean result = session.getAttribute(CONST_CAS_GATEWAY) != null; + session.removeAttribute(CONST_CAS_GATEWAY); + return result; + } - public String storeGatewayInformation(final HttpServletRequest request, - final String serviceUrl) { - request.getSession(true).setAttribute(CONST_CAS_GATEWAY, "yes"); - return serviceUrl; - } + public String storeGatewayInformation(final HttpServletRequest request, final String serviceUrl) { + request.getSession(true).setAttribute(CONST_CAS_GATEWAY, "yes"); + return serviceUrl; + } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/ExactUrlPatternMatcherStrategy.java b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/ExactUrlPatternMatcherStrategy.java new file mode 100644 index 000000000..9fd0ddf4e --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/ExactUrlPatternMatcherStrategy.java @@ -0,0 +1,46 @@ +/* + * 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.authentication; + +/** + * A pattern matcher that produces a successful match if the pattern + * specified matches the given url exactly and equally. + * + * @author Misagh Moayyed + * @since 3.3.1 + */ +public final class ExactUrlPatternMatcherStrategy implements UrlPatternMatcherStrategy { + + private String pattern; + + public ExactUrlPatternMatcherStrategy() {} + + public ExactUrlPatternMatcherStrategy(final String pattern) { + this.setPattern(pattern); + } + + public boolean matches(final String url) { + return url.equals(this.pattern); + } + + public void setPattern(final String pattern) { + this.pattern = pattern; + } + +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/FacesCompatibleAuthenticationRedirectStrategy.java b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/FacesCompatibleAuthenticationRedirectStrategy.java new file mode 100644 index 000000000..29dce2f70 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/FacesCompatibleAuthenticationRedirectStrategy.java @@ -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.authentication; + +import java.io.IOException; +import java.io.PrintWriter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.jasig.cas.client.util.CommonUtils; + +/** + * Implementation of the redirect strategy that can handle a Faces Ajax request in addition to the standard redirect style. + * + * @author Scott Battaglia + * @since 3.3.0 + */ +public final class FacesCompatibleAuthenticationRedirectStrategy implements AuthenticationRedirectStrategy { + + private static final String FACES_PARTIAL_AJAX_PARAMETER = "javax.faces.partial.ajax"; + + public void redirect(final HttpServletRequest request, final HttpServletResponse response, + final String potentialRedirectUrl) throws IOException { + + if (CommonUtils.isNotBlank(request.getParameter(FACES_PARTIAL_AJAX_PARAMETER))) { + // this is an ajax request - redirect ajaxly + response.setContentType("text/xml"); + response.setStatus(200); + + final PrintWriter writer = response.getWriter(); + writer.write(""); + writer.write(String.format("", + potentialRedirectUrl)); + } else { + response.sendRedirect(potentialRedirectUrl); + } + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/GatewayResolver.java b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/GatewayResolver.java index 9c587fb93..9499a1947 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/GatewayResolver.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/GatewayResolver.java @@ -1,22 +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: + * 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 + * 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 + * 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.authentication; import javax.servlet.http.HttpServletRequest; @@ -32,21 +31,21 @@ */ public interface GatewayResolver { - /** - * Determines if the request has been gatewayed already. Should also do gateway clean up. - * - * @param request the Http Servlet Request - * @param serviceUrl the service url - * @return true if yes, false otherwise. - */ - boolean hasGatewayedAlready(HttpServletRequest request, String serviceUrl); - - /** - * Storage the request for gatewaying and return the service url, which can be modified. - * - * @param request the HttpServletRequest. - * @param serviceUrl the service url - * @return the potentially modified service url to redirect to - */ - String storeGatewayInformation(HttpServletRequest request, String serviceUrl); + /** + * Determines if the request has been gatewayed already. Should also do gateway clean up. + * + * @param request the Http Servlet Request + * @param serviceUrl the service url + * @return true if yes, false otherwise. + */ + boolean hasGatewayedAlready(HttpServletRequest request, String serviceUrl); + + /** + * Storage the request for gatewaying and return the service url, which can be modified. + * + * @param request the HttpServletRequest. + * @param serviceUrl the service url + * @return the potentially modified service url to redirect to + */ + String storeGatewayInformation(HttpServletRequest request, String serviceUrl); } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/RegexUrlPatternMatcherStrategy.java b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/RegexUrlPatternMatcherStrategy.java new file mode 100644 index 000000000..e5665cdf7 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/RegexUrlPatternMatcherStrategy.java @@ -0,0 +1,47 @@ +/* + * 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.authentication; + +import java.util.regex.Pattern; + +/** + * A pattern matcher that looks inside the url to find the pattern,. that + * is assumed to have been specified via regular expressions syntax. + * + * @author Misagh Moayyed + * @since 3.3.1 + */ +public final class RegexUrlPatternMatcherStrategy implements UrlPatternMatcherStrategy { + + private Pattern pattern; + + public RegexUrlPatternMatcherStrategy() {} + + public RegexUrlPatternMatcherStrategy(final String pattern) { + this.setPattern(pattern); + } + + public boolean matches(final String url) { + return this.pattern.matcher(url).find(); + } + + public void setPattern(final String pattern) { + this.pattern = Pattern.compile(pattern); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/Saml11AuthenticationFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/Saml11AuthenticationFilter.java deleted file mode 100644 index b0db0c223..000000000 --- a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/Saml11AuthenticationFilter.java +++ /dev/null @@ -1,45 +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: - * - * 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.authentication; - -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; - -/** - * Extension to the default Authentication filter that sets the required SAML1.1 artifact parameter name and service parameter name. - *

- * Note, as of 3.3, the final keyword was removed to allow you to override the method to retrieve tickets, per CASC-154s - * - * @author Scott Battaglia - * @since 3.1.12 - * @version $Revision$ $Date$ - */ -public class Saml11AuthenticationFilter extends AuthenticationFilter { - - protected final void initInternal(final FilterConfig filterConfig) throws ServletException { - super.initInternal(filterConfig); - - log.warn("SAML1.1 compliance requires the [artifactParameterName] and [serviceParameterName] to be set to specified values."); - log.warn("This filter will overwrite any user-provided values (if any are provided)"); - - setArtifactParameterName("SAMLart"); - setServiceParameterName("TARGET"); - } -} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/SimpleGroup.java b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/SimpleGroup.java index 0abe6029a..37e7f5930 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/SimpleGroup.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/SimpleGroup.java @@ -1,22 +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: + * 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 + * 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 + * 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.authentication; import java.security.Principal; @@ -65,7 +64,7 @@ public Enumeration members() { public boolean removeMember(final Principal user) { return this.members.remove(user); } - + public String toString() { return super.toString() + ": " + members.toString(); } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/SimplePrincipal.java b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/SimplePrincipal.java index fba5e09aa..4f73a7340 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/SimplePrincipal.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/SimplePrincipal.java @@ -1,27 +1,25 @@ -/** +/* * 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: + * 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 + * 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 + * 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.authentication; import java.io.Serializable; import java.security.Principal; - import org.jasig.cas.client.util.CommonUtils; /** @@ -63,7 +61,7 @@ public boolean equals(final Object o) { } else if (!(o instanceof SimplePrincipal)) { return false; } else { - return getName().equals(((SimplePrincipal)o).getName()); + return getName().equals(((SimplePrincipal) o).getName()); } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/authentication/UrlPatternMatcherStrategy.java b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/UrlPatternMatcherStrategy.java new file mode 100644 index 000000000..a2e70e900 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/authentication/UrlPatternMatcherStrategy.java @@ -0,0 +1,42 @@ +/* + * 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.authentication; +/** + * Defines an abstraction by which request urls can be matches against a given pattern. + * New instances for all extensions for this strategy interface will be created per + * each request. The client will ultimately invoke the {@link #matches(String)} method + * having already applied and set the pattern via the {@link #setPattern(String)} method. + * The pattern itself will be retrieved via the client configuration. + * @author Misagh Moayyed + * @since 3.3.1 + */ +public interface UrlPatternMatcherStrategy { + /** + * Execute the match between the given pattern and the url + * @param url the request url typically with query strings included + * @return true if match is successful + */ + boolean matches(String url); + + /** + * The pattern against which the url is compared + * @param pattern + */ + void setPattern(String pattern); +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/BaseConfigurationStrategy.java b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/BaseConfigurationStrategy.java new file mode 100644 index 000000000..0ae9d976e --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/BaseConfigurationStrategy.java @@ -0,0 +1,111 @@ +/* + * 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.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 configurationKey) { + return getValue(configurationKey, new Parser() { + public Boolean parse(final String value) { + return CommonUtils.toBoolean(value); + } + }); + } + + public final long getLong(final ConfigurationKey configurationKey) { + return getValue(configurationKey, new Parser() { + public Long parse(final String value) { + return CommonUtils.toLong(value, configurationKey.getDefaultValue()); + } + }); + } + + public final int getInt(final ConfigurationKey configurationKey) { + return getValue(configurationKey, new Parser() { + public Integer parse(final String value) { + return CommonUtils.toInt(value, configurationKey.getDefaultValue()); + } + }); + } + + public final String getString(final ConfigurationKey configurationKey) { + return getValue(configurationKey, new Parser() { + public String parse(final String value) { + return value; + } + }); + } + + public Class getClass(final ConfigurationKey> configurationKey) { + return getValue(configurationKey, new Parser>() { + public Class parse(final String value) { + try { + return ReflectUtils.loadClass(value); + } catch (final IllegalArgumentException e) { + return configurationKey.getDefaultValue(); + } + } + }); + } + + private T getValue(final ConfigurationKey configurationKey, final Parser 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 parse(String value); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationKey.java b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationKey.java new file mode 100644 index 000000000..da6a3a19f --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationKey.java @@ -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 { + + 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 null value indicates that there is no default. + * + * @return the default value or null. + */ + public E getDefaultValue() { + return this.defaultValue; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationKeys.java b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationKeys.java new file mode 100644 index 000000000..14ec12062 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationKeys.java @@ -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 ARTIFACT_PARAMETER_NAME = new ConfigurationKey("artifactParameterName", Protocol.CAS2.getArtifactParameterName()); + ConfigurationKey SERVER_NAME = new ConfigurationKey("serverName", null); + ConfigurationKey SERVICE = new ConfigurationKey("service"); + ConfigurationKey RENEW = new ConfigurationKey("renew", Boolean.FALSE); + ConfigurationKey LOGOUT_PARAMETER_NAME = new ConfigurationKey("logoutParameterName", "logoutRequest"); + ConfigurationKey ARTIFACT_PARAMETER_OVER_POST = new ConfigurationKey("artifactParameterOverPost", Boolean.FALSE); + ConfigurationKey EAGERLY_CREATE_SESSIONS = new ConfigurationKey("eagerlyCreateSessions", Boolean.TRUE); + ConfigurationKey ENCODE_SERVICE_URL = new ConfigurationKey("encodeServiceUrl", Boolean.TRUE); + ConfigurationKey SSL_CONFIG_FILE = new ConfigurationKey("sslConfigFile", null); + ConfigurationKey ROLE_ATTRIBUTE = new ConfigurationKey("roleAttribute", null); + ConfigurationKey IGNORE_CASE = new ConfigurationKey("ignoreCase", Boolean.FALSE); + ConfigurationKey CAS_SERVER_LOGIN_URL = new ConfigurationKey("casServerLoginUrl", null); + ConfigurationKey GATEWAY = new ConfigurationKey("gateway", Boolean.FALSE); + ConfigurationKey> AUTHENTICATION_REDIRECT_STRATEGY_CLASS = new ConfigurationKey>("authenticationRedirectStrategyClass", null); + ConfigurationKey> GATEWAY_STORAGE_CLASS = new ConfigurationKey>("gatewayStorageClass", DefaultGatewayResolverImpl.class); + ConfigurationKey CAS_SERVER_URL_PREFIX = new ConfigurationKey("casServerUrlPrefix", null); + ConfigurationKey ENCODING = new ConfigurationKey("encoding", null); + ConfigurationKey TOLERANCE = new ConfigurationKey("tolerance", 1000L); + + /** + * @deprecated As of 3.4. This constant is not used by the client and will + * be removed in future versions. + */ + @Deprecated + ConfigurationKey DISABLE_XML_SCHEMA_VALIDATION = new ConfigurationKey("disableXmlSchemaValidation", Boolean.FALSE); + ConfigurationKey IGNORE_PATTERN = new ConfigurationKey("ignorePattern", null); + ConfigurationKey IGNORE_URL_PATTERN_TYPE = new ConfigurationKey("ignoreUrlPatternType", "REGEX"); + ConfigurationKey> HOSTNAME_VERIFIER = new ConfigurationKey>("hostnameVerifier", null); + ConfigurationKey HOSTNAME_VERIFIER_CONFIG = new ConfigurationKey("hostnameVerifierConfig", null); + ConfigurationKey EXCEPTION_ON_VALIDATION_FAILURE = new ConfigurationKey("exceptionOnValidationFailure", Boolean.TRUE); + ConfigurationKey REDIRECT_AFTER_VALIDATION = new ConfigurationKey("redirectAfterValidation", Boolean.TRUE); + ConfigurationKey USE_SESSION = new ConfigurationKey("useSession", Boolean.TRUE); + ConfigurationKey SECRET_KEY = new ConfigurationKey("secretKey", null); + ConfigurationKey CIPHER_ALGORITHM = new ConfigurationKey("cipherAlgorithm", "DESede"); + ConfigurationKey PROXY_RECEPTOR_URL = new ConfigurationKey("proxyReceptorUrl", null); + ConfigurationKey> PROXY_GRANTING_TICKET_STORAGE_CLASS = new ConfigurationKey>("proxyGrantingTicketStorageClass", ProxyGrantingTicketStorageImpl.class); + ConfigurationKey MILLIS_BETWEEN_CLEAN_UPS = new ConfigurationKey("millisBetweenCleanUps", 60000); + ConfigurationKey ACCEPT_ANY_PROXY = new ConfigurationKey("acceptAnyProxy", Boolean.FALSE); + ConfigurationKey ALLOWED_PROXY_CHAINS = new ConfigurationKey("allowedProxyChains", null); + ConfigurationKey> TICKET_VALIDATOR_CLASS = new ConfigurationKey>("ticketValidatorClass", null); + ConfigurationKey PROXY_CALLBACK_URL = new ConfigurationKey("proxyCallbackUrl", null); + ConfigurationKey FRONT_LOGOUT_PARAMETER_NAME = new ConfigurationKey("frontLogoutParameterName", "SAMLRequest"); + ConfigurationKey RELAY_STATE_PARAMETER_NAME = new ConfigurationKey("relayStateParameterName", "RelayState"); +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationStrategy.java b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationStrategy.java new file mode 100644 index 000000000..1493d7230 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationStrategy.java @@ -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 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 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 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 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. + */ + Class getClass(ConfigurationKey> 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 filterClazz); +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationStrategyName.java b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationStrategyName.java new file mode 100644 index 000000000..2d3146f0b --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/ConfigurationStrategyName.java @@ -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 + * web.xml 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 configurationStrategyClass; + + private ConfigurationStrategyName(final Class 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 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) 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; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/JndiConfigurationStrategyImpl.java b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/JndiConfigurationStrategyImpl.java new file mode 100644 index 000000000..6fcaa110b --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/JndiConfigurationStrategyImpl.java @@ -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 defaultValue 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 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); + } + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/LegacyConfigurationStrategyImpl.java b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/LegacyConfigurationStrategyImpl.java new file mode 100644 index 000000000..a9af77ca6 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/LegacyConfigurationStrategyImpl.java @@ -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 defaultValue. + * + * @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 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); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/PropertiesConfigurationStrategyImpl.java b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/PropertiesConfigurationStrategyImpl.java new file mode 100644 index 000000000..5dd7dd9ae --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/PropertiesConfigurationStrategyImpl.java @@ -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 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); + } + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/SystemPropertiesConfigurationStrategyImpl.java b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/SystemPropertiesConfigurationStrategyImpl.java new file mode 100644 index 000000000..49be2c5f0 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/SystemPropertiesConfigurationStrategyImpl.java @@ -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 filterClazz) { + } + + @Override + protected String get(ConfigurationKey configurationKey) { + return System.getProperty(configurationKey.getName()); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/configuration/WebXmlConfigurationStrategyImpl.java b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/WebXmlConfigurationStrategyImpl.java new file mode 100644 index 000000000..634b9f16e --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/configuration/WebXmlConfigurationStrategyImpl.java @@ -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 defaultValue. + * + * @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 clazz) { + this.filterConfig = filterConfig; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/jaas/AssertionPrincipal.java b/cas-client-core/src/main/java/org/jasig/cas/client/jaas/AssertionPrincipal.java index 6abeff65b..8ae58947c 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/jaas/AssertionPrincipal.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/jaas/AssertionPrincipal.java @@ -1,26 +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: + * 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 + * 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 + * 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.jaas; import java.io.Serializable; - import org.jasig.cas.client.authentication.SimplePrincipal; import org.jasig.cas.client.validation.Assertion; @@ -33,7 +31,7 @@ * */ public class AssertionPrincipal extends SimplePrincipal implements Serializable { - + /** AssertionPrincipal.java */ private static final long serialVersionUID = 2288520214366461693L; diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/jaas/CasLoginModule.java b/cas-client-core/src/main/java/org/jasig/cas/client/jaas/CasLoginModule.java index 58a3e0e36..5deb87e81 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/jaas/CasLoginModule.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/jaas/CasLoginModule.java @@ -1,22 +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: + * 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 + * 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 + * 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.jaas; import java.beans.BeanInfo; @@ -27,26 +26,19 @@ import java.security.Principal; import java.security.acl.Group; import java.util.*; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; - +import java.util.concurrent.TimeUnit; import javax.security.auth.Subject; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.callback.*; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.jasig.cas.client.authentication.SimpleGroup; import org.jasig.cas.client.authentication.SimplePrincipal; import org.jasig.cas.client.util.CommonUtils; import org.jasig.cas.client.util.ReflectUtils; import org.jasig.cas.client.validation.Assertion; import org.jasig.cas.client.validation.TicketValidator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * JAAS login module that delegates to a CAS {@link TicketValidator} component @@ -79,6 +71,8 @@ * for JAAS providers that attempt to periodically reauthenticate to renew principal. * Since CAS tickets are one-time-use, a cached assertion must be provided on reauthentication. *

  • cacheTimeout (optional) - Assertion cache timeout in minutes.
  • + *
  • cacheTimeoutUnit (optional) - Assertion cache timeout unit. Must be one of {@link TimeUnit} enumeration + * names, e.g. DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS. Default unit is MINUTES.
  • * * *

    @@ -109,14 +103,14 @@ public class CasLoginModule implements LoginModule { /** Constant for login name stored in shared state. */ public static final String LOGIN_NAME = "javax.security.auth.login.name"; - + /** * Default group name for storing caller principal. * The default value supports JBoss, but is configurable to hopefully * support other JEE containers. */ public static final String DEFAULT_PRINCIPAL_GROUP_NAME = "CallerPrincipal"; - + /** * Default group name for storing role membership data. * The default value supports JBoss, but is configurable to hopefully @@ -129,50 +123,50 @@ public class CasLoginModule implements LoginModule { */ public static final int DEFAULT_CACHE_TIMEOUT = 480; + /** Default assertion cache timeout unit is minutes. */ + public static final TimeUnit DEFAULT_CACHE_TIMEOUT_UNIT = TimeUnit.MINUTES; + /** * Stores mapping of ticket to assertion to support JAAS providers that * attempt to periodically re-authenticate to renew principal. Since * CAS tickets are one-time-use, a cached assertion must be provided on * re-authentication. */ - protected static final Map ASSERTION_CACHE = new HashMap(); + protected static final Map ASSERTION_CACHE = new HashMap(); - /** Executor responsible for assertion cache cleanup */ - protected static Executor cacheCleanerExecutor = Executors.newSingleThreadExecutor(); - /** Logger instance */ - protected final Log log = LogFactory.getLog(getClass()); - + protected final Logger logger = LoggerFactory.getLogger(getClass()); + /** JAAS authentication subject */ protected Subject subject; - + /** JAAS callback handler */ protected CallbackHandler callbackHandler; - + /** CAS ticket validator */ protected TicketValidator ticketValidator; - + /** CAS service parameter used if no service is provided via TextCallback on login */ protected String service; - + /** CAS assertion */ protected Assertion assertion; - + /** CAS ticket credential */ protected TicketCredential ticket; - + /** Login module shared state */ - protected Map sharedState; - + protected Map sharedState; + /** Roles to be added to all authenticated principals by default */ protected String[] defaultRoles; - + /** Names of attributes in the CAS assertion that should be used for role data */ protected Set roleAttributeNames = new HashSet(); - + /** Name of JAAS Group containing caller principal */ protected String principalGroupName = DEFAULT_PRINCIPAL_GROUP_NAME; - + /** Name of JAAS Group containing role data */ protected String roleGroupName = DEFAULT_ROLE_GROUP_NAME; @@ -182,8 +176,12 @@ public class CasLoginModule implements LoginModule { /** Assertion cache timeout in minutes */ protected int cacheTimeout = DEFAULT_CACHE_TIMEOUT; + /** Units of cache timeout. */ + protected TimeUnit cacheTimeoutUnit = DEFAULT_CACHE_TIMEOUT_UNIT; + /** * Initializes the CAS login module. + * * @param subject Authentication subject. * @param handler Callback handler. * @param state Shared state map. @@ -201,55 +199,60 @@ public class CasLoginModule implements LoginModule { * which by default are single use, reauthentication fails. Assertion caching addresses this * behavior. *

  • cacheTimeout (optional) - assertion cache timeout in minutes.
  • + *
  • cacheTimeoutUnit (optional) - Assertion cache timeout unit. Must be one of {@link TimeUnit} enumeration + * names, e.g. DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS. Default unit is MINUTES.
  • * */ + public final void initialize(final Subject subject, final CallbackHandler handler, final Map state, + final Map options) { - - public final void initialize(final Subject subject, final CallbackHandler handler, final Map state, final Map options) { this.assertion = null; this.callbackHandler = handler; this.subject = subject; - this.sharedState = (Map) state; + this.sharedState = (Map) state; this.sharedState = new HashMap(state); - + String ticketValidatorClass = null; for (final String key : options.keySet()) { - log.trace("Processing option " + key); + logger.trace("Processing option {}", key); if ("service".equals(key)) { this.service = (String) options.get(key); - log.debug("Set service=" + this.service); + logger.debug("Set service={}", this.service); } else if ("ticketValidatorClass".equals(key)) { ticketValidatorClass = (String) options.get(key); - log.debug("Set ticketValidatorClass=" + ticketValidatorClass); + logger.debug("Set ticketValidatorClass={}", ticketValidatorClass); } else if ("defaultRoles".equals(key)) { final String roles = (String) options.get(key); - log.trace("Got defaultRoles value " + roles); + logger.trace("Got defaultRoles value {}", roles); this.defaultRoles = roles.split(",\\s*"); - log.debug("Set defaultRoles=" + Arrays.asList(this.defaultRoles)); + logger.debug("Set defaultRoles={}", Arrays.asList(this.defaultRoles)); } else if ("roleAttributeNames".equals(key)) { final String attrNames = (String) options.get(key); - log.trace("Got roleAttributeNames value " + attrNames); + logger.trace("Got roleAttributeNames value {}", attrNames); final String[] attributes = attrNames.split(",\\s*"); this.roleAttributeNames.addAll(Arrays.asList(attributes)); - log.debug("Set roleAttributeNames=" + this.roleAttributeNames); + logger.debug("Set roleAttributeNames={}", this.roleAttributeNames); } else if ("principalGroupName".equals(key)) { this.principalGroupName = (String) options.get(key); - log.debug("Set principalGroupName=" + this.principalGroupName); + logger.debug("Set principalGroupName={}", this.principalGroupName); } else if ("roleGroupName".equals(key)) { this.roleGroupName = (String) options.get(key); - log.debug("Set roleGroupName=" + this.roleGroupName); + logger.debug("Set roleGroupName={}", this.roleGroupName); } else if ("cacheAssertions".equals(key)) { this.cacheAssertions = Boolean.parseBoolean((String) options.get(key)); - log.debug("Set cacheAssertions=" + this.cacheAssertions); + logger.debug("Set cacheAssertions={}", this.cacheAssertions); } else if ("cacheTimeout".equals(key)) { this.cacheTimeout = Integer.parseInt((String) options.get(key)); - log.debug("Set cacheTimeout=" + this.cacheTimeout); + logger.debug("Set cacheTimeout={}", this.cacheTimeout); + } else if ("cacheTimeoutUnit".equals(key)) { + this.cacheTimeoutUnit = Enum.valueOf(TimeUnit.class, (String) options.get(key)); + logger.debug("Set cacheTimeoutUnit={}", this.cacheTimeoutUnit); } } if (this.cacheAssertions) { - cacheCleanerExecutor.execute(new CacheCleaner()); + cleanCache(); } CommonUtils.assertNotNull(ticketValidatorClass, "ticketValidatorClass is required."); @@ -275,10 +278,10 @@ protected void postLogin(final boolean result) { } public final boolean login() throws LoginException { - log.debug("Performing login."); + logger.debug("Performing login."); if (!preLogin()) { - log.debug("preLogin failed."); + logger.debug("preLogin failed."); return false; } @@ -289,46 +292,46 @@ public final boolean login() throws LoginException { try { this.callbackHandler.handle(new Callback[] { ticketCallback, serviceCallback }); } catch (final IOException e) { - log.info("Login failed due to IO exception in callback handler: " + e); + logger.info("Login failed due to IO exception in callback handler: {}", e); throw (LoginException) new LoginException("IO exception in callback handler: " + e).initCause(e); } catch (final UnsupportedCallbackException e) { - log.info("Login failed due to unsupported callback: " + e); - throw (LoginException) new LoginException("Callback handler does not support PasswordCallback and TextInputCallback.").initCause(e); + logger.info("Login failed due to unsupported callback: {}", e); + throw (LoginException) new LoginException( + "Callback handler does not support PasswordCallback and TextInputCallback.").initCause(e); } if (ticketCallback.getPassword() != null) { this.ticket = new TicketCredential(new String(ticketCallback.getPassword())); - final String service = CommonUtils.isNotBlank(serviceCallback.getName()) ? serviceCallback.getName() : this.service; + final String service = CommonUtils.isNotBlank(serviceCallback.getName()) ? serviceCallback.getName() + : this.service; if (this.cacheAssertions) { - synchronized(ASSERTION_CACHE) { - if (ASSERTION_CACHE.get(ticket) != null) { - log.debug("Assertion found in cache."); - this.assertion = ASSERTION_CACHE.get(ticket); - } + this.assertion = ASSERTION_CACHE.get(ticket); + if (this.assertion != null) { + logger.debug("Assertion found in cache."); } } if (this.assertion == null) { - log.debug("CAS assertion is null; ticket validation required."); + logger.debug("CAS assertion is null; ticket validation required."); if (CommonUtils.isBlank(service)) { - log.info("Login failed because required CAS service parameter not provided."); - throw new LoginException("Neither login module nor callback handler provided required service parameter."); + logger.info("Login failed because required CAS service parameter not provided."); + throw new LoginException( + "Neither login module nor callback handler provided required service parameter."); } try { - if (log.isDebugEnabled()) { - log.debug("Attempting ticket validation with service=" + service + " and ticket=" + ticket); - } + logger.debug("Attempting ticket validation with service={} and ticket={}", service, + this.ticket); this.assertion = this.ticketValidator.validate(this.ticket.getName(), service); } catch (final Exception e) { - log.info("Login failed due to CAS ticket validation failure: " + e); + logger.info("Login failed due to CAS ticket validation failure: {}", e); throw (LoginException) new LoginException("CAS ticket validation failed: " + e).initCause(e); } } - log.info("Login succeeded."); + logger.info("Login succeeded."); } else { - log.info("Login failed because callback handler did not provide CAS ticket."); + logger.info("Login failed because callback handler did not provide CAS ticket."); throw new LoginException("Callback handler did not provide CAS ticket."); } result = true; @@ -380,7 +383,8 @@ public final boolean commit() throws LoginException { throw new LoginException("Ticket credential not found."); } - final AssertionPrincipal casPrincipal = new AssertionPrincipal(this.assertion.getPrincipal().getName(), this.assertion); + final AssertionPrincipal casPrincipal = new AssertionPrincipal(this.assertion.getPrincipal().getName(), + this.assertion); this.subject.getPrincipals().add(casPrincipal); // Add group containing principal as sole member @@ -396,7 +400,7 @@ public final boolean commit() throws LoginException { roleGroup.addMember(new SimplePrincipal(defaultRole)); } - final Map attributes = this.assertion.getPrincipal().getAttributes(); + final Map attributes = this.assertion.getPrincipal().getAttributes(); for (final String key : attributes.keySet()) { if (this.roleAttributeNames.contains(key)) { // Attribute value is Object if singular or Collection if plural @@ -415,16 +419,10 @@ public final boolean commit() throws LoginException { // Place principal name in shared state for downstream JAAS modules (module chaining use case) this.sharedState.put(LOGIN_NAME, assertion.getPrincipal().getName()); - if (log.isDebugEnabled()) { - if (log.isDebugEnabled()) { - log.debug("Created JAAS subject with principals: " + subject.getPrincipals()); - } - } + logger.debug("Created JAAS subject with principals: {}", subject.getPrincipals()); if (this.cacheAssertions) { - if (log.isDebugEnabled()) { - log.debug("Caching assertion for principal " + this.assertion.getPrincipal()); - } + logger.debug("Caching assertion for principal {}", this.assertion.getPrincipal()); ASSERTION_CACHE.put(this.ticket, this.assertion); } } else { @@ -442,21 +440,29 @@ public final boolean commit() throws LoginException { } public final boolean logout() throws LoginException { - log.debug("Performing logout."); + logger.debug("Performing logout."); if (!preLogout()) { return false; } + // Remove cache entry if assertion caching is enabled + if (this.cacheAssertions) { + for (final TicketCredential ticket : this.subject.getPrivateCredentials(TicketCredential.class)) { + logger.debug("Removing cached assertion for {}", ticket); + ASSERTION_CACHE.remove(ticket); + } + } + // Remove all CAS principals removePrincipalsOfType(AssertionPrincipal.class); removePrincipalsOfType(SimplePrincipal.class); removePrincipalsOfType(SimpleGroup.class); - + // Remove all CAS credentials removeCredentialsOfType(TicketCredential.class); - log.info("Logout succeeded."); + logger.info("Logout succeeded."); postLogout(); return true; @@ -478,39 +484,40 @@ protected void postLogout() { // template method } - /** * Creates a {@link TicketValidator} instance from a class name and map of property name/value pairs. * @param className Fully-qualified name of {@link TicketValidator} concrete class. * @param propertyMap Map of property name/value pairs to set on validator instance. * @return Ticket validator with properties set. */ - private TicketValidator createTicketValidator(final String className, final Map propertyMap) { - CommonUtils.assertTrue(propertyMap.containsKey("casServerUrlPrefix"), "Required property casServerUrlPrefix not found."); + private TicketValidator createTicketValidator(final String className, final Map propertyMap) { + CommonUtils.assertTrue(propertyMap.containsKey("casServerUrlPrefix"), + "Required property casServerUrlPrefix not found."); final Class validatorClass = ReflectUtils.loadClass(className); - final TicketValidator validator = ReflectUtils.newInstance(validatorClass, propertyMap.get("casServerUrlPrefix")); + final TicketValidator validator = ReflectUtils.newInstance(validatorClass, + propertyMap.get("casServerUrlPrefix")); try { final BeanInfo info = Introspector.getBeanInfo(validatorClass); for (final String property : propertyMap.keySet()) { if (!"casServerUrlPrefix".equals(property)) { - log.debug("Attempting to set TicketValidator property " + property); + logger.debug("Attempting to set TicketValidator property {}", property); final String value = (String) propertyMap.get(property); final PropertyDescriptor pd = ReflectUtils.getPropertyDescriptor(info, property); if (pd != null) { - ReflectUtils.setProperty(property, convertIfNecessary(pd, value), validator, info); - log.debug("Set " + property + "=" + value); + ReflectUtils.setProperty(property, convertIfNecessary(pd, value), validator, info); + logger.debug("Set {} = {}", property, value); } else { - log.warn("Cannot find property " + property + " on " + className); + logger.warn("Cannot find property {} on {}", property, className); } } } } catch (final IntrospectionException e) { throw new RuntimeException("Error getting bean info for " + validatorClass, e); } - + return validator; } @@ -534,7 +541,8 @@ private static Object convertIfNecessary(final PropertyDescriptor pd, final Stri } else if (long.class.equals(pd.getPropertyType())) { return new Long(value); } else { - throw new IllegalArgumentException("No conversion strategy exists for property " + pd.getName() + " of type " + pd.getPropertyType()); + throw new IllegalArgumentException("No conversion strategy exists for property " + pd.getName() + + " of type " + pd.getPropertyType()); } } @@ -554,26 +562,21 @@ private void removeCredentialsOfType(final Class clazz) { this.subject.getPrivateCredentials().removeAll(this.subject.getPrivateCredentials(clazz)); } - /** Removes expired entries from the assertion cache. */ - private class CacheCleaner implements Runnable { - public void run() { - if (log.isDebugEnabled()) { - log.debug("Cleaning assertion cache of size " + CasLoginModule.ASSERTION_CACHE.size()); - } - final Iterator> iter = - CasLoginModule.ASSERTION_CACHE.entrySet().iterator(); - final Calendar cutoff = Calendar.getInstance(); - cutoff.add(Calendar.MINUTE, -CasLoginModule.this.cacheTimeout); - while (iter.hasNext()) { - final Assertion assertion = iter.next().getValue(); - final Calendar created = Calendar.getInstance(); - created.setTime(assertion.getValidFromDate()); - if (created.before(cutoff)) { - if (log.isDebugEnabled()) { - log.debug("Removing expired assertion for principal " + assertion.getPrincipal()); - } - iter.remove(); - } + /** + * Removes expired entries from the assertion cache. + */ + private void cleanCache() { + logger.debug("Cleaning assertion cache of size {}", ASSERTION_CACHE.size()); + final Iterator> iter = ASSERTION_CACHE.entrySet().iterator(); + final Calendar cutoff = Calendar.getInstance(); + cutoff.setTimeInMillis(System.currentTimeMillis() - this.cacheTimeoutUnit.toMillis(this.cacheTimeout)); + while (iter.hasNext()) { + final Assertion assertion = iter.next().getValue(); + final Calendar created = Calendar.getInstance(); + created.setTime(assertion.getValidFromDate()); + if (created.before(cutoff)) { + logger.debug("Removing expired assertion for principal {}", assertion.getPrincipal()); + iter.remove(); } } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/jaas/ServiceAndTicketCallbackHandler.java b/cas-client-core/src/main/java/org/jasig/cas/client/jaas/ServiceAndTicketCallbackHandler.java index 62bced724..7fc88ef4f 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/jaas/ServiceAndTicketCallbackHandler.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/jaas/ServiceAndTicketCallbackHandler.java @@ -1,31 +1,25 @@ -/** +/* * 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: + * 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 + * 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 + * 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.jaas; import java.io.IOException; - -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.callback.*; /** * Callback handler that provides the CAS service and ticket to a @@ -41,10 +35,10 @@ public class ServiceAndTicketCallbackHandler implements CallbackHandler { /** CAS service URL */ private final String service; - + /** CAS service ticket */ private final String ticket; - + /** * Creates a new instance with the given service and ticket. * diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/jaas/Servlet3AuthenticationFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/jaas/Servlet3AuthenticationFilter.java new file mode 100644 index 000000000..e64b57897 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/jaas/Servlet3AuthenticationFilter.java @@ -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.jaas; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +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.util.AbstractCasFilter; +import org.jasig.cas.client.util.CommonUtils; + +/** + * Servlet filter performs a programmatic JAAS login using the Servlet 3.0 HttpServletRequest#login() facility. + * This component should be compatible with any servlet container that supports the Servlet 3.0/JEE6 specification. + *

    + * The filter executes when it receives a CAS ticket and expects the + * {@link CasLoginModule} JAAS module to perform the CAS + * ticket validation in order to produce an {@link org.jasig.cas.client.jaas.AssertionPrincipal} from which + * the CAS assertion is obtained and inserted into the session to enable SSO. + *

    + * If a service init-param is specified for this filter, it supersedes + * the service defined for the {@link CasLoginModule}. + * + * @author Daniel Fisher + * @author Marvin S. Addison + * @since 3.3 + */ +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, getProtocol().getArtifactParameterName()); + + if (session != null && session.getAttribute(CONST_CAS_ASSERTION) == null && ticket != null) { + try { + final String service = constructServiceUrl(request, response); + logger.debug("Attempting CAS ticket validation with service={} and ticket={}", service, ticket); + request.login(service, ticket); + if (request.getUserPrincipal() instanceof AssertionPrincipal) { + final AssertionPrincipal principal = (AssertionPrincipal) request.getUserPrincipal(); + logger.debug("Installing CAS assertion into session."); + request.getSession().setAttribute(CONST_CAS_ASSERTION, principal.getAssertion()); + } else { + logger.debug("Aborting -- principal is not of type AssertionPrincipal"); + throw new GeneralSecurityException("JAAS authentication did not produce CAS AssertionPrincipal."); + } + } catch (final ServletException e) { + logger.debug("JAAS authentication failed."); + response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); + } catch (final GeneralSecurityException e) { + response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); + } + } else if (session != null && request.getUserPrincipal() == null) { + // There is evidence that in some cases the principal can disappear + // in JBoss despite a valid session. + // This block forces consistency between principal and assertion. + logger.info("User principal not found. Removing CAS assertion from session to force re-authentication."); + session.removeAttribute(CONST_CAS_ASSERTION); + } + chain.doFilter(request, response); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/jaas/TicketCredential.java b/cas-client-core/src/main/java/org/jasig/cas/client/jaas/TicketCredential.java index 2f7e92e5f..621da53fd 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/jaas/TicketCredential.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/jaas/TicketCredential.java @@ -1,22 +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: + * 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 + * 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 + * 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.jaas; import java.security.Principal; @@ -33,7 +32,7 @@ public final class TicketCredential implements Principal { /** Hash code seed value */ private static final int HASHCODE_SEED = 17; - + /** Ticket ID string */ private String ticket; @@ -54,12 +53,15 @@ public String toString() { } public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; final TicketCredential that = (TicketCredential) o; - if (ticket != null ? !ticket.equals(that.ticket) : that.ticket != null) return false; + if (ticket != null ? !ticket.equals(that.ticket) : that.ticket != null) + return false; return true; } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/AbstractEncryptedProxyGrantingTicketStorageImpl.java b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/AbstractEncryptedProxyGrantingTicketStorageImpl.java index 7842ac33d..24826cfc3 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/AbstractEncryptedProxyGrantingTicketStorageImpl.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/AbstractEncryptedProxyGrantingTicketStorageImpl.java @@ -1,12 +1,32 @@ +/* + * 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.proxy; -import javax.crypto.Cipher; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.DESedeKeySpec; +import org.jasig.cas.client.configuration.ConfigurationKeys; + import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; +import javax.crypto.Cipher; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESedeKeySpec; /** * Provides encryption capabilities. Not entirely safe to configure since we have no way of controlling the @@ -18,13 +38,12 @@ */ 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 { + public final void setSecretKey(final String key) throws NoSuchAlgorithmException, InvalidKeyException, + InvalidKeySpecException { this.key = SecretKeyFactory.getInstance(this.cipherAlgorithm).generateSecret(new DESedeKeySpec(key.getBytes())); } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/Cas20ProxyRetriever.java b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/Cas20ProxyRetriever.java index 45adbaed4..e80304a29 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/Cas20ProxyRetriever.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/Cas20ProxyRetriever.java @@ -1,54 +1,49 @@ -/** +/* * 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: + * 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 + * 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 + * 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.proxy; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import java.net.URL; +import java.net.URLEncoder; +import org.jasig.cas.client.ssl.HttpURLConnectionFactory; import org.jasig.cas.client.util.CommonUtils; import org.jasig.cas.client.util.XmlUtils; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Implementation of a ProxyRetriever that follows the CAS 2.0 specification. * For more information on the CAS 2.0 specification, please see the specification + * href="http://www.jasig.org/cas/protocol">specification * document. *

    * In general, this class will make a call to the CAS server with some specified * parameters and receive an XML response to parse. * * @author Scott Battaglia - * @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $ * @since 3.0 */ public final class Cas20ProxyRetriever implements ProxyRetriever { /** Unique Id for serialization. */ - private static final long serialVersionUID = 560409469568911791L; + private static final long serialVersionUID = 560409469568911792L; - /** - * Instance of Commons Logging. - */ - private final Log log = LogFactory.getLog(this.getClass()); + private static final Logger logger = LoggerFactory.getLogger(Cas20ProxyRetriever.class); /** * Url to CAS server. @@ -57,39 +52,56 @@ public final class Cas20ProxyRetriever implements ProxyRetriever { private final String encoding; + /** 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. * * @param casServerUrl the URL to the CAS server (i.e. http://localhost/cas/) * @param encoding the encoding to use. + * @param urlFactory url connection factory use when retrieving proxy responses from the server */ - public Cas20ProxyRetriever(final String casServerUrl, final String encoding) { + public Cas20ProxyRetriever(final String casServerUrl, final String encoding, + final HttpURLConnectionFactory urlFactory) { CommonUtils.assertNotNull(casServerUrl, "casServerUrl cannot be null."); this.casServerUrl = casServerUrl; this.encoding = encoding; + this.urlConnectionFactory = urlFactory; } - public String getProxyTicketIdFor(final String proxyGrantingTicketId, - final String targetService) { + public String getProxyTicketIdFor(final String proxyGrantingTicketId, final String targetService) { + CommonUtils.assertNotNull(proxyGrantingTicketId, "proxyGrantingTicketId cannot be null."); + CommonUtils.assertNotNull(targetService, "targetService cannot be null."); + + final URL url = constructUrl(proxyGrantingTicketId, targetService); + final String response; - final String url = constructUrl(proxyGrantingTicketId, targetService); - final String response = CommonUtils.getResponseFromServer(url, this.encoding); + 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)) { - log.debug(error); + logger.debug(error); return null; } return XmlUtils.getTextForElement(response, "proxyTicket"); } - private String constructUrl(final String proxyGrantingTicketId, final String targetService) { + private URL constructUrl(final String proxyGrantingTicketId, final String targetService) { try { - return this.casServerUrl + (this.casServerUrl.endsWith("/") ? "" : "/") + "proxy" + "?pgt=" - + proxyGrantingTicketId + "&targetService=" - + URLEncoder.encode(targetService, "UTF-8"); - } catch (final UnsupportedEncodingException e) { + return new URL(this.casServerUrl + (this.casServerUrl.endsWith("/") ? "" : "/") + "proxy" + "?pgt=" + + proxyGrantingTicketId + "&targetService=" + URLEncoder.encode(targetService, "UTF-8")); + } catch (final Exception e) { throw new RuntimeException(e); } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/CleanUpTimerTask.java b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/CleanUpTimerTask.java index e48986c4e..ce31b4b24 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/CleanUpTimerTask.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/CleanUpTimerTask.java @@ -1,22 +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: + * 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 + * 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 + * 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.proxy; import java.util.TimerTask; @@ -39,6 +38,7 @@ public final class CleanUpTimerTask extends TimerTask { public CleanUpTimerTask(final ProxyGrantingTicketStorage proxyGrantingTicketStorage) { this.proxyGrantingTicketStorage = proxyGrantingTicketStorage; } + public void run() { this.proxyGrantingTicketStorage.cleanUp(); } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorage.java b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorage.java index d4bf58ae9..0dc15c611 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorage.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorage.java @@ -1,22 +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: + * 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 + * 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 + * 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.proxy; /** @@ -47,7 +46,7 @@ public interface ProxyGrantingTicketStorage { * @return the ProxyGrantingTicket Id or null if it can't be found */ public String retrieve(String proxyGrantingTicketIou); - + /** * Called on a regular basis by an external timer, * giving implementations a chance to remove stale data. diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorageImpl.java b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorageImpl.java index a1d13941e..727eedef5 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorageImpl.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorageImpl.java @@ -1,30 +1,29 @@ -/** +/* * 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: + * 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 + * 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 + * 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.proxy; -import java.util.*; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.jasig.cas.client.util.CommonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Implementation of {@link ProxyGrantingTicketStorage} that is backed by a @@ -39,8 +38,8 @@ * @since 3.0 */ public final class ProxyGrantingTicketStorageImpl implements ProxyGrantingTicketStorage { - - private final Log log = LogFactory.getLog(getClass()); + + private final Logger logger = LoggerFactory.getLogger(getClass()); /** * Default timeout in milliseconds. @@ -50,7 +49,7 @@ public final class ProxyGrantingTicketStorageImpl implements ProxyGrantingTicket /** * Map that stores the PGTIOU to PGT mappings. */ - private final ConcurrentMap cache = new ConcurrentHashMap(); + private final ConcurrentMap cache = new ConcurrentHashMap(); /** * time, in milliseconds, before a {@link ProxyGrantingTicketHolder} @@ -58,7 +57,7 @@ public final class ProxyGrantingTicketStorageImpl implements ProxyGrantingTicket * * @see ProxyGrantingTicketStorageImpl#DEFAULT_TIMEOUT */ - private long timeout; + private long timeout; /** * Constructor set the timeout to the default value. @@ -74,7 +73,7 @@ public ProxyGrantingTicketStorageImpl() { * @param timeout the time to hold on to the ProxyGrantingTicket */ public ProxyGrantingTicketStorageImpl(final long timeout) { - this.timeout = timeout; + this.timeout = timeout; } /** @@ -82,27 +81,28 @@ public ProxyGrantingTicketStorageImpl(final long timeout) { * Its removed after retrieval. */ public String retrieve(final String proxyGrantingTicketIou) { + if (CommonUtils.isBlank(proxyGrantingTicketIou)) { + return null; + } + final ProxyGrantingTicketHolder holder = this.cache.get(proxyGrantingTicketIou); if (holder == null) { - log.info("No Proxy Ticket found for [" + proxyGrantingTicketIou + "]."); + logger.info("No Proxy Ticket found for [{}].", proxyGrantingTicketIou); return null; } this.cache.remove(proxyGrantingTicketIou); - if (log.isDebugEnabled()) { - log.debug("Returned ProxyGrantingTicket of [" + holder.getProxyGrantingTicket() + "]"); - } + logger.debug("Returned ProxyGrantingTicket of [{}]", holder.getProxyGrantingTicket()); return holder.getProxyGrantingTicket(); } public void save(final String proxyGrantingTicketIou, final String proxyGrantingTicket) { final ProxyGrantingTicketHolder holder = new ProxyGrantingTicketHolder(proxyGrantingTicket); - if (log.isDebugEnabled()) { - log.debug("Saving ProxyGrantingTicketIOU and ProxyGrantingTicket combo: [" + proxyGrantingTicketIou + ", " + proxyGrantingTicket + "]"); - } + logger.debug("Saving ProxyGrantingTicketIOU and ProxyGrantingTicket combo: [{}, {}]", proxyGrantingTicketIou, + proxyGrantingTicket); this.cache.put(proxyGrantingTicketIou, holder); } @@ -111,13 +111,13 @@ public void save(final String proxyGrantingTicketIou, final String proxyGranting * called regularly via an external thread or timer. */ public void cleanUp() { - for (final Map.Entry holder : this.cache.entrySet()) { + for (final Map.Entry holder : this.cache.entrySet()) { if (holder.getValue().isExpired(this.timeout)) { this.cache.remove(holder.getKey()); } } } - + private static final class ProxyGrantingTicketHolder { private final String proxyGrantingTicket; diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyRetriever.java b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyRetriever.java index 14c006f7b..07e01a48d 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyRetriever.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/ProxyRetriever.java @@ -1,22 +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: + * 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 + * 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 + * 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.proxy; import java.io.Serializable; @@ -38,6 +37,5 @@ public interface ProxyRetriever extends Serializable { * @param targetService the service we want to proxy. * @return the ProxyTicket Id if Granted, null otherwise. */ - String getProxyTicketIdFor(String proxyGrantingTicketId, - String targetService); + String getProxyTicketIdFor(String proxyGrantingTicketId, String targetService); } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/package.html b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/package.html index fa0cb0635..17c3c9c5c 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/proxy/package.html +++ b/cas-client-core/src/main/java/org/jasig/cas/client/proxy/package.html @@ -5,20 +5,19 @@ 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: + 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 + 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 + 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. --> -

    The proxy package includes a servlet to act as a proxy receptor, diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/session/HashMapBackedSessionMappingStorage.java b/cas-client-core/src/main/java/org/jasig/cas/client/session/HashMapBackedSessionMappingStorage.java index c58e3a47b..27a6c6018 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/session/HashMapBackedSessionMappingStorage.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/session/HashMapBackedSessionMappingStorage.java @@ -1,31 +1,28 @@ -/** +/* * 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: + * 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 + * 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 + * 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 org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - import java.util.HashMap; import java.util.Map; - import javax.servlet.http.HttpSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * HashMap backed implementation of SessionMappingStorage. @@ -36,50 +33,48 @@ * */ public final class HashMapBackedSessionMappingStorage implements SessionMappingStorage { - + /** * Maps the ID from the CAS server to the Session. */ - private final Map MANAGED_SESSIONS = new HashMap(); + private final Map MANAGED_SESSIONS = new HashMap(); /** * Maps the Session ID to the key from the CAS Server. */ - private final Map ID_TO_SESSION_KEY_MAPPING = new HashMap(); + private final Map ID_TO_SESSION_KEY_MAPPING = new HashMap(); - private final Log log = LogFactory.getLog(getClass()); + private final Logger logger = LoggerFactory.getLogger(getClass()); - public synchronized void addSessionById(String mappingId, HttpSession session) { + public synchronized void addSessionById(String mappingId, HttpSession session) { ID_TO_SESSION_KEY_MAPPING.put(session.getId(), mappingId); MANAGED_SESSIONS.put(mappingId, session); - } + } - public synchronized void removeBySessionById(String sessionId) { - if (log.isDebugEnabled()) { - log.debug("Attempting to remove Session=[" + sessionId + "]"); - } + public synchronized void removeBySessionById(final String sessionId) { + logger.debug("Attempting to remove Session=[{}]", sessionId); final String key = ID_TO_SESSION_KEY_MAPPING.get(sessionId); - if (log.isDebugEnabled()) { + if (logger.isDebugEnabled()) { if (key != null) { - log.debug("Found mapping for session. Session Removed."); + logger.debug("Found mapping for session. Session Removed."); } else { - log.debug("No mapping for session found. Ignoring."); + logger.debug("No mapping for session found. Ignoring."); } } MANAGED_SESSIONS.remove(key); ID_TO_SESSION_KEY_MAPPING.remove(sessionId); - } + } - public synchronized HttpSession removeSessionByMappingId(String mappingId) { - final HttpSession session = MANAGED_SESSIONS.get(mappingId); + public synchronized HttpSession removeSessionByMappingId(String mappingId) { + final HttpSession session = MANAGED_SESSIONS.get(mappingId); if (session != null) { - removeBySessionById(session.getId()); + removeBySessionById(session.getId()); } return session; - } + } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/session/SessionMappingStorage.java b/cas-client-core/src/main/java/org/jasig/cas/client/session/SessionMappingStorage.java index 32e53a133..ab0dce4b0 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/session/SessionMappingStorage.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/session/SessionMappingStorage.java @@ -1,22 +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: + * 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 + * 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 + * 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 javax.servlet.http.HttpSession; @@ -30,26 +29,26 @@ * */ public interface SessionMappingStorage { - - /** - * Remove the HttpSession based on the mappingId. - * - * @param mappingId the id the session is keyed under. - * @return the HttpSession if it exists. - */ - HttpSession removeSessionByMappingId(String mappingId); - - /** - * Remove a session by its Id. - * @param sessionId the id of the session. - */ - void removeBySessionById(String sessionId); - - /** - * Add a session by its mapping Id. - * @param mappingId the id to map the session to. - * @param session the HttpSession. - */ - void addSessionById(String mappingId, HttpSession session); + + /** + * Remove the HttpSession based on the mappingId. + * + * @param mappingId the id the session is keyed under. + * @return the HttpSession if it exists. + */ + HttpSession removeSessionByMappingId(String mappingId); + + /** + * Remove a session by its Id. + * @param sessionId the id of the session. + */ + void removeBySessionById(String sessionId); + + /** + * Add a session by its mapping Id. + * @param mappingId the id to map the session to. + * @param session the HttpSession. + */ + void addSessionById(String mappingId, HttpSession session); } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutFilter.java index a9b439026..5720a6907 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutFilter.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutFilter.java @@ -1,33 +1,32 @@ -/** +/* * 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: + * 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 + * 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 + * 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 org.jasig.cas.client.util.AbstractConfigurationFilter; - -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; 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; /** * Implements the Single Sign Out protocol. It handles registering the session and destroying the session. @@ -38,50 +37,72 @@ */ 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", "ticket")); - handler.setLogoutParameterName(getPropertyFromInitParams(filterConfig, "logoutParameterName", "logoutRequest")); - handler.setArtifactParameterOverPost(parseBoolean(getPropertyFromInitParams(filterConfig, "artifactParameterOverPost", "false"))); + 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); + } + + public void setRelayStateParameterName(final String name) { + HANDLER.setRelayStateParameterName(name); + } + + public void setCasServerUrlPrefix(final String 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, final FilterChain filterChain) throws IOException, ServletException { + + public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, + final FilterChain filterChain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) servletRequest; + final HttpServletResponse response = (HttpServletResponse) servletResponse; - if (handler.isTokenRequest(request)) { - handler.recordSession(request); - } else if (handler.isLogoutRequest(request)) { - handler.destroySession(request); - // Do not continue up filter chain - return; - } else { - log.trace("Ignoring URI " + request.getRequestURI()); + /** + *

    Workaround for now for the fact that Spring Security will fail since it doesn't call {@link #init(javax.servlet.FilterConfig)}.

    + *

    Ultimately we need to allow deployers to actually inject their fully-initialized {@link org.jasig.cas.client.session.SingleSignOutHandler}.

    + */ + if (!this.handlerInitialized.getAndSet(true)) { + HANDLER.init(); } - filterChain.doFilter(servletRequest, servletResponse); + if (HANDLER.process(request, response)) { + filterChain.doFilter(servletRequest, servletResponse); + } } public void destroy() { // nothing to do } - + protected static SingleSignOutHandler getSingleSignOutHandler() { - return handler; + return HANDLER; } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHandler.java b/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHandler.java index 31f3e24d7..5d07095cc 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHandler.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHandler.java @@ -1,34 +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: + * 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 + * 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 + * 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.util.Arrays; +import java.util.List; +import java.util.zip.Inflater; + +import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.commons.codec.binary.Base64; +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 java.util.Arrays; -import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Performs CAS single sign-out operations in an API-agnostic fashion. @@ -40,22 +45,36 @@ */ public final class SingleSignOutHandler { + private final static int DECOMPRESSION_FACTOR = 10; + /** Logger instance */ - private final Log log = LogFactory.getLog(getClass()); + private final Logger logger = LoggerFactory.getLogger(getClass()); /** Mapping of token IDs and session IDs to HTTP sessions */ private SessionMappingStorage sessionMappingStorage = new HashMapBackedSessionMappingStorage(); - + /** The name of the artifact parameter. This is used to capture the session identifier. */ - private String artifactParameterName = "ticket"; + private String artifactParameterName = Protocol.CAS2.getArtifactParameterName(); + + /** Parameter name that stores logout request for back channel SLO */ + private String logoutParameterName = ConfigurationKeys.LOGOUT_PARAMETER_NAME.getDefaultValue(); - /** Parameter name that stores logout request */ - private String logoutParameterName = "logoutRequest"; + /** Parameter name that stores logout request for front channel SLO */ + 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 = ConfigurationKeys.RELAY_STATE_PARAMETER_NAME.getDefaultValue(); + + /** The prefix url of the CAS server */ + private String casServerUrlPrefix = ""; private boolean artifactParameterOverPost = false; + private boolean eagerlyCreateSessions = true; + private List safeParameters; + private LogoutStrategy logoutStrategy = isServlet30() ? new Servlet30LogoutStrategy() : new Servlet25LogoutStrategy(); public void setSessionMappingStorage(final SessionMappingStorage storage) { this.sessionMappingStorage = storage; @@ -77,27 +96,61 @@ public void setArtifactParameterName(final String name) { } /** - * @param name Name of parameter containing CAS logout request message. + * @param name Name of parameter containing CAS logout request message for back channel SLO. */ public void setLogoutParameterName(final String name) { this.logoutParameterName = name; } + /** + * @param casServerUrlPrefix The prefix url of the CAS server. + */ + public void setCasServerUrlPrefix(final String casServerUrlPrefix) { + this.casServerUrlPrefix = casServerUrlPrefix; + } + + /** + * @param name Name of parameter containing CAS logout request message for front channel SLO. + */ + public void setFrontLogoutParameterName(final String name) { + this.frontLogoutParameterName = name; + } + + /** + * @param name Name of parameter containing the state of the CAS server webflow. + */ + public void setRelayStateParameterName(final String name) { + this.relayStateParameterName = name; + } + + public void setEagerlyCreateSessions(final boolean eagerlyCreateSessions) { + this.eagerlyCreateSessions = eagerlyCreateSessions; + } + /** * 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.sessionMappingStorage, "sessionMappingStorage 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); + } } } - + /** * Determines whether the given request contains an authentication token. * @@ -105,20 +158,70 @@ public void init() { * * @return True if request contains authentication token, false otherwise. */ - public boolean isTokenRequest(final HttpServletRequest request) { - return CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.artifactParameterName, this.safeParameters)); + private boolean isTokenRequest(final HttpServletRequest request) { + return CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.artifactParameterName, + this.safeParameters)); } /** - * Determines whether the given request is a CAS logout request. + * Determines whether the given request is a CAS back channel logout request. * * @param request HTTP request. * * @return True if request is logout request, false otherwise. */ - public boolean isLogoutRequest(final HttpServletRequest request) { - return "POST".equals(request.getMethod()) && !isMultipartRequest(request) && - CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName, this.safeParameters)); + private boolean isBackChannelLogoutRequest(final HttpServletRequest request) { + return "POST".equals(request.getMethod()) + && !isMultipartRequest(request) + && CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName, + this.safeParameters)); + } + + /** + * 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()) && CommonUtils.isNotBlank(this.casServerUrlPrefix) + && CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.frontLogoutParameterName)); + } + + /** + * Process a request regarding the SLO process: record the session or destroy it. + * + * @param request the incoming HTTP request. + * @param response the HTTP response. + * @return if the request should continue to be processed. + */ + public boolean process(final HttpServletRequest request, final HttpServletResponse response) { + if (isTokenRequest(request)) { + logger.trace("Received a token request"); + recordSession(request); + return true; + + } else if (isBackChannelLogoutRequest(request)) { + logger.trace("Received a back channel logout request"); + destroySession(request); + return false; + + } else if (isFrontChannelLogoutRequest(request)) { + logger.trace("Received a front channel logout request"); + destroySession(request); + // redirection url to the CAS server + final String redirectionUrl = computeRedirectionToServer(request); + if (redirectionUrl != null) { + CommonUtils.sendRedirect(response, redirectionUrl); + } + return false; + + } else { + logger.trace("Ignoring URI for logout: {}", request.getRequestURI()); + return true; + } } /** @@ -127,14 +230,17 @@ public boolean isLogoutRequest(final HttpServletRequest request) { * * @param request HTTP request containing an authentication token. */ - public void recordSession(final HttpServletRequest request) { - final HttpSession session = request.getSession(true); + private void recordSession(final HttpServletRequest request) { + final HttpSession session = request.getSession(this.eagerlyCreateSessions); - final String token = CommonUtils.safeGetParameter(request, this.artifactParameterName, this.safeParameters); - if (log.isDebugEnabled()) { - log.debug("Recording session for token " + token); + if (session == null) { + logger.debug("No session currently exists (and none created). Cannot record session information for single sign out."); + return; } + final String token = CommonUtils.safeGetParameter(request, this.artifactParameterName, this.safeParameters); + logger.debug("Recording session for token {}", token); + try { this.sessionMappingStorage.removeBySessionById(session.getId()); } catch (final Exception e) { @@ -142,38 +248,134 @@ public void recordSession(final HttpServletRequest request) { } sessionMappingStorage.addSessionById(token, session); } - + + /** + * Uncompress a logout message (base64 + deflate). + * + * @param originalMessage the original logout message. + * @return the uncompressed logout message. + */ + private String uncompressLogoutMessage(final String originalMessage) { + final byte[] binaryMessage = Base64.decodeBase64(originalMessage); + + Inflater decompresser = null; + try { + // decompress the bytes + decompresser = new Inflater(); + decompresser.setInput(binaryMessage); + final byte[] result = new byte[binaryMessage.length * DECOMPRESSION_FACTOR]; + + final int resultLength = decompresser.inflate(result); + + // decode the bytes into a String + return new String(result, 0, resultLength, "UTF-8"); + } catch (final Exception e) { + logger.error("Unable to decompress logout message", e); + throw new RuntimeException(e); + } finally { + if (decompresser != null) { + decompresser.end(); + } + } + } + /** * Destroys the current HTTP session for the given CAS logout request. * * @param request HTTP request containing a CAS logout message. */ - public void destroySession(final HttpServletRequest request) { - final String logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName, this.safeParameters); - if (log.isTraceEnabled()) { - log.trace ("Logout request:\n" + logoutMessage); + private void destroySession(final HttpServletRequest request) { + final String logoutMessage; + // front channel logout -> the message needs to be base64 decoded + decompressed + if (isFrontChannelLogoutRequest(request)) { + logoutMessage = uncompressLogoutMessage(CommonUtils.safeGetParameter(request, + this.frontLogoutParameterName)); + } else { + logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName, this.safeParameters); } - + logger.trace("Logout request:\n{}", logoutMessage); + final String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex"); if (CommonUtils.isNotBlank(token)) { final HttpSession session = this.sessionMappingStorage.removeSessionByMappingId(token); if (session != null) { - String sessionID = session.getId(); + final String sessionID = session.getId(); + logger.debug("Invalidating session [{}] for token [{}]", sessionID, token); - if (log.isDebugEnabled()) { - log.debug ("Invalidating session [" + sessionID + "] for token [" + token + "]"); - } try { session.invalidate(); } catch (final IllegalStateException e) { - log.debug("Error invalidating session.", e); + logger.debug("Error invalidating session.", e); } + this.logoutStrategy.logout(request); } } } + /** + * Compute the redirection url to the CAS server when it's a front channel SLO + * (depending on the relay state parameter). + * + * @param request The HTTP request. + * @return the redirection url to the CAS server. + */ + private String computeRedirectionToServer(final HttpServletRequest request) { + final String relayStateValue = CommonUtils.safeGetParameter(request, this.relayStateParameterName); + // if we have a state value -> redirect to the CAS server to continue the logout process + if (CommonUtils.isNotBlank(relayStateValue)) { + final StringBuilder buffer = new StringBuilder(); + buffer.append(casServerUrlPrefix); + if (!this.casServerUrlPrefix.endsWith("/")) { + buffer.append("/"); + } + buffer.append("logout?_eventId=next&"); + buffer.append(this.relayStateParameterName); + buffer.append("="); + buffer.append(CommonUtils.urlEncode(relayStateValue)); + final String redirectUrl = buffer.toString(); + logger.debug("Redirection url to the CAS server: {}", redirectUrl); + return redirectUrl; + } + return null; + } + 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."); + } + } + } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHttpSessionListener.java b/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHttpSessionListener.java index 7d28bd16c..d1f3b5d11 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHttpSessionListener.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/session/SingleSignOutHttpSessionListener.java @@ -1,22 +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: + * 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 + * 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 + * 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 javax.servlet.http.HttpSession; @@ -35,16 +34,16 @@ */ public final class SingleSignOutHttpSessionListener implements HttpSessionListener { - private SessionMappingStorage sessionMappingStorage; - + private SessionMappingStorage sessionMappingStorage; + public void sessionCreated(final HttpSessionEvent event) { // nothing to do at the moment } public void sessionDestroyed(final HttpSessionEvent event) { - if (sessionMappingStorage == null) { - sessionMappingStorage = getSessionMappingStorage(); - } + if (sessionMappingStorage == null) { + sessionMappingStorage = getSessionMappingStorage(); + } final HttpSession session = event.getSession(); sessionMappingStorage.removeBySessionById(session.getId()); } @@ -56,6 +55,6 @@ public void sessionDestroyed(final HttpSessionEvent event) { * @return the SessionMappingStorage */ protected static SessionMappingStorage getSessionMappingStorage() { - return SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage(); + return SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage(); } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/ssl/AnyHostnameVerifier.java b/cas-client-core/src/main/java/org/jasig/cas/client/ssl/AnyHostnameVerifier.java index 14fe4ec99..c9672ce27 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/ssl/AnyHostnameVerifier.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/ssl/AnyHostnameVerifier.java @@ -1,22 +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: + * 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 + * 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 + * 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 javax.net.ssl.HostnameVerifier; diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/ssl/HttpURLConnectionFactory.java b/cas-client-core/src/main/java/org/jasig/cas/client/ssl/HttpURLConnectionFactory.java new file mode 100644 index 000000000..8ea298f09 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/ssl/HttpURLConnectionFactory.java @@ -0,0 +1,46 @@ +/* + * 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 java.io.Serializable; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; + +/** + * A factory to prepare and configure {@link java.net.URLConnection} instances. + * + * @author Misagh Moayyed + * @since 3.3 + */ +public interface HttpURLConnectionFactory extends Serializable { + + /** + * Receives a {@link URLConnection} instance typically as a result of a {@link URL} + * opening a connection to a remote resource. The received url connection is then + * configured and prepared appropriately depending on its type and is then returned to the caller + * to accommodate method chaining. + * + * @param url The url connection that needs to be configured + * @return The configured {@link HttpURLConnection} instance + * + * @see {@link HttpsURLConnectionFactory} + */ + HttpURLConnection buildHttpURLConnection(final URLConnection url); +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/ssl/HttpsURLConnectionFactory.java b/cas-client-core/src/main/java/org/jasig/cas/client/ssl/HttpsURLConnectionFactory.java new file mode 100644 index 000000000..cede90858 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/ssl/HttpsURLConnectionFactory.java @@ -0,0 +1,191 @@ +/* + * 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 java.io.*; +import java.net.HttpURLConnection; +import java.net.URLConnection; +import java.security.KeyStore; +import java.util.Properties; +import javax.net.ssl.*; +import org.jasig.cas.client.util.CommonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * An implementation of the {@link HttpURLConnectionFactory} whose responsible to configure + * the underlying https connection, if needed, with a given hostname and SSL socket factory based on the + * configuration provided. + * + * @author Misagh Moayyed + * @since 3.3 + * @see #setHostnameVerifier(HostnameVerifier) + * @see #setSSLConfiguration(Properties) + */ +public final class HttpsURLConnectionFactory implements HttpURLConnectionFactory { + + private static final long serialVersionUID = 1L; + + private static final Logger LOGGER = LoggerFactory.getLogger(HttpsURLConnectionFactory.class); + + /** + * Hostname verifier used when making an SSL request to the CAS server. + * Defaults to {@link HttpsURLConnection#getDefaultHostnameVerifier()} + */ + private HostnameVerifier hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier(); + + /** + * Properties file that can contains key/trust info for Client Side Certificates + */ + private Properties sslConfiguration = new Properties(); + + public HttpsURLConnectionFactory() { + } + + public HttpsURLConnectionFactory(final HostnameVerifier verifier, final Properties config) { + setHostnameVerifier(verifier); + setSSLConfiguration(config); + } + + public final void setSSLConfiguration(final Properties config) { + this.sslConfiguration = config; + } + + /** + * Set the host name verifier for the https connection received. + * + * @see AnyHostnameVerifier + * @see RegexHostnameVerifier + * @see WhitelistHostnameVerifier + */ + public final void setHostnameVerifier(final HostnameVerifier verifier) { + this.hostnameVerifier = verifier; + } + + public HttpURLConnection buildHttpURLConnection(final URLConnection url) { + return this.configureHttpsConnectionIfNeeded(url); + } + + /** + * Configures the connection with specific settings for secure http connections + * If the connection instance is not a {@link HttpsURLConnection}, + * no additional changes will be made and the connection itself is simply returned. + * + * @param conn the http connection + */ + private HttpURLConnection configureHttpsConnectionIfNeeded(final URLConnection conn) { + if (conn instanceof HttpsURLConnection) { + final HttpsURLConnection httpsConnection = (HttpsURLConnection) conn; + final SSLSocketFactory socketFactory = this.createSSLSocketFactory(); + if (socketFactory != null) { + httpsConnection.setSSLSocketFactory(socketFactory); + } + + if (this.hostnameVerifier != null) { + httpsConnection.setHostnameVerifier(this.hostnameVerifier); + } + } + return (HttpURLConnection) conn; + } + + /** + * Creates a {@link SSLSocketFactory} based on the configuration specified + *

    + * Sample properties file: + *

    +     * protocol=TLS
    +     * keyStoreType=JKS
    +     * keyStorePath=/var/secure/location/.keystore
    +     * keyStorePass=changeit
    +     * certificatePassword=aGoodPass
    +     * 
    + * @return the {@link SSLSocketFactory} + */ + private SSLSocketFactory createSSLSocketFactory() { + InputStream keyStoreIS = null; + try { + final SSLContext sslContext = SSLContext.getInstance(this.sslConfiguration.getProperty("protocol", "SSL")); + + if (this.sslConfiguration.getProperty("keyStoreType") != null) { + final KeyStore keyStore = KeyStore.getInstance(this.sslConfiguration.getProperty("keyStoreType")); + if (this.sslConfiguration.getProperty("keyStorePath") != null) { + keyStoreIS = new FileInputStream(this.sslConfiguration.getProperty("keyStorePath")); + if (this.sslConfiguration.getProperty("keyStorePass") != null) { + keyStore.load(keyStoreIS, this.sslConfiguration.getProperty("keyStorePass").toCharArray()); + LOGGER.debug("Keystore has {} keys", keyStore.size()); + final KeyManagerFactory keyManager = KeyManagerFactory.getInstance(this.sslConfiguration + .getProperty("keyManagerType", "SunX509")); + keyManager.init(keyStore, this.sslConfiguration.getProperty("certificatePassword") + .toCharArray()); + sslContext.init(keyManager.getKeyManagers(), null, null); + return sslContext.getSocketFactory(); + } + } + } + + } catch (final Exception e) { + LOGGER.error(e.getMessage(), e); + } finally { + CommonUtils.closeQuietly(keyStoreIS); + } + 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(); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/ssl/RegexHostnameVerifier.java b/cas-client-core/src/main/java/org/jasig/cas/client/ssl/RegexHostnameVerifier.java index 6bb707406..a13c9f734 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/ssl/RegexHostnameVerifier.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/ssl/RegexHostnameVerifier.java @@ -1,25 +1,26 @@ -/** +/* * 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: + * 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 + * 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 + * 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 java.io.Serializable; import java.util.regex.Pattern; - import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSession; @@ -32,12 +33,13 @@ * @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; - - + /** * Creates a new instance using the given regular expression. * @@ -47,7 +49,6 @@ public RegexHostnameVerifier(final String regex) { this.pattern = Pattern.compile(regex); } - /** {@inheritDoc} */ public boolean verify(final String hostname, final SSLSession session) { return pattern.matcher(hostname).matches(); diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/ssl/WhitelistHostnameVerifier.java b/cas-client-core/src/main/java/org/jasig/cas/client/ssl/WhitelistHostnameVerifier.java index c09002a70..c882225e4 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/ssl/WhitelistHostnameVerifier.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/ssl/WhitelistHostnameVerifier.java @@ -1,26 +1,26 @@ -/** +/* * 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: + * 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 + * 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 + * 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 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. @@ -30,12 +30,13 @@ * @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; - /** * Creates a new instance using the given array of allowed hosts. * @@ -45,7 +46,6 @@ public WhitelistHostnameVerifier(final String[] allowed) { this.allowedHosts = allowed; } - /** * Creates a new instance using the given list of allowed hosts. * diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/AbstractCasFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/AbstractCasFilter.java index 79c94b327..9b48296f1 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/util/AbstractCasFilter.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/AbstractCasFilter.java @@ -1,26 +1,25 @@ -/** +/* * 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: + * 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 + * 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 + * 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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.jasig.cas.client.Protocol; +import org.jasig.cas.client.configuration.ConfigurationKeys; import javax.servlet.FilterConfig; import javax.servlet.ServletException; @@ -38,23 +37,16 @@ *

    Please note that one of the two above parameters must be set.

    * * @author Scott Battaglia - * @version $Revision$ $Date$ + * @author Misagh Moayyed * @since 3.1 */ 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_"; - /** Instance of commons logging for logging purposes. */ - protected final Log log = LogFactory.getLog(getClass()); + private Protocol protocol; - /** Defines the parameter to look for for the artifact. */ - private String artifactParameterName = "ticket"; - - /** 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; @@ -66,24 +58,23 @@ 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)); - log.trace("Loading serverName property: " + this.serverName); - setService(getPropertyFromInitParams(filterConfig, "service", null)); - log.trace("Loading service property: " + this.service); - setArtifactParameterName(getPropertyFromInitParams(filterConfig, "artifactParameterName", "ticket")); - log.trace("Loading artifact parameter name property: " + this.artifactParameterName); - setServiceParameterName(getPropertyFromInitParams(filterConfig, "serviceParameterName", "service")); - log.trace("Loading serviceParameterName property: " + this.serviceParameterName); - setEncodeServiceUrl(parseBoolean(getPropertyFromInitParams(filterConfig, "encodeServiceUrl", "true"))); - log.trace("Loading encodeServiceUrl property: " + this.encodeServiceUrl); - + setServerName(getString(ConfigurationKeys.SERVER_NAME)); + setService(getString(ConfigurationKeys.SERVICE)); + setEncodeServiceUrl(getBoolean(ConfigurationKeys.ENCODE_SERVICE_URL)); + initInternal(filterConfig); } init(); } + /** Controls the ordering of filter initialization and checking by defining a method that runs before the init. * @param filterConfig the original filter configuration. * @throws ServletException if there is a problem. @@ -98,10 +89,10 @@ protected void initInternal(final FilterConfig filterConfig) throws ServletExcep * 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), "serverName and service cannot both be set. You MUST ONLY set one."); + 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), + "serverName and service cannot both be set. You MUST ONLY set one."); } // empty implementation as most filters won't need this. @@ -110,7 +101,9 @@ public void destroy() { } protected final String constructServiceUrl(final HttpServletRequest request, final HttpServletResponse response) { - return CommonUtils.constructServiceUrl(request, response, this.service, this.serverName, this.artifactParameterName, this.encodeServiceUrl); + return CommonUtils.constructServiceUrl(request, response, this.service, this.serverName, + this.protocol.getServiceParameterName(), + this.protocol.getArtifactParameterName(), this.encodeServiceUrl); } /** @@ -121,8 +114,8 @@ protected final String constructServiceUrl(final HttpServletRequest request, fin */ public final void setServerName(final String serverName) { if (serverName != null && serverName.endsWith("/")) { - this.serverName = serverName.substring(0, serverName.length()-1); - log.info(String.format("Eliminated extra slash from serverName [%s]. It is now [%s]", serverName, this.serverName)); + this.serverName = serverName.substring(0, serverName.length() - 1); + logger.info("Eliminated extra slash from serverName [{}]. It is now [{}]", serverName, this.serverName); } else { this.serverName = serverName; } @@ -132,24 +125,12 @@ public final void setService(final String service) { 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; + this.encodeServiceUrl = encodeServiceUrl; } - public final String getServiceParameterName() { - return this.serviceParameterName; + protected Protocol getProtocol() { + return this.protocol; } /** @@ -159,6 +140,6 @@ public final String getServiceParameterName() { * @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()); } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/AbstractConfigurationFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/AbstractConfigurationFilter.java index de6dca7e1..9d3e54293 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/util/AbstractConfigurationFilter.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/AbstractConfigurationFilter.java @@ -1,31 +1,32 @@ -/** +/* * 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: + * 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 + * 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 + * 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 javax.naming.InitialContext; -import javax.naming.NamingException; import javax.servlet.Filter; import javax.servlet.FilterConfig; +import javax.servlet.ServletException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +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; /** * Abstracts out the ability to configure the filters from the initial properties provided. @@ -35,90 +36,42 @@ * @since 3.1 */ public abstract class AbstractConfigurationFilter implements Filter { - - protected final Log log = LogFactory.getLog(getClass()); + + 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. - *

    - * 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} - *

    - * Essentially the documented order is: - *

      - *
    1. FilterConfig.getInitParameter
    2. - *
    3. ServletContext.getInitParameter
    4. - *
    5. java:comp/env/cas/SHORTFILTERNAME/{propertyName}
    6. - *
    7. java:comp/env/cas/{propertyName}
    8. - *
    9. Default Value
    10. - *
    - * - * - * @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)) { - log.info("Property [" + propertyName + "] loaded from FilterConfig.getInitParameter with value [" + value + "]"); - return value; - } + 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()); + } - final String value2 = filterConfig.getServletContext().getInitParameter(propertyName); + protected final boolean getBoolean(final ConfigurationKey configurationKey) { + return this.configurationStrategy.getBoolean(configurationKey); + } - if (CommonUtils.isNotBlank(value2)) { - log.info("Property [" + propertyName + "] loaded from ServletContext.getInitParameter with value [" + value2 + "]"); - return value2; - } - InitialContext context; - try { - context = new InitialContext(); - } catch (final NamingException e) { - log.warn(e,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)) { - log.info("Property [" + propertyName + "] loaded from JNDI Filter Specific Property with value [" + value3 + "]"); - return value3; - } - - final String value4 = loadFromContext(context, "java:comp/env/cas/" + propertyName); - - if (CommonUtils.isNotBlank(value4)) { - log.info("Property [" + propertyName + "] loaded from JNDI with value [" + value4 + "]"); - return value4; - } + protected final String getString(final ConfigurationKey configurationKey) { + return this.configurationStrategy.getString(configurationKey); + } - log.info("Property [" + propertyName + "] not found. Using default value [" + defaultValue + "]"); - return defaultValue; + protected final long getLong(final ConfigurationKey configurationKey) { + return this.configurationStrategy.getLong(configurationKey); } - - protected final boolean parseBoolean(final String value) { - return ((value != null) && value.equalsIgnoreCase("true")); + + protected final int getInt(final ConfigurationKey configurationKey) { + return this.configurationStrategy.getInt(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 Class getClass(final ConfigurationKey> configurationKey) { + return this.configurationStrategy.getClass(configurationKey); } - public final void setIgnoreInitConfiguration(boolean ignoreInitConfiguration) { + public final void setIgnoreInitConfiguration(final boolean ignoreInitConfiguration) { this.ignoreInitConfiguration = ignoreInitConfiguration; } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/AssertionHolder.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/AssertionHolder.java index 7959ca0ec..842a2686d 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/util/AssertionHolder.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/AssertionHolder.java @@ -1,22 +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: + * 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 + * 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 + * 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.jasig.cas.client.validation.Assertion; @@ -35,7 +34,6 @@ public class AssertionHolder { */ private static final ThreadLocal threadLocal = new ThreadLocal(); - /** * Retrieve the assertion from the ThreadLocal. * diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/AssertionThreadLocalFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/AssertionThreadLocalFilter.java index d0af95e58..9cae17dce 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/util/AssertionThreadLocalFilter.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/AssertionThreadLocalFilter.java @@ -1,35 +1,28 @@ -/** +/* * 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: + * 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 + * 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 + * 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.jasig.cas.client.validation.Assertion; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; +import java.io.IOException; +import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; -import java.io.IOException; +import org.jasig.cas.client.validation.Assertion; /** * Places the assertion in a ThreadLocal such that other resources can access it that do not have access to the web tier session. @@ -44,10 +37,13 @@ public void init(final FilterConfig filterConfig) throws ServletException { // nothing to do here } - public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { + public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, + final FilterChain filterChain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) servletRequest; final HttpSession session = request.getSession(false); - final Assertion assertion = (Assertion) (session == null ? request.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION)); + final Assertion assertion = (Assertion) (session == null ? request + .getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : session + .getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION)); try { AssertionHolder.setAssertion(assertion); diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/CommonUtils.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/CommonUtils.java index 733393f3d..639b3b48b 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/util/CommonUtils.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/CommonUtils.java @@ -1,47 +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: + * 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 + * 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 + * 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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; -import org.jasig.cas.client.validation.ProxyList; -import org.jasig.cas.client.validation.ProxyListEditor; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; +import java.net.URLEncoder; +import java.util.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.net.URLConnection; -import java.net.URLEncoder; -import java.net.URL; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.*; +import org.jasig.cas.client.Protocol; +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; +import org.slf4j.LoggerFactory; /** * Common utilities so that we don't need to include Commons Lang. @@ -52,9 +45,8 @@ */ public final class CommonUtils { - /** Instance of Commons Logging. */ - private static final Log LOG = LogFactory.getLog(CommonUtils.class); - + private static final Logger LOGGER = LoggerFactory.getLogger(CommonUtils.class); + /** * Constant representing the ProxyGrantingTicket IOU Request Parameter. */ @@ -65,16 +57,23 @@ public final class CommonUtils { */ private static final String PARAM_PROXY_GRANTING_TICKET = "pgtId"; + private static final HttpURLConnectionFactory DEFAULT_URL_CONNECTION_FACTORY = new HttpsURLConnectionFactory(); + + private static final String SERVICE_PARAMETER_NAMES; + 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); + static { + final Set serviceParameterSet = new HashSet(4); + for (final Protocol protocol : Protocol.values()) { + serviceParameterSet.add(protocol.getServiceParameterName()); + } + SERVICE_PARAMETER_NAMES = serviceParameterSet.toString() + .replaceAll("\\[|\\]", "") + .replaceAll("\\s", ""); } - /** * Check whether the object is null or not. If it is, throw an exception and * display the message. @@ -106,7 +105,7 @@ public static void assertNotEmpty(final Collection c, final String message) { * 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) { @@ -115,6 +114,20 @@ public static void assertTrue(final boolean cond, final String message) { } } + + /** + * 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. * @@ -168,43 +181,48 @@ public static boolean isNotBlank(final String string) { * @param gateway where we should send gateway or not. * @return the fully constructed redirect url. */ - public static String constructRedirectUrl(final String casServerLoginUrl, final String serviceParameterName, final String serviceUrl, final boolean renew, final boolean gateway) { + public static String constructRedirectUrl(final String casServerLoginUrl, final String serviceParameterName, + final String serviceUrl, final boolean renew, final boolean gateway) { + return casServerLoginUrl + (casServerLoginUrl.contains("?") ? "&" : "?") + serviceParameterName + "=" + + urlEncode(serviceUrl) + (renew ? "&renew=true" : "") + (gateway ? "&gateway=true" : ""); + } + + /** + * Url encode a value using UTF-8 encoding. + * + * @param value the value to encode. + * @return the encoded value. + */ + public static String urlEncode(String value) { try { - return casServerLoginUrl + (casServerLoginUrl.contains("?") ? "&" : "?") + serviceParameterName + "=" - + URLEncoder.encode(serviceUrl, "UTF-8") - + (renew ? "&renew=true" : "") - + (gateway ? "&gateway=true" : ""); + return URLEncoder.encode(value, "UTF-8"); } catch (final UnsupportedEncodingException e) { throw new RuntimeException(e); } } - - public static void readAndRespondToProxyReceptorRequest(final HttpServletRequest request, final HttpServletResponse response, final ProxyGrantingTicketStorage proxyGrantingTicketStorage) throws IOException { + + public static void readAndRespondToProxyReceptorRequest(final HttpServletRequest request, + final HttpServletResponse response, final ProxyGrantingTicketStorage proxyGrantingTicketStorage) + throws IOException { final String proxyGrantingTicketIou = request.getParameter(PARAM_PROXY_GRANTING_TICKET_IOU); - final String proxyGrantingTicket = request.getParameter(PARAM_PROXY_GRANTING_TICKET); + final String proxyGrantingTicket = request.getParameter(PARAM_PROXY_GRANTING_TICKET); - if (CommonUtils.isBlank(proxyGrantingTicket) || CommonUtils.isBlank(proxyGrantingTicketIou)) { - response.getWriter().write(""); - return; - } + if (CommonUtils.isBlank(proxyGrantingTicket) || CommonUtils.isBlank(proxyGrantingTicketIou)) { + response.getWriter().write(""); + return; + } - if (LOG.isDebugEnabled()) { - LOG.debug("Received proxyGrantingTicketId [" - + proxyGrantingTicket + "] for proxyGrantingTicketIou [" - + proxyGrantingTicketIou + "]"); - } + LOGGER.debug("Received proxyGrantingTicketId [{}] for proxyGrantingTicketIou [{}]", proxyGrantingTicket, + proxyGrantingTicketIou); - proxyGrantingTicketStorage.save(proxyGrantingTicketIou, proxyGrantingTicket); + proxyGrantingTicketStorage.save(proxyGrantingTicketIou, proxyGrantingTicket); - if (LOG.isDebugEnabled()) { - LOG.debug("Successfully saved proxyGrantingTicketId [" - + proxyGrantingTicket + "] for proxyGrantingTicketIou [" - + proxyGrantingTicketIou + "]"); - } - - response.getWriter().write(""); - response.getWriter().write(""); + LOGGER.debug("Successfully saved proxyGrantingTicketId [{}] for proxyGrantingTicketIou [{}]", + proxyGrantingTicket, proxyGrantingTicketIou); + + response.getWriter().write(""); + response.getWriter().write(""); } protected static String findMatchingServerName(final HttpServletRequest request, final String serverName) { @@ -216,12 +234,12 @@ protected static String findMatchingServerName(final HttpServletRequest request, final String host = request.getHeader("Host"); final String xHost = request.getHeader("X-Forwarded-Host"); - + final String comparisonHost; if (xHost != null && host == "localhost") { - comparisonHost = xHost; + comparisonHost = xHost; } else { - comparisonHost = host; + comparisonHost = host; } if (comparisonHost == null) { @@ -238,70 +256,104 @@ protected static String findMatchingServerName(final HttpServletRequest request, return serverNames[0]; } - -/** + + private static boolean serverNameContainsPort(final boolean containsScheme, final String serverName) { + if (!containsScheme && serverName.contains(":")) { + return true; + } + + final int schemeIndex = serverName.indexOf(":"); + final int portIndex = serverName.lastIndexOf(":"); + return schemeIndex != portIndex; + } + + private static boolean requestIsOnStandardPort(final HttpServletRequest request) { + final int serverPort = request.getServerPort(); + return serverPort == 80 || serverPort == 443; + } + + /** + * Constructs a service url from the HttpServletRequest or from the given + * serviceUrl. Prefers the serviceUrl provided if both a serviceUrl and a + * serviceName. Compiles a list of all service parameters for supported protocols + * and removes them all from the query string. + * + * @param request the HttpServletRequest + * @param response the HttpServletResponse + * @param service the configured service url (this will be used if not null) + * @param serverNames the server name to use to construct the service url if the service param is empty. Note, prior to CAS Client 3.3, this was a single value. + * As of 3.3, it can be a space-separated value. We keep it as a single value, but will convert it to an array internally to get the matching value. This keeps backward compatability with anything using this public + * method. + * @param artifactParameterName the artifact parameter name to remove (i.e. ticket) + * @param encode whether to encode the url or not (i.e. Jsession). + * @return the service url to use. + */ + @Deprecated + public static String constructServiceUrl(final HttpServletRequest request, final HttpServletResponse response, + final String service, final String serverNames, + final String artifactParameterName, final boolean encode) { + return constructServiceUrl(request, response, service, serverNames, SERVICE_PARAMETER_NAMES + , artifactParameterName, encode); + } + + /** * Constructs a service url from the HttpServletRequest or from the given * serviceUrl. Prefers the serviceUrl provided if both a serviceUrl and a * serviceName. * - * @param request the HttpServletRequest + * @param request the HttpServletRequest * @param response the HttpServletResponse * @param service the configured service url (this will be used if not null) - * @param serverNames the server name to use to constuct the service url if the service param is empty. Note, prior to CAS Client 3.3, this was a single value. + * @param serverNames the server name to use to construct the service url if the service param is empty. Note, prior to CAS Client 3.3, this was a single value. * As of 3.3, it can be a space-separated value. We keep it as a single value, but will convert it to an array internally to get the matching value. This keeps backward compatability with anything using this public * method. + * @param serviceParameterName the service parameter name to remove (i.e. service) * @param artifactParameterName the artifact parameter name to remove (i.e. ticket) - * @param encode whether to encode the url or not (i.e. Jsession). + * @param encode whether to encode the url or not (i.e. Jsession). * @return the service url to use. */ - public static String constructServiceUrl(final HttpServletRequest request, final HttpServletResponse response, final String service, final String serverNames, final String artifactParameterName, final boolean encode) { + public static String constructServiceUrl(final HttpServletRequest request, final HttpServletResponse response, + final String service, final String serverNames, final String serviceParameterName, + final String artifactParameterName, final boolean encode) { if (CommonUtils.isNotBlank(service)) { return encode ? response.encodeURL(service) : service; } - final StringBuilder buffer = new StringBuilder(); - final String serverName = findMatchingServerName(request, serverNames); + final URIBuilder originalRequestUrl = new URIBuilder(request.getRequestURL().toString(), encode); + originalRequestUrl.setParameters(request.getQueryString()); + + URIBuilder builder = null; + boolean containsScheme = true; if (!serverName.startsWith("https://") && !serverName.startsWith("http://")) { - buffer.append(request.isSecure() ? "https://" : "http://"); + builder = new URIBuilder(encode); + builder.setScheme(request.isSecure() ? "https" : "http"); + builder.setHost(serverName); + containsScheme = false; + } else { + builder = new URIBuilder(serverName, encode); } - buffer.append(serverName); - buffer.append(request.getRequestURI()); - - if (CommonUtils.isNotBlank(request.getQueryString())) { - final int location = request.getQueryString().indexOf(artifactParameterName + "="); - if (location == 0) { - final String returnValue = encode ? response.encodeURL(buffer.toString()): buffer.toString(); - if (LOG.isDebugEnabled()) { - LOG.debug("serviceUrl generated: " + returnValue); - } - return returnValue; - } - - buffer.append("?"); + if (!serverNameContainsPort(containsScheme, serverName) && !requestIsOnStandardPort(request)) { + builder.setPort(request.getServerPort()); + } - if (location == -1) { - buffer.append(request.getQueryString()); - } else if (location > 0) { - final int actualLocation = request.getQueryString() - .indexOf("&" + artifactParameterName + "="); + builder.setEncodedPath(request.getRequestURI()); - if (actualLocation == -1) { - buffer.append(request.getQueryString()); - } else if (actualLocation > 0) { - buffer.append(request.getQueryString().substring(0, - actualLocation)); + final List serviceParameterNames = Arrays.asList(serviceParameterName.split(",")); + if (!serviceParameterNames.isEmpty() && !originalRequestUrl.getQueryParams().isEmpty()) { + for (final URIBuilder.BasicNameValuePair pair : originalRequestUrl.getQueryParams()) { + if (!pair.getName().equals(artifactParameterName) && !serviceParameterNames.contains(pair.getName())) { + builder.addParameter(pair.getName(), pair.getValue()); } } } - final String returnValue = encode ? response.encodeURL(buffer.toString()) : buffer.toString(); - if (LOG.isDebugEnabled()) { - LOG.debug("serviceUrl generated: " + returnValue); - } + final String result = builder.toString(); + final String returnValue = encode ? response.encodeURL(result) : result; + LOGGER.debug("serviceUrl generated: {}", returnValue); return returnValue; } @@ -321,18 +373,21 @@ public static String constructServiceUrl(final HttpServletRequest request, final * @param parameter the parameter to look for. * @return the value of the parameter. */ - public static String safeGetParameter(final HttpServletRequest request, final String parameter, final List parameters) { + public static String safeGetParameter(final HttpServletRequest request, final String parameter, + final List parameters) { if ("POST".equals(request.getMethod()) && parameters.contains(parameter)) { - LOG.debug("safeGetParameter called on a POST HttpServletRequest for Restricted Parameters. Cannot complete check safely. Reverting to standard behavior for this Parameter"); + LOGGER.debug("safeGetParameter called on a POST HttpServletRequest for Restricted Parameters. Cannot complete check safely. Reverting to standard behavior for this Parameter"); return request.getParameter(parameter); } - return request.getQueryString() == null || !request.getQueryString().contains(parameter) ? null : request.getParameter(parameter); + return request.getQueryString() == null || !request.getQueryString().contains(parameter) ? null : request + .getParameter(parameter); } public static String safeGetParameter(final HttpServletRequest request, final String parameter) { return safeGetParameter(request, parameter, Arrays.asList("logoutRequest")); } + /** * Contacts the remote URL and returns the response. * @@ -340,64 +395,58 @@ public static String safeGetParameter(final HttpServletRequest request, final St * @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, HttpsURLConnection.getDefaultHostnameVerifier(), encoding); + return getResponseFromServer(constructedUrl, DEFAULT_URL_CONNECTION_FACTORY, encoding); } /** * Contacts the remote URL and returns the response. * * @param constructedUrl the url to contact. - * @param hostnameVerifier Host name verifier to use for HTTPS connections. + * @param factory connection factory to prepare the URL connection instance * @param encoding the encoding to use. * @return the response. */ - public static String getResponseFromServer(final URL constructedUrl, final HostnameVerifier hostnameVerifier, final String encoding) { - URLConnection conn = null; + public static String getResponseFromServer(final URL constructedUrl, final HttpURLConnectionFactory factory, + final String encoding) { + + HttpURLConnection conn = null; + InputStreamReader in = null; try { - conn = constructedUrl.openConnection(); - if (conn instanceof HttpsURLConnection) { - ((HttpsURLConnection)conn).setHostnameVerifier(hostnameVerifier); - } - final BufferedReader in; + conn = factory.buildHttpURLConnection(constructedUrl.openConnection()); if (CommonUtils.isEmpty(encoding)) { - in = new BufferedReader(new InputStreamReader(conn.getInputStream())); + in = new InputStreamReader(conn.getInputStream()); } else { - in = new BufferedReader(new InputStreamReader(conn.getInputStream(), encoding)); + in = new InputStreamReader(conn.getInputStream(), encoding); } - String line; - final StringBuilder stringBuffer = new StringBuilder(255); - - while ((line = in.readLine()) != null) { - stringBuffer.append(line); - stringBuffer.append("\n"); + final StringBuilder builder = new StringBuilder(255); + int byteRead; + while ((byteRead = in.read()) != -1) { + builder.append((char) byteRead); } - return stringBuffer.toString(); + + return builder.toString(); } catch (final Exception e) { - LOG.error(e.getMessage(), e); + LOGGER.error(e.getMessage(), e); throw new RuntimeException(e); } finally { - if (conn != null && conn instanceof HttpURLConnection) { - ((HttpURLConnection)conn).disconnect(); + closeQuietly(in); + if (conn != null) { + conn.disconnect(); } } - - } - /** - * Contacts the remote URL and returns the response. - * - * @param url the url to contact. - * @param encoding the encoding to use. - * @return the response. - */ - public static String getResponseFromServer(final String url, String encoding) { - try { - return getResponseFromServer(new URL(url), encoding); - } catch (final MalformedURLException e) { - throw new IllegalArgumentException(e); - } } public static ProxyList createProxyList(final String proxies) { @@ -408,7 +457,7 @@ public static ProxyList createProxyList(final String proxies) { final ProxyListEditor editor = new ProxyListEditor(); editor.setAsText(proxies); return (ProxyList) editor.getValue(); - } + } /** * Sends the redirect message and captures the exceptions that we can't possibly do anything with. @@ -420,8 +469,234 @@ public static void sendRedirect(final HttpServletResponse response, final String try { response.sendRedirect(url); } catch (final Exception e) { - LOG.warn(e.getMessage(), e); + LOGGER.warn(e.getMessage(), e); + } + + } + + /** + * Unconditionally close a {@link Closeable}. Equivalent to {@link java.io.Closeable#close()}close(), except any exceptions + * will be ignored. This is typically used in finally blocks. + * @param resource the resource to close + */ + public static void closeQuietly(final Closeable resource) { + try { + if (resource != null) { + resource.close(); + } + } catch (final IOException e) { + //ignore + } + } + + /** + *

    Converts a String to a boolean (optimised for performance).

    + * + *

    {@code 'true'}, {@code 'on'}, {@code 'y'}, {@code 't'} or {@code 'yes'} + * (case insensitive) will return {@code true}. Otherwise, + * {@code false} is returned.

    + * + *

    This method performs 4 times faster (JDK1.4) than + * {@code Boolean.valueOf(String)}. However, this method accepts + * 'on' and 'yes', 't', 'y' as true values. + * + *

    +     *   BooleanUtils.toBoolean(null)    = false
    +     *   BooleanUtils.toBoolean("true")  = true
    +     *   BooleanUtils.toBoolean("TRUE")  = true
    +     *   BooleanUtils.toBoolean("tRUe")  = true
    +     *   BooleanUtils.toBoolean("on")    = true
    +     *   BooleanUtils.toBoolean("yes")   = true
    +     *   BooleanUtils.toBoolean("false") = false
    +     *   BooleanUtils.toBoolean("x gti") = false
    +     *   BooleanUtils.toBooleanObject("y") = true
    +     *   BooleanUtils.toBooleanObject("n") = false
    +     *   BooleanUtils.toBooleanObject("t") = true
    +     *   BooleanUtils.toBooleanObject("f") = false
    +     * 
    + * + * @param str the String to check + * @return the boolean value of the string, {@code false} if no match or the String is null + */ + public static boolean toBoolean(final String str) { + return toBooleanObject(str) == Boolean.TRUE; + } + + /** + *

    Converts a String to a Boolean.

    + * + *

    {@code 'true'}, {@code 'on'}, {@code 'y'}, {@code 't'} or {@code 'yes'} + * (case insensitive) will return {@code true}. + * {@code 'false'}, {@code 'off'}, {@code 'n'}, {@code 'f'} or {@code 'no'} + * (case insensitive) will return {@code false}. + * Otherwise, {@code null} is returned.

    + * + *

    NOTE: This returns null and will throw a NullPointerException if autoboxed to a boolean.

    + * + *
    +     *   // N.B. case is not significant
    +     *   BooleanUtils.toBooleanObject(null)    = null
    +     *   BooleanUtils.toBooleanObject("true")  = Boolean.TRUE
    +     *   BooleanUtils.toBooleanObject("T")     = Boolean.TRUE // i.e. T[RUE]
    +     *   BooleanUtils.toBooleanObject("false") = Boolean.FALSE
    +     *   BooleanUtils.toBooleanObject("f")     = Boolean.FALSE // i.e. f[alse]
    +     *   BooleanUtils.toBooleanObject("No")    = Boolean.FALSE
    +     *   BooleanUtils.toBooleanObject("n")     = Boolean.FALSE // i.e. n[o]
    +     *   BooleanUtils.toBooleanObject("on")    = Boolean.TRUE
    +     *   BooleanUtils.toBooleanObject("ON")    = Boolean.TRUE
    +     *   BooleanUtils.toBooleanObject("off")   = Boolean.FALSE
    +     *   BooleanUtils.toBooleanObject("oFf")   = Boolean.FALSE
    +     *   BooleanUtils.toBooleanObject("yes")   = Boolean.TRUE
    +     *   BooleanUtils.toBooleanObject("Y")     = Boolean.TRUE // i.e. Y[ES]
    +     *   BooleanUtils.toBooleanObject("blue")  = null
    +     *   BooleanUtils.toBooleanObject("true ") = null // trailing space (too long)
    +     *   BooleanUtils.toBooleanObject("ono")   = null // does not match on or no
    +     * 
    + * + * @param str the String to check; upper and lower case are treated as the same + * @return the Boolean value of the string, {@code null} if no match or {@code null} input + */ + public static Boolean toBooleanObject(final String str) { + // Previously used equalsIgnoreCase, which was fast for interned 'true'. + // Non interned 'true' matched 15 times slower. + // + // Optimisation provides same performance as before for interned 'true'. + // Similar performance for null, 'false', and other strings not length 2/3/4. + // 'true'/'TRUE' match 4 times slower, 'tRUE'/'True' 7 times slower. + if (str == "true") { + return Boolean.TRUE; + } + if (str == null) { + return null; + } + switch (str.length()) { + case 1: { + final char ch0 = str.charAt(0); + if (ch0 == 'y' || ch0 == 'Y' || + ch0 == 't' || ch0 == 'T') { + return Boolean.TRUE; + } + if (ch0 == 'n' || ch0 == 'N' || + ch0 == 'f' || ch0 == 'F') { + return Boolean.FALSE; + } + break; + } + case 2: { + final char ch0 = str.charAt(0); + final char ch1 = str.charAt(1); + if ((ch0 == 'o' || ch0 == 'O') && + (ch1 == 'n' || ch1 == 'N') ) { + return Boolean.TRUE; + } + if ((ch0 == 'n' || ch0 == 'N') && + (ch1 == 'o' || ch1 == 'O') ) { + return Boolean.FALSE; + } + break; + } + case 3: { + final char ch0 = str.charAt(0); + final char ch1 = str.charAt(1); + final char ch2 = str.charAt(2); + if ((ch0 == 'y' || ch0 == 'Y') && + (ch1 == 'e' || ch1 == 'E') && + (ch2 == 's' || ch2 == 'S') ) { + return Boolean.TRUE; + } + if ((ch0 == 'o' || ch0 == 'O') && + (ch1 == 'f' || ch1 == 'F') && + (ch2 == 'f' || ch2 == 'F') ) { + return Boolean.FALSE; + } + break; + } + case 4: { + final char ch0 = str.charAt(0); + final char ch1 = str.charAt(1); + final char ch2 = str.charAt(2); + final char ch3 = str.charAt(3); + if ((ch0 == 't' || ch0 == 'T') && + (ch1 == 'r' || ch1 == 'R') && + (ch2 == 'u' || ch2 == 'U') && + (ch3 == 'e' || ch3 == 'E') ) { + return Boolean.TRUE; + } + break; + } + case 5: { + final char ch0 = str.charAt(0); + final char ch1 = str.charAt(1); + final char ch2 = str.charAt(2); + final char ch3 = str.charAt(3); + final char ch4 = str.charAt(4); + if ((ch0 == 'f' || ch0 == 'F') && + (ch1 == 'a' || ch1 == 'A') && + (ch2 == 'l' || ch2 == 'L') && + (ch3 == 's' || ch3 == 'S') && + (ch4 == 'e' || ch4 == 'E') ) { + return Boolean.FALSE; + } + break; + } + default: + break; + } + + return null; + } + + /** + *

    Convert a String to a long, returning a + * default value if the conversion fails.

    + * + *

    If the string is null, the default value is returned.

    + * + *
    +     *   NumberUtils.toLong(null, 1L) = 1L
    +     *   NumberUtils.toLong("", 1L)   = 1L
    +     *   NumberUtils.toLong("1", 0L)  = 1L
    +     * 
    + * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the long represented by the string, or the default if conversion fails + */ + public static long toLong(final String str, final long defaultValue) { + if (str == null) { + return defaultValue; + } + try { + return Long.parseLong(str); + } catch (final NumberFormatException nfe) { + return defaultValue; } + } + /** + *

    Convert a String to an int, returning a + * default value if the conversion fails.

    + * + *

    If the string is null, the default value is returned.

    + * + *
    +     *   NumberUtils.toInt(null, 1) = 1
    +     *   NumberUtils.toInt("", 1)   = 1
    +     *   NumberUtils.toInt("1", 0)  = 1
    +     * 
    + * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the int represented by the string, or the default if conversion fails + */ + public static int toInt(final String str, final int defaultValue) { + if(str == null) { + return defaultValue; + } + try { + return Integer.parseInt(str); + } catch (final NumberFormatException nfe) { + return defaultValue; + } } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/DelegatingFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/DelegatingFilter.java index 6c71ef831..c25ff0caf 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/util/DelegatingFilter.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/DelegatingFilter.java @@ -1,36 +1,29 @@ -/** +/* * 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: + * 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 + * 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 + * 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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.Map; +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A Delegating Filter looks up a parameter in the request object and matches @@ -46,7 +39,7 @@ public final class DelegatingFilter implements Filter { /** * Instance of Commons Logging. */ - private final Log log = LogFactory.getLog(this.getClass()); + private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** * The request parameter to look for in the Request object. @@ -56,7 +49,7 @@ public final class DelegatingFilter implements Filter { /** * The map of filters to delegate to and the criteria (as key). */ - private final Map delegators; + private final Map delegators; /** * The default filter to use if there is no match. @@ -69,11 +62,13 @@ public final class DelegatingFilter implements Filter { */ private final boolean exactMatch; - public DelegatingFilter(final String requestParameterName, final Map delegators, final boolean exactMatch) { + public DelegatingFilter(final String requestParameterName, final Map delegators, + final boolean exactMatch) { this(requestParameterName, delegators, exactMatch, null); } - public DelegatingFilter(final String requestParameterName, final Map delegators, final boolean exactMatch, final Filter defaultFilter) { + public DelegatingFilter(final String requestParameterName, final Map delegators, + final boolean exactMatch, final Filter defaultFilter) { CommonUtils.assertNotNull(requestParameterName, "requestParameterName cannot be null."); CommonUtils.assertTrue(!delegators.isEmpty(), "delegators cannot be empty."); @@ -87,7 +82,8 @@ public void destroy() { // nothing to do here } - public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain) throws IOException, ServletException { + public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain) + throws IOException, ServletException { final String parameter = CommonUtils.safeGetParameter((HttpServletRequest) request, this.requestParameterName); @@ -95,19 +91,15 @@ public void doFilter(final ServletRequest request, final ServletResponse respons for (final String key : this.delegators.keySet()) { if ((parameter.equals(key) && this.exactMatch) || (parameter.matches(key) && !this.exactMatch)) { final Filter filter = this.delegators.get(key); - if (log.isDebugEnabled()) { - log.debug("Match found for parameter [" - + this.requestParameterName + "] with value [" - + parameter + "]. Delegating to filter [" - + filter.getClass().getName() + "]"); - } + logger.debug("Match found for parameter [{}] with value [{}]. Delegating to filter [{}]", + this.requestParameterName, parameter, filter.getClass().getName()); filter.doFilter(request, response, filterChain); return; } } } - log.debug("No match found for parameter [" + this.requestParameterName + "] with value [" + parameter + "]"); + logger.debug("No match found for parameter [{}] with value [{}]", this.requestParameterName, parameter); if (this.defaultFilter != null) { this.defaultFilter.doFilter(request, response, filterChain); diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/ErrorRedirectFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/ErrorRedirectFilter.java index 9ad2862fb..b1425a4bb 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/util/ErrorRedirectFilter.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/ErrorRedirectFilter.java @@ -1,128 +1,136 @@ -/** +/* * 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: + * 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 + * 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 + * 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.io.IOException; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; +import javax.servlet.*; import javax.servlet.http.HttpServletResponse; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +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. - *

    + *

    * 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. - *

    + *

    * 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 { - private final Log log = LogFactory.getLog(getClass()); - - private final List errors = new ArrayList(); - - private String defaultErrorRedirectPage; - - public void destroy() { - // nothing to do here - } - - public void doFilter(final ServletRequest request, final ServletResponse response, - final FilterChain filterChain) throws IOException, ServletException { - final HttpServletResponse httpResponse = (HttpServletResponse) response; - try { - filterChain.doFilter(request, response); - } catch (final ServletException e) { - final Throwable t = e.getCause(); - ErrorHolder currentMatch = null; + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final List errors = new ArrayList(); + + private String defaultErrorRedirectPage; + + public void destroy() { + // nothing to do here + } + + public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain) + throws IOException, ServletException { + final HttpServletResponse httpResponse = (HttpServletResponse) response; + try { + filterChain.doFilter(request, response); + } catch (final Exception e) { + final Throwable t = extractErrorToCompare(e); + ErrorHolder currentMatch = null; for (final ErrorHolder errorHolder : this.errors) { - if (errorHolder.exactMatch(t)) { - currentMatch = errorHolder; - break; - } else if (errorHolder.inheritanceMatch(t)) { - currentMatch = errorHolder; - } - } - - if (currentMatch != null) { - httpResponse.sendRedirect(currentMatch.getUrl()); - } else { - httpResponse.sendRedirect(defaultErrorRedirectPage); - } - } - } - - public void init(final FilterConfig filterConfig) throws ServletException { - this.defaultErrorRedirectPage = filterConfig.getInitParameter("defaultErrorRedirectPage"); - - final Enumeration enumeration = filterConfig.getInitParameterNames(); - while (enumeration.hasMoreElements()) { - final String className = (String) enumeration.nextElement(); - try { - if (!className.equals("defaultErrorRedirectPage")) { - this.errors.add(new ErrorHolder(className, filterConfig.getInitParameter(className))); - } - } catch (final ClassNotFoundException e) { - log.warn("Class [" + className + "] cannot be found in ClassLoader. Ignoring."); - } - } - } - - protected final class ErrorHolder { - - private Class className; - - private String url; - - protected ErrorHolder(final String className, final String url) throws ClassNotFoundException { - this.className = Class.forName(className); - this.url = url; - } - - public boolean exactMatch(final Throwable e) { - return this.className.equals(e.getClass()); - } - - public boolean inheritanceMatch(final Throwable e) { - return className.isAssignableFrom(e.getClass()); - } - - public String getUrl() { - return this.url; - } - } + if (errorHolder.exactMatch(t)) { + currentMatch = errorHolder; + break; + } else if (errorHolder.inheritanceMatch(t)) { + currentMatch = errorHolder; + } + } + + if (currentMatch != null) { + httpResponse.sendRedirect(currentMatch.getUrl()); + } else { + httpResponse.sendRedirect(defaultErrorRedirectPage); + } + } + } + + /** + * 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"); + + final Enumeration enumeration = filterConfig.getInitParameterNames(); + while (enumeration.hasMoreElements()) { + final String className = (String) enumeration.nextElement(); + try { + if (!className.equals("defaultErrorRedirectPage")) { + this.errors.add(new ErrorHolder(className, filterConfig.getInitParameter(className))); + } + } catch (final ClassNotFoundException e) { + logger.warn("Class [{}] cannot be found in ClassLoader. Ignoring.", className); + } + } + } + + protected final class ErrorHolder { + + private Class className; + + private String url; + + protected ErrorHolder(final String className, final String url) throws ClassNotFoundException { + this.className = Class.forName(className); + this.url = url; + } + + public boolean exactMatch(final Throwable e) { + return this.className.equals(e.getClass()); + } + + public boolean inheritanceMatch(final Throwable e) { + return className.isAssignableFrom(e.getClass()); + } + + public String getUrl() { + return this.url; + } + } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/HttpServletRequestWrapperFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/HttpServletRequestWrapperFilter.java index 56920b0df..a474a83ac 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/util/HttpServletRequestWrapperFilter.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/HttpServletRequestWrapperFilter.java @@ -1,38 +1,33 @@ -/** +/* * 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: + * 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 + * 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 + * 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.jasig.cas.client.authentication.AttributePrincipal; -import org.jasig.cas.client.validation.Assertion; - -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; -import javax.servlet.http.HttpSession; import java.io.IOException; import java.security.Principal; import java.util.Collection; +import javax.servlet.*; +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; /** * Implementation of a filter that wraps the normal HttpServletRequest with a @@ -56,7 +51,7 @@ public final class HttpServletRequestWrapperFilter extends AbstractConfiguration /** Name of the attribute used to answer role membership queries */ private String roleAttribute; - + /** Whether or not to ignore case in role membership queries */ private boolean ignoreCase; @@ -69,23 +64,28 @@ public void destroy() { * request.getRemoteUser to the underlying Assertion object * stored in the user session. */ - public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { + public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, + final FilterChain filterChain) throws IOException, ServletException { final AttributePrincipal principal = retrievePrincipalFromSessionOrRequest(servletRequest); - filterChain.doFilter(new CasHttpServletRequestWrapper((HttpServletRequest) servletRequest, principal), servletResponse); + filterChain.doFilter(new CasHttpServletRequestWrapper((HttpServletRequest) servletRequest, principal), + servletResponse); } protected AttributePrincipal retrievePrincipalFromSessionOrRequest(final ServletRequest servletRequest) { final HttpServletRequest request = (HttpServletRequest) servletRequest; final HttpSession session = request.getSession(false); - final Assertion assertion = (Assertion) (session == null ? request.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION)); + final Assertion assertion = (Assertion) (session == null ? request + .getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION) : session + .getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION)); return assertion == null ? null : assertion.getPrincipal(); } 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 { @@ -107,36 +107,36 @@ public String getRemoteUser() { public boolean isUserInRole(final String role) { if (CommonUtils.isBlank(role)) { - log.debug("No valid role provided. Returning false."); + logger.debug("No valid role provided. Returning false."); return false; } if (this.principal == null) { - log.debug("No Principal in Request. Returning false."); + logger.debug("No Principal in Request. Returning false."); return false; } if (CommonUtils.isBlank(roleAttribute)) { - log.debug("No Role Attribute Configured. Returning false."); + logger.debug("No Role Attribute Configured. Returning false."); return false; } final Object value = this.principal.getAttributes().get(roleAttribute); - + if (value instanceof Collection) { for (final Object o : (Collection) value) { if (rolesEqual(role, o)) { - log.debug("User [" + getRemoteUser() + "] is in role [" + role + "]: " + true); + logger.debug("User [{}] is in role [{}]: true", getRemoteUser(), role); return true; } } } final boolean isMember = rolesEqual(role, value); - log.debug("User [" + getRemoteUser() + "] is in role [" + role + "]: " + isMember); + logger.debug("User [{}] is in role [{}]: {}", getRemoteUser(), role, isMember); return isMember; } - + /** * Determines whether the given role is equal to the candidate * role attribute taking into account case sensitivity. diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/IOUtils.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/IOUtils.java new file mode 100644 index 000000000..d1d82f76f --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/IOUtils.java @@ -0,0 +1,92 @@ +/* + * 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.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 + } + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/MapNamespaceContext.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/MapNamespaceContext.java new file mode 100644 index 000000000..0afac39dc --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/MapNamespaceContext.java @@ -0,0 +1,80 @@ +/* + * 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 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 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(); + 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 namespaceMap) { + this.namespaceMap = namespaceMap; + } + + public String getNamespaceURI(final String prefix) { + return namespaceMap.get(prefix); + } + + public String getPrefix(final String namespaceURI) { + for (final Map.Entry 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(); + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/ReflectUtils.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/ReflectUtils.java index 0b1f963ef..1ec21162e 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/util/ReflectUtils.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/ReflectUtils.java @@ -1,22 +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: + * 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 + * 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 + * 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.beans.BeanInfo; @@ -54,7 +53,6 @@ public static Class loadClass(final String className) throws IllegalArgum } } - /** * Creates a new instance of the given class by passing the given arguments * to the constructor. @@ -62,10 +60,10 @@ public static Class loadClass(final String className) throws IllegalArgum * @param args Constructor arguments. * @return New instance of given class. */ - public static T newInstance(final String className, final Object ... args) { - return newInstance(ReflectUtils.loadClass(className), args); + public static T newInstance(final String className, final Object... args) { + return newInstance(ReflectUtils. loadClass(className), args); } - + /** * Creates a new instance of the given class by passing the given arguments * to the constructor. @@ -73,7 +71,7 @@ public static T newInstance(final String className, final Object ... args) { * @param args Constructor arguments. * @return New instance of given class. */ - public static T newInstance(final Class clazz, final Object ... args) { + public static T newInstance(final Class clazz, final Object... args) { final Class[] argClasses = new Class[args.length]; for (int i = 0; i < args.length; i++) { argClasses[i] = args[i].getClass(); @@ -139,7 +137,8 @@ public static void setProperty(final String propertyName, final Object value, fi * @param target Target JavaBean on which to set property. * @param info BeanInfo describing the target JavaBean. */ - public static void setProperty(final String propertyName, final Object value, final Object target, final BeanInfo info) { + public static void setProperty(final String propertyName, final Object value, final Object target, + final BeanInfo info) { try { final PropertyDescriptor pd = getPropertyDescriptor(info, propertyName); pd.getWriteMethod().invoke(target, value); diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/ThreadLocalXPathExpression.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/ThreadLocalXPathExpression.java new file mode 100644 index 000000000..e0d9b5dc9 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/ThreadLocalXPathExpression.java @@ -0,0 +1,105 @@ +/* + * 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.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 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"); + } + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/URIBuilder.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/URIBuilder.java new file mode 100644 index 000000000..7a6874a3b --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/URIBuilder.java @@ -0,0 +1,665 @@ +/* + * 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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLDecoder; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Pattern; + +/** + * A utility class borrowed from apache http-client to build uris. + * @author Misagh Moayyed + * @since 3.4 + */ +public final class URIBuilder { + private static final Logger LOGGER = LoggerFactory.getLogger(URIBuilder.class); + private static final Pattern IPV6_STD_PATTERN = Pattern.compile("^[0-9a-fA-F]{1,4}(:[0-9a-fA-F]{1,4}){7}$"); + + private String scheme; + private String encodedSchemeSpecificPart; + private String encodedAuthority; + private String userInfo; + private String encodedUserInfo; + private String host; + private int port; + private String path; + private String encodedPath; + private String encodedQuery; + private List queryParams; + private String query; + private boolean encode; + private String fragment; + private String encodedFragment; + + /** + * Constructs an empty instance. + */ + public URIBuilder() { + super(); + this.port = -1; + } + + public URIBuilder(final boolean encode) { + this(); + setEncode(encode); + } + + /** + * Construct an instance from the string which must be a valid URI. + * + * @param string a valid URI in string form + * @throws RuntimeException if the input is not a valid URI + */ + public URIBuilder(final String string) { + super(); + try { + digestURI(new URI(string)); + } catch (final URISyntaxException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + public URIBuilder(final String string, boolean encode) { + super(); + try { + setEncode(encode); + digestURI(new URI(string)); + } catch (final URISyntaxException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + + /** + * Construct an instance from the provided URI. + * @param uri the uri to digest + */ + public URIBuilder(final URI uri) { + super(); + digestURI(uri); + } + + private List parseQuery(final String query) { + + try { + final Charset utf8 = Charset.forName("UTF-8"); + if (query != null && !query.isEmpty()) { + final List list = new ArrayList(); + final String queryValue = URLDecoder.decode(query, utf8.name()); + final String[] parametersArray = queryValue.split("&"); + + for (final String parameter : parametersArray) { + final String[] parameterCombo = parameter.split("="); + if (parameterCombo.length == 2) { + list.add(new BasicNameValuePair(parameterCombo[0], parameterCombo[1])); + } + } + return list; + } + } catch (final UnsupportedEncodingException e) { + LOGGER.error(e.getMessage(), e); + } + return new ArrayList(); + } + + /** + * Builds a {@link URI} instance. + */ + public URI build() { + try { + return new URI(buildString()); + } catch (final URISyntaxException e) { + throw new RuntimeException(e); + } + } + + private static boolean isIPv6Address(final String input) { + return IPV6_STD_PATTERN.matcher(input).matches(); + } + + private String buildString() { + final StringBuilder sb = new StringBuilder(); + if (this.scheme != null) { + sb.append(this.scheme).append(':'); + } + if (this.encodedSchemeSpecificPart != null) { + sb.append(this.encodedSchemeSpecificPart); + } else { + if (this.encodedAuthority != null) { + sb.append("//").append(this.encodedAuthority); + } else if (this.host != null) { + sb.append("//"); + if (this.encodedUserInfo != null) { + sb.append(this.encodedUserInfo).append("@"); + } else if (this.userInfo != null) { + sb.append(encodeUserInfo(this.userInfo)).append("@"); + } + if (isIPv6Address(this.host)) { + sb.append("[").append(this.host).append("]"); + } else { + sb.append(this.host); + } + if (this.port >= 0) { + sb.append(":").append(this.port); + } + } + if (this.encodedPath != null) { + sb.append(normalizePath(this.encodedPath)); + } else if (this.path != null) { + sb.append(encodePath(normalizePath(this.path))); + } + if (this.encodedQuery != null) { + sb.append("?").append(this.encodedQuery); + } else if (this.queryParams != null && !this.queryParams.isEmpty()) { + sb.append("?").append(encodeUrlForm(this.queryParams)); + } else if (this.query != null) { + sb.append("?").append(encodeUric(this.query)); + } + } + if (this.encodedFragment != null) { + sb.append("#").append(this.encodedFragment); + } else if (this.fragment != null) { + sb.append("#").append(encodeUric(this.fragment)); + } + return sb.toString(); + } + + public URIBuilder digestURI(final URI uri) { + this.scheme = uri.getScheme(); + this.encodedSchemeSpecificPart = uri.getRawSchemeSpecificPart(); + this.encodedAuthority = uri.getRawAuthority(); + this.host = uri.getHost(); + this.port = uri.getPort(); + this.encodedUserInfo = uri.getRawUserInfo(); + this.userInfo = uri.getUserInfo(); + this.encodedPath = uri.getRawPath(); + this.path = uri.getPath(); + this.encodedQuery = uri.getRawQuery(); + this.queryParams = parseQuery(uri.getRawQuery()); + this.encodedFragment = uri.getRawFragment(); + this.fragment = uri.getFragment(); + return this; + } + + private String encodeUserInfo(final String userInfo) { + return this.encode ? CommonUtils.urlEncode(userInfo) : userInfo; + } + + private String encodePath(final String path) { + return this.encode ? CommonUtils.urlEncode(path) : path; + } + + private String encodeUrlForm(final List params) { + final StringBuilder result = new StringBuilder(); + for (final BasicNameValuePair parameter : params) { + final String encodedName = this.encode ? CommonUtils.urlEncode(parameter.getName()) : parameter.getName(); + final String encodedValue = this.encode ? CommonUtils.urlEncode(parameter.getValue()) : parameter.getValue(); + + if (result.length() > 0) { + result.append("&"); + } + result.append(encodedName); + if (encodedValue != null) { + result.append("="); + result.append(encodedValue); + } + } + return result.toString(); + } + + private String encodeUric(final String fragment) { + return this.encode ? CommonUtils.urlEncode(fragment) : fragment; + } + + public URIBuilder setEncode(boolean encode) { + this.encode = encode; + return this; + } + + /** + * Sets URI scheme. + */ + public URIBuilder setScheme(final String scheme) { + this.scheme = scheme; + return this; + } + + /** + * Sets URI user info. The value is expected to be unescaped and may contain non ASCII + * characters. + */ + public URIBuilder setUserInfo(final String userInfo) { + this.userInfo = userInfo; + this.encodedSchemeSpecificPart = null; + this.encodedAuthority = null; + this.encodedUserInfo = null; + return this; + } + + /** + * Sets URI user info as a combination of username and password. These values are expected to + * be unescaped and may contain non ASCII characters. + */ + public URIBuilder setUserInfo(final String username, final String password) { + return setUserInfo(username + ':' + password); + } + + /** + * Sets URI host. + */ + public URIBuilder setHost(final String host) { + this.host = host; + this.encodedSchemeSpecificPart = null; + this.encodedAuthority = null; + return this; + } + + /** + * Sets URI port. + */ + public URIBuilder setPort(final int port) { + this.port = port < 0 ? -1 : port; + this.encodedSchemeSpecificPart = null; + this.encodedAuthority = null; + return this; + } + + /** + * Sets URI path. The value is expected to be unescaped and may contain non ASCII characters. + */ + public URIBuilder setPath(final String path) { + this.path = path; + this.encodedSchemeSpecificPart = null; + this.encodedPath = null; + return this; + } + + public URIBuilder setEncodedPath(final String path) { + this.encodedPath = path; + this.encodedSchemeSpecificPart = null; + return this; + } + + /** + * Removes URI query. + */ + public URIBuilder removeQuery() { + this.queryParams = null; + this.query = null; + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + return this; + } + + /** + * Sets URI query parameters. The parameter name / values are expected to be unescaped + * and may contain non ASCII characters. + *

    + * Please note query parameters and custom query component are mutually exclusive. This method + * will remove custom query if present. + *

    + */ + public URIBuilder setParameters(final List nvps) { + this.queryParams = new ArrayList(); + this.queryParams.addAll(nvps); + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + this.query = null; + return this; + } + + public URIBuilder setParameters(final String queryParameters) { + this.queryParams = new ArrayList(); + this.queryParams.addAll(parseQuery(queryParameters)); + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + this.query = null; + return this; + } + + + + /** + * Adds URI query parameters. The parameter name / values are expected to be unescaped + * and may contain non ASCII characters. + *

    + * Please note query parameters and custom query component are mutually exclusive. This method + * will remove custom query if present. + *

    + */ + public URIBuilder addParameters(final List nvps) { + if (this.queryParams == null || this.queryParams.isEmpty()) { + this.queryParams = new ArrayList(); + } + this.queryParams.addAll(nvps); + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + this.query = null; + return this; + } + + /** + * Sets URI query parameters. The parameter name / values are expected to be unescaped + * and may contain non ASCII characters. + *

    + * Please note query parameters and custom query component are mutually exclusive. This method + * will remove custom query if present. + *

    + */ + public URIBuilder setParameters(final BasicNameValuePair... nvps) { + if (this.queryParams == null) { + this.queryParams = new ArrayList(); + } else { + this.queryParams.clear(); + } + for (final BasicNameValuePair nvp: nvps) { + this.queryParams.add(nvp); + } + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + this.query = null; + return this; + } + + /** + * Adds parameter to URI query. The parameter name and value are expected to be unescaped + * and may contain non ASCII characters. + *

    + * Please note query parameters and custom query component are mutually exclusive. This method + * will remove custom query if present. + *

    + */ + public URIBuilder addParameter(final String param, final String value) { + if (this.queryParams == null) { + this.queryParams = new ArrayList(); + } + this.queryParams.add(new BasicNameValuePair(param, value)); + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + this.query = null; + return this; + } + + /** + * Sets parameter of URI query overriding existing value if set. The parameter name and value + * are expected to be unescaped and may contain non ASCII characters. + *

    + * Please note query parameters and custom query component are mutually exclusive. This method + * will remove custom query if present. + *

    + */ + public URIBuilder setParameter(final String param, final String value) { + if (this.queryParams == null) { + this.queryParams = new ArrayList(); + } + if (!this.queryParams.isEmpty()) { + for (final Iterator it = this.queryParams.iterator(); it.hasNext(); ) { + final BasicNameValuePair nvp = it.next(); + if (nvp.getName().equals(param)) { + it.remove(); + } + } + } + this.queryParams.add(new BasicNameValuePair(param, value)); + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + this.query = null; + return this; + } + + /** + * Clears URI query parameters. + */ + public URIBuilder clearParameters() { + this.queryParams = null; + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + return this; + } + + /** + * Sets custom URI query. The value is expected to be unescaped and may contain non ASCII + * characters. + *

    + * Please note query parameters and custom query component are mutually exclusive. This method + * will remove query parameters if present. + *

    + */ + public URIBuilder setCustomQuery(final String query) { + this.query = query; + this.encodedQuery = null; + this.encodedSchemeSpecificPart = null; + this.queryParams = null; + return this; + } + + /** + * Sets URI fragment. The value is expected to be unescaped and may contain non ASCII + * characters. + */ + public URIBuilder setFragment(final String fragment) { + this.fragment = fragment; + this.encodedFragment = null; + return this; + } + + public URIBuilder setEncodedFragment(final String fragment) { + this.fragment = null; + this.encodedFragment = fragment; + return this; + } + + public URIBuilder setEncodedQuery(final String query) { + this.query = null; + this.encodedFragment = query; + return this; + } + + public boolean isAbsolute() { + return this.scheme != null; + } + + public boolean isOpaque() { + return this.path == null; + } + + public String getScheme() { + return this.scheme; + } + + public String getUserInfo() { + return this.userInfo; + } + + public String getHost() { + return this.host; + } + + public int getPort() { + return this.port; + } + + public String getPath() { + return this.path; + } + + public List getQueryParams() { + if (this.queryParams != null) { + return new ArrayList(this.queryParams); + } + return new ArrayList(); + + } + + public String getFragment() { + return this.fragment; + } + + @Override + public String toString() { + return buildString(); + } + + private static String normalizePath(final String path) { + String s = path; + if (s == null) { + return null; + } + int n = 0; + for (; n < s.length(); n++) { + if (s.charAt(n) != '/') { + break; + } + } + if (n > 1) { + s = s.substring(n - 1); + } + return s; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final URIBuilder that = (URIBuilder) o; + + if (port != that.port) return false; + if (encode != that.encode) return false; + if (scheme != null ? !scheme.equals(that.scheme) : that.scheme != null) return false; + if (encodedSchemeSpecificPart != null ? !encodedSchemeSpecificPart.equals(that.encodedSchemeSpecificPart) : that.encodedSchemeSpecificPart != null) + return false; + if (encodedAuthority != null ? !encodedAuthority.equals(that.encodedAuthority) : that.encodedAuthority != null) + return false; + if (userInfo != null ? !userInfo.equals(that.userInfo) : that.userInfo != null) return false; + if (encodedUserInfo != null ? !encodedUserInfo.equals(that.encodedUserInfo) : that.encodedUserInfo != null) + return false; + if (host != null ? !host.equals(that.host) : that.host != null) return false; + if (path != null ? !path.equals(that.path) : that.path != null) return false; + if (encodedPath != null ? !encodedPath.equals(that.encodedPath) : that.encodedPath != null) return false; + if (encodedQuery != null ? !encodedQuery.equals(that.encodedQuery) : that.encodedQuery != null) return false; + if (queryParams != null ? !queryParams.equals(that.queryParams) : that.queryParams != null) return false; + if (query != null ? !query.equals(that.query) : that.query != null) return false; + if (fragment != null ? !fragment.equals(that.fragment) : that.fragment != null) return false; + return !(encodedFragment != null ? !encodedFragment.equals(that.encodedFragment) : that.encodedFragment != null); + + } + + @Override + public int hashCode() { + int result = scheme != null ? scheme.hashCode() : 0; + result = 31 * result + (encodedSchemeSpecificPart != null ? encodedSchemeSpecificPart.hashCode() : 0); + result = 31 * result + (encodedAuthority != null ? encodedAuthority.hashCode() : 0); + result = 31 * result + (userInfo != null ? userInfo.hashCode() : 0); + result = 31 * result + (encodedUserInfo != null ? encodedUserInfo.hashCode() : 0); + result = 31 * result + (host != null ? host.hashCode() : 0); + result = 31 * result + port; + result = 31 * result + (path != null ? path.hashCode() : 0); + result = 31 * result + (encodedPath != null ? encodedPath.hashCode() : 0); + result = 31 * result + (encodedQuery != null ? encodedQuery.hashCode() : 0); + result = 31 * result + (queryParams != null ? queryParams.hashCode() : 0); + result = 31 * result + (query != null ? query.hashCode() : 0); + result = 31 * result + (encode ? 1 : 0); + result = 31 * result + (fragment != null ? fragment.hashCode() : 0); + result = 31 * result + (encodedFragment != null ? encodedFragment.hashCode() : 0); + return result; + } + + public static class BasicNameValuePair implements Cloneable, Serializable { + private static final long serialVersionUID = -6437800749411518984L; + + private final String name; + private final String value; + + /** + * Default Constructor taking a name and a value. The value may be null. + * + * @param name The name. + * @param value The value. + */ + public BasicNameValuePair(final String name, final String value) { + super(); + this.name = name; + this.value = value; + } + + public String getName() { + return this.name; + } + + public String getValue() { + return this.value; + } + + @Override + public String toString() { + // don't call complex default formatting for a simple toString + + if (this.value == null) { + return name; + } + final int len = this.name.length() + 1 + this.value.length(); + final StringBuilder buffer = new StringBuilder(len); + buffer.append(this.name); + buffer.append("="); + buffer.append(this.value); + return buffer.toString(); + } + + @Override + public boolean equals(final Object object) { + if (this == object) { + return true; + } + + if (object == null) { + return false; + } + + if (object instanceof BasicNameValuePair) { + final BasicNameValuePair that = (BasicNameValuePair) object; + return this.name.equals(that.name) + && this.value.equals(that.value); + } + return false; + } + + @Override + public int hashCode() { + return 133 * this.name.hashCode() * this.value.hashCode(); + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/XmlUtils.java b/cas-client-core/src/main/java/org/jasig/cas/client/util/XmlUtils.java index 62abfdfe0..77831f495 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/util/XmlUtils.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/XmlUtils.java @@ -1,42 +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: + * 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 + * 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 + * 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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import java.io.StringReader; +import java.util.*; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.w3c.dom.Document; -import org.w3c.dom.NodeList; 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 org.xml.sax.helpers.XMLReaderFactory; -import javax.xml.parsers.DocumentBuilder; +import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.List; +import javax.xml.parsers.SAXParserFactory; /** * Common utilities for easily parsing XML without duplicating logic. @@ -50,7 +47,35 @@ public final class XmlUtils { /** * Static instance of Commons Logging. */ - private final static Log LOG = LogFactory.getLog(XmlUtils.class); + 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 features = new HashMap(); + features.put(XMLConstants.FEATURE_SECURE_PROCESSING, true); + features.put("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + for (final Map.Entry 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. @@ -59,14 +84,17 @@ public final class XmlUtils { */ public static XMLReader getXmlReader() { try { - final XMLReader reader = XMLReaderFactory.createXMLReader(); + final XMLReader reader = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); + reader.setFeature("http://xml.org/sax/features/namespaces", true); + reader.setFeature("http://xml.org/sax/features/namespace-prefixes", false); reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); return reader; - } catch (final SAXException e) { + } catch (final Exception e) { throw new RuntimeException("Unable to create XMLReader", e); } } + /** * Retrieve the text for a group of elements. Each text element is an entry * in a list. @@ -76,8 +104,7 @@ public static XMLReader getXmlReader() { * @param element the element to look for * @return the list of text from the elements. */ - public static List getTextForElements(final String xmlAsString, - final String element) { + public static List getTextForElements(final String xmlAsString, final String element) { final List elements = new ArrayList(2); final XMLReader reader = getXmlReader(); @@ -87,16 +114,14 @@ public static List getTextForElements(final String xmlAsString, private StringBuilder buffer = new StringBuilder(); - public void startElement(final String uri, final String localName, - final String qName, final Attributes attributes) - throws SAXException { + public void startElement(final String uri, final String localName, final String qName, + final Attributes attributes) throws SAXException { if (localName.equals(element)) { this.foundElement = true; } } - public void endElement(final String uri, final String localName, - final String qName) throws SAXException { + public void endElement(final String uri, final String localName, final String qName) throws SAXException { if (localName.equals(element)) { this.foundElement = false; elements.add(this.buffer.toString()); @@ -104,8 +129,7 @@ public void endElement(final String uri, final String localName, } } - public void characters(char[] ch, int start, int length) - throws SAXException { + public void characters(char[] ch, int start, int length) throws SAXException { if (this.foundElement) { this.buffer.append(ch, start, length); } @@ -118,7 +142,7 @@ public void characters(char[] ch, int start, int length) try { reader.parse(new InputSource(new StringReader(xmlAsString))); } catch (final Exception e) { - LOG.error(e, e); + LOGGER.error(e.getMessage(), e); return null; } @@ -133,8 +157,7 @@ public void characters(char[] ch, int start, int length) * @param element the element to look for * @return the text value of the element. */ - public static String getTextForElement(final String xmlAsString, - final String element) { + public static String getTextForElement(final String xmlAsString, final String element) { final XMLReader reader = getXmlReader(); final StringBuilder builder = new StringBuilder(); @@ -142,23 +165,20 @@ public static String getTextForElement(final String xmlAsString, private boolean foundElement = false; - public void startElement(final String uri, final String localName, - final String qName, final Attributes attributes) - throws SAXException { + public void startElement(final String uri, final String localName, final String qName, + final Attributes attributes) throws SAXException { if (localName.equals(element)) { this.foundElement = true; } } - public void endElement(final String uri, final String localName, - final String qName) throws SAXException { + public void endElement(final String uri, final String localName, final String qName) throws SAXException { if (localName.equals(element)) { this.foundElement = false; } } - public void characters(char[] ch, int start, int length) - throws SAXException { + public void characters(char[] ch, int start, int length) throws SAXException { if (this.foundElement) { builder.append(ch, start, length); } @@ -171,33 +191,10 @@ public void characters(char[] ch, int start, int length) try { reader.parse(new InputSource(new StringReader(xmlAsString))); } catch (final Exception e) { - LOG.error(e, e); + LOGGER.error(e.getMessage(), e); return null; } return builder.toString(); } - - /** - * Retrieve the child nodes from xml string, for a specific element. - * - * @param xmlAsString the xml response - * @param tagName the element to look for - * @return the {@link org.w3c.dom.NodeList NodeList} containing the child nodes. - * @throws ParserConfigurationException - * @throws IOException - * @throws SAXException - */ - public static NodeList getNodeListForElements(final String xmlAsString, final String tagName) - throws ParserConfigurationException, - IOException, - SAXException { - - final DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - final InputSource inStream = new InputSource(); - inStream.setCharacterStream(new StringReader(xmlAsString)); - final Document document = documentBuilder.parse(inStream); - - return document.getElementsByTagName(tagName).item(0).getChildNodes(); - } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/util/package.html b/cas-client-core/src/main/java/org/jasig/cas/client/util/package.html index 300a79b89..fcf85e2f9 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/util/package.html +++ b/cas-client-core/src/main/java/org/jasig/cas/client/util/package.html @@ -5,20 +5,19 @@ 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: + 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 + 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 + 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. --> -

    The validation package includes interfaces for validating Tickets, as well as the common implementations.

    diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractCasProtocolUrlBasedTicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractCasProtocolUrlBasedTicketValidator.java index 84d9e6740..146280d7e 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractCasProtocolUrlBasedTicketValidator.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractCasProtocolUrlBasedTicketValidator.java @@ -1,27 +1,25 @@ -/** +/* * 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: + * 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 + * 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 + * 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.util.CommonUtils; - import java.net.URL; +import org.jasig.cas.client.util.CommonUtils; /** * Abstract class that knows the protocol for validating a CAS ticket. @@ -36,18 +34,10 @@ protected AbstractCasProtocolUrlBasedTicketValidator(final String casServerUrlPr 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. */ protected final String retrieveResponseFromServer(final URL validationUrl, final String ticket) { - if (this.hostnameVerifier != null) { - return CommonUtils.getResponseFromServer(validationUrl, this.hostnameVerifier, getEncoding()); - } else { - return CommonUtils.getResponseFromServer(validationUrl, getEncoding()); - } + return CommonUtils.getResponseFromServer(validationUrl, getURLConnectionFactory(), getEncoding()); } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractTicketValidationFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractTicketValidationFilter.java index 71b62e7ed..51df207b1 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractTicketValidationFilter.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractTicketValidationFilter.java @@ -1,47 +1,49 @@ -/** +/* * 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: + * 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 + * 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 + * 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.util.AbstractCasFilter; -import org.jasig.cas.client.util.CommonUtils; -import org.jasig.cas.client.util.ReflectUtils; - +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; import javax.net.ssl.HostnameVerifier; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; +import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; + +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; /** * The filter that handles all the work of validating ticket requests. *

    * This filter can be configured with the following values: *

      - *
    • redirectAfterValidation - redirect the CAS client to the same URL without the ticket.
    • + *
    • redirectAfterValidation - redirect the CAS client to the same URL without the ticket. + * (default: true, Will be forced to false when {@link #useSession} is false.)
    • *
    • exceptionOnValidationFailure - throw an exception if the validation fails. Otherwise, continue - * processing.
    • - *
    • useSession - store any of the useful information in a session attribute.
    • + * processing. (default: true) + *
    • useSession - store any of the useful information in a session attribute. (default: true)
    • + *
    • hostnameVerifier - name of class implementing a {@link HostnameVerifier}.
    • + *
    • hostnameVerifierConfig - name of configuration class (constructor argument of verifier).
    • *
    * * @author Scott Battaglia @@ -58,13 +60,21 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter { * successful validation to remove the ticket parameter from the query * string. */ - private boolean redirectAfterValidation = false; + private boolean redirectAfterValidation = true; /** Determines whether an exception is thrown when there is a ticket validation failure. */ - private boolean exceptionOnValidationFailure = true; + private boolean exceptionOnValidationFailure = false; + /** + * Specify whether the Assertion should be stored in a session + * attribute {@link AbstractCasFilter#CONST_CAS_ASSERTION}. + */ private boolean useSession = true; + protected AbstractTicketValidationFilter(final Protocol protocol) { + super(protocol); + } + /** * Template method to return the appropriate validator. * @@ -74,18 +84,39 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter { protected TicketValidator getTicketValidator(final FilterConfig filterConfig) { return this.ticketValidator; } - + + /** + * Gets the ssl config to use for HTTPS connections + * if one is configured for this filter. + * @return Properties that can contains key/trust info for Client Side Certificates + */ + protected Properties getSSLConfig() { + final Properties properties = new Properties(); + final String fileName = getString(ConfigurationKeys.SSL_CONFIG_FILE); + + if (fileName != null) { + FileInputStream fis = null; + try { + fis = new FileInputStream(fileName); + properties.load(fis); + logger.trace("Loaded {} entries from {}", properties.size(), fileName); + } catch (final IOException ioe) { + logger.error(ioe.getMessage(), ioe); + } finally { + CommonUtils.closeQuietly(fis); + } + } + return properties; + } + /** * 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); - log.trace("Using hostnameVerifier parameter: " + className); - final String config = getPropertyFromInitParams(filterConfig, "hostnameVerifierConfig", null); - log.trace("Using hostnameVerifierConfig parameter: " + config); + protected HostnameVerifier getHostnameVerifier() { + final Class className = getClass(ConfigurationKeys.HOSTNAME_VERIFIER); + final String config = getString(ConfigurationKeys.HOSTNAME_VERIFIER_CONFIG); if (className != null) { if (config != null) { return ReflectUtils.newInstance(className, config); @@ -97,12 +128,15 @@ protected HostnameVerifier getHostnameVerifier(final FilterConfig filterConfig) } protected void initInternal(final FilterConfig filterConfig) throws ServletException { - setExceptionOnValidationFailure(parseBoolean(getPropertyFromInitParams(filterConfig, "exceptionOnValidationFailure", "true"))); - log.trace("Setting exceptionOnValidationFailure parameter: " + this.exceptionOnValidationFailure); - setRedirectAfterValidation(parseBoolean(getPropertyFromInitParams(filterConfig, "redirectAfterValidation", "true"))); - log.trace("Setting redirectAfterValidation parameter: " + this.redirectAfterValidation); - setUseSession(parseBoolean(getPropertyFromInitParams(filterConfig, "useSession", "true"))); - log.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."); + setRedirectAfterValidation(false); + } + setTicketValidator(getTicketValidator(filterConfig)); super.initInternal(filterConfig); } @@ -122,7 +156,8 @@ public void init() { * @throws IOException if there is an I/O problem * @throws ServletException if there is a servlet problem. */ - protected boolean preFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { + protected boolean preFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, + final FilterChain filterChain) throws IOException, ServletException { return true; } @@ -135,8 +170,9 @@ protected boolean preFilter(final ServletRequest servletRequest, final ServletRe * @param response the HttpServletResponse. * @param assertion the successful Assertion from the server. */ - protected void onSuccessfulValidation(final HttpServletRequest request, final HttpServletResponse response, final Assertion assertion) { - // nothing to do here. + protected void onSuccessfulValidation(final HttpServletRequest request, final HttpServletResponse response, + final Assertion assertion) { + // nothing to do here. } /** @@ -150,7 +186,8 @@ protected void onFailedValidation(final HttpServletRequest request, final HttpSe // nothing to do here. } - public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { + public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, + final FilterChain filterChain) throws IOException, ServletException { if (!preFilter(servletRequest, servletResponse, filterChain)) { return; @@ -161,16 +198,13 @@ public final void doFilter(final ServletRequest servletRequest, final ServletRes final String ticket = retrieveTicketFromRequest(request); if (CommonUtils.isNotBlank(ticket)) { - if (log.isDebugEnabled()) { - log.debug("Attempting to validate ticket: " + ticket); - } + logger.debug("Attempting to validate ticket: {}", ticket); try { - final Assertion assertion = this.ticketValidator.validate(ticket, constructServiceUrl(request, response)); + final Assertion assertion = this.ticketValidator.validate(ticket, + constructServiceUrl(request, response)); - if (log.isDebugEnabled()) { - log.debug("Successfully authenticated user: " + assertion.getPrincipal().getName()); - } + logger.debug("Successfully authenticated user: {}", assertion.getPrincipal().getName()); request.setAttribute(CONST_CAS_ASSERTION, assertion); @@ -180,13 +214,12 @@ public final void doFilter(final ServletRequest servletRequest, final ServletRes onSuccessfulValidation(request, response, assertion); if (this.redirectAfterValidation) { - log. debug("Redirecting after successful ticket validation."); + logger.debug("Redirecting after successful ticket validation."); response.sendRedirect(constructServiceUrl(request, response)); return; } } catch (final TicketValidationException e) { - response.setStatus(HttpServletResponse.SC_FORBIDDEN); - log.warn(e, e); + logger.debug(e.getMessage(), e); onFailedValidation(request, response); @@ -194,7 +227,7 @@ public final void doFilter(final ServletRequest servletRequest, final ServletRes throw new ServletException(e); } - response.sendError(HttpServletResponse.SC_FORBIDDEN); + response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); return; } @@ -205,8 +238,8 @@ public final void doFilter(final ServletRequest servletRequest, final ServletRes } public final void setTicketValidator(final TicketValidator ticketValidator) { - this.ticketValidator = ticketValidator; -} + this.ticketValidator = ticketValidator; + } public final void setRedirectAfterValidation(final boolean redirectAfterValidation) { this.redirectAfterValidation = redirectAfterValidation; @@ -219,4 +252,4 @@ public final void setExceptionOnValidationFailure(final boolean exceptionOnValid public final void setUseSession(final boolean useSession) { this.useSession = useSession; } -} +} \ No newline at end of file diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractUrlBasedTicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractUrlBasedTicketValidator.java index 0012b5e86..59c4d88e1 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractUrlBasedTicketValidator.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AbstractUrlBasedTicketValidator.java @@ -1,55 +1,50 @@ -/** +/* * 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: + * 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 + * 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 + * 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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.jasig.cas.client.util.CommonUtils; - import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; - -import javax.net.ssl.HostnameVerifier; +import org.jasig.cas.client.ssl.HttpURLConnectionFactory; +import org.jasig.cas.client.ssl.HttpsURLConnectionFactory; +import org.jasig.cas.client.util.CommonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Abstract validator implementation for tickets that must be validated against a server. * * @author Scott Battaglia - * @version $Revision$ $Date$ * @since 3.1 */ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator { + protected final Logger logger = LoggerFactory.getLogger(getClass()); + /** - * Commons Logging instance. - */ - protected final Log log = LogFactory.getLog(getClass()); - - /** - * Hostname verifier used when making an SSL request to the CAS server. + * URLConnection factory instance to use when making validation requests to the CAS server. + * Defaults to {@link HttpsURLConnectionFactory} */ - protected HostnameVerifier hostnameVerifier; + private HttpURLConnectionFactory urlConnectionFactory = new HttpsURLConnectionFactory(); /** * Prefix for the CAS server. Should be everything up to the url endpoint, including the /. @@ -66,7 +61,7 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator /** * A map containing custom parameters to pass to the validation url. */ - private Map customParameters; + private Map customParameters; private String encoding; @@ -85,7 +80,7 @@ protected AbstractUrlBasedTicketValidator(final String casServerUrlPrefix) { * * @param urlParameters the map containing the parameters. */ - protected void populateUrlAttributeMap(final Map urlParameters) { + protected void populateUrlAttributeMap(final Map urlParameters) { // nothing to do } @@ -95,13 +90,6 @@ protected void populateUrlAttributeMap(final Map urlParameters) { */ 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. @@ -111,26 +99,27 @@ protected void populateUrlAttributeMap(final Map urlParameters) { * @return the fully constructed URL. */ protected final String constructValidationUrl(final String ticket, final String serviceUrl) { - final Map urlParameters = new HashMap(); + final Map urlParameters = new HashMap(); - log.debug("Placing URL parameters in map."); + 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"); } - log.debug("Calling template URL attribute map."); + logger.debug("Calling template URL attribute map."); populateUrlAttributeMap(urlParameters); - log.debug("Loading custom parameters from configuration."); + logger.debug("Loading custom parameters from configuration."); if (this.customParameters != null) { urlParameters.putAll(this.customParameters); } final String suffix = getUrlSuffix(); - final StringBuilder buffer = new StringBuilder(urlParameters.size()*10 + this.casServerUrlPrefix.length() + suffix.length() +1); + final StringBuilder buffer = new StringBuilder(urlParameters.size() * 10 + this.casServerUrlPrefix.length() + + suffix.length() + 1); int i = 0; @@ -140,7 +129,7 @@ protected final String constructValidationUrl(final String ticket, final String } buffer.append(suffix); - for (Map.Entry entry : urlParameters.entrySet()) { + for (Map.Entry entry : urlParameters.entrySet()) { final String key = entry.getKey(); final String value = entry.getValue(); @@ -148,7 +137,8 @@ protected final String constructValidationUrl(final String ticket, final String buffer.append(i++ == 0 ? "?" : "&"); buffer.append(key); buffer.append("="); - buffer.append(value); + final String encodedValue = encodeUrl(value); + buffer.append(encodedValue); } } @@ -163,10 +153,10 @@ protected final String constructValidationUrl(final String ticket, final String * @return the encoded url, or the original url if "UTF-8" character encoding could not be found. */ protected final String encodeUrl(final String url) { - if (url == null) { - return null; - } - + if (url == null) { + return null; + } + try { return URLEncoder.encode(url, "UTF-8"); } catch (final UnsupportedEncodingException e) { @@ -195,24 +185,18 @@ protected final String encodeUrl(final String url) { protected abstract String retrieveResponseFromServer(URL validationUrl, String ticket); public final Assertion validate(final String ticket, final String service) throws TicketValidationException { - - final String validationUrl = constructValidationUrl(ticket, service); - if (log.isDebugEnabled()) { - log.debug("Constructing validation url: " + validationUrl); - } + logger.debug("Constructing validation url: {}", validationUrl); try { - log.debug("Retrieving response from server."); + logger.debug("Retrieving response from server."); final String serverResponse = retrieveResponseFromServer(new URL(validationUrl), ticket); if (serverResponse == null) { throw new TicketValidationException("The CAS server returned no response."); } - - if (log.isDebugEnabled()) { - log.debug("Server response: " + serverResponse); - } + + logger.debug("Server response: {}", serverResponse); return parseResponseFromServer(serverResponse); } catch (final MalformedURLException e) { @@ -224,13 +208,9 @@ public final void setRenew(final boolean renew) { this.renew = renew; } - public final void setCustomParameters(final Map customParameters) { + public final void setCustomParameters(final Map customParameters) { this.customParameters = customParameters; } - - public final void setHostnameVerifier(final HostnameVerifier verifier) { - this.hostnameVerifier = verifier; - } public final void setEncoding(final String encoding) { this.encoding = encoding; @@ -251,4 +231,12 @@ protected final String getCasServerUrlPrefix() { protected final Map getCustomParameters() { return this.customParameters; } -} \ No newline at end of file + + protected HttpURLConnectionFactory getURLConnectionFactory() { + return this.urlConnectionFactory; + } + + public void setURLConnectionFactory(final HttpURLConnectionFactory urlConnectionFactory) { + this.urlConnectionFactory = urlConnectionFactory; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Assertion.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Assertion.java index 75ac70d40..06d98aa59 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Assertion.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Assertion.java @@ -1,29 +1,27 @@ -/** +/* * 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: + * 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 + * 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 + * 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.authentication.AttributePrincipal; - import java.io.Serializable; import java.util.Date; import java.util.Map; +import org.jasig.cas.client.authentication.AttributePrincipal; /** * Represents a response to a validation request. @@ -48,12 +46,20 @@ public interface Assertion extends Serializable { */ Date getValidUntilDate(); + /** + * The date the authentication actually occurred on. If its unable to be determined, it should be set to the current + * time. + * + * @return the authentication date, or the current time if it can't be determined. + */ + Date getAuthenticationDate(); + /** * The key/value pairs associated with this assertion. * * @return the map of attributes. */ - Map getAttributes(); + Map getAttributes(); /** * The principal for which this assertion is valid. @@ -61,4 +67,12 @@ public interface Assertion extends Serializable { * @return the principal. */ AttributePrincipal getPrincipal(); + + /** + * Determines whether an Assertion is considered usable or not. A naive implementation may just check the date validity. + * + * @return true if its valid, false otherwise. + * @since 3.3.0 (though in 3.3.0, no one actually calls this) + */ + boolean isValid(); } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/AssertionImpl.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AssertionImpl.java index a845fe871..86e286fd7 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/AssertionImpl.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/AssertionImpl.java @@ -1,31 +1,29 @@ -/** +/* * 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: + * 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 + * 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 + * 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.authentication.AttributePrincipal; -import org.jasig.cas.client.authentication.AttributePrincipalImpl; -import org.jasig.cas.client.util.CommonUtils; - import java.util.Collections; import java.util.Date; import java.util.Map; +import org.jasig.cas.client.authentication.AttributePrincipal; +import org.jasig.cas.client.authentication.AttributePrincipalImpl; +import org.jasig.cas.client.util.CommonUtils; /** * Concrete Implementation of the {@link Assertion}. @@ -38,16 +36,18 @@ public final class AssertionImpl implements Assertion { /** Unique Id for serialization. */ - private static final long serialVersionUID = -7767943925833639221L; + private static final long serialVersionUID = -7767943925833639221L; - /** The date from which the assertion is valid. */ + /** The date from which the assertion is valid. */ private final Date validFromDate; /** The date the assertion is valid until. */ private final Date validUntilDate; + private final Date authenticationDate; + /** Map of key/value pairs associated with this assertion. I.e. authentication type. */ - private final Map attributes; + private final Map attributes; /** The principal for which this assertion is valid for. */ private final AttributePrincipal principal; @@ -58,7 +58,7 @@ public final class AssertionImpl implements Assertion { * @param name the name of the principal for which this assertion is valid. */ public AssertionImpl(final String name) { - this(new AttributePrincipalImpl(name)); + this(new AttributePrincipalImpl(name)); } /** @@ -67,7 +67,7 @@ public AssertionImpl(final String name) { * @param principal the Principal to associate with the Assertion. */ public AssertionImpl(final AttributePrincipal principal) { - this(principal, Collections.emptyMap()); + this(principal, Collections. emptyMap()); } /** @@ -76,8 +76,8 @@ public AssertionImpl(final AttributePrincipal principal) { * @param principal the Principal to associate with the Assertion. * @param attributes the key/value pairs for this attribute. */ - public AssertionImpl(final AttributePrincipal principal, final Map attributes) { - this(principal, new Date(), null, attributes); + public AssertionImpl(final AttributePrincipal principal, final Map attributes) { + this(principal, new Date(), null, new Date(), attributes); } /** @@ -88,16 +88,23 @@ public AssertionImpl(final AttributePrincipal principal, final Map attributes) { + public AssertionImpl(final AttributePrincipal principal, final Date validFromDate, final Date validUntilDate, + final Date authenticationDate, final Map attributes) { this.principal = principal; this.validFromDate = validFromDate; this.validUntilDate = validUntilDate; this.attributes = attributes; + this.authenticationDate = authenticationDate; CommonUtils.assertNotNull(this.principal, "principal cannot be null."); CommonUtils.assertNotNull(this.validFromDate, "validFromDate cannot be null."); CommonUtils.assertNotNull(this.attributes, "attributes cannot be null."); } + + public Date getAuthenticationDate() { + return this.authenticationDate; + } + public Date getValidFromDate() { return this.validFromDate; } @@ -106,11 +113,20 @@ public Date getValidUntilDate() { return this.validUntilDate; } - public Map getAttributes() { + public Map getAttributes() { return this.attributes; } public AttributePrincipal getPrincipal() { return this.principal; } + + public boolean isValid() { + if (this.validFromDate == null) { + return true; + } + + final Date now = new Date(); + return this.validFromDate.before(now) && (this.validUntilDate == null || this.validUntilDate.after(now)); + } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas10TicketValidationFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas10TicketValidationFilter.java index 51f59e7b2..aca0dee44 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas10TicketValidationFilter.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas10TicketValidationFilter.java @@ -1,43 +1,54 @@ -/** +/* * 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: + * 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 + * 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 + * 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 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. *

    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 { + public Cas10TicketValidationFilter() { + super(Protocol.CAS1); + } + protected final TicketValidator getTicketValidator(final FilterConfig filterConfig) { - final String casServerUrlPrefix = getPropertyFromInitParams(filterConfig, "casServerUrlPrefix", null); + final String casServerUrlPrefix = getString(ConfigurationKeys.CAS_SERVER_URL_PREFIX); final Cas10TicketValidator validator = new Cas10TicketValidator(casServerUrlPrefix); - validator.setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false"))); - validator.setHostnameVerifier(getHostnameVerifier(filterConfig)); - validator.setEncoding(getPropertyFromInitParams(filterConfig, "encoding", null)); + validator.setRenew(getBoolean(ConfigurationKeys.RENEW)); + + final HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(), + getSSLConfig()); + validator.setURLConnectionFactory(factory); + validator.setEncoding(getString(ConfigurationKeys.ENCODING)); return validator; } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas10TicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas10TicketValidator.java index 7bdfd0442..dce9a50a0 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas10TicketValidator.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas10TicketValidator.java @@ -1,22 +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: + * 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 + * 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 + * 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.BufferedReader; diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ProxyReceivingTicketValidationFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ProxyReceivingTicketValidationFilter.java index fb97ac3a2..5fe133715 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ProxyReceivingTicketValidationFilter.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ProxyReceivingTicketValidationFilter.java @@ -1,43 +1,43 @@ -/** +/* * 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: + * 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 + * 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 + * 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.IOException; import java.util.*; - -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; +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. - *

    + *

    * 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}. * @@ -45,16 +45,20 @@ * @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"}; - - 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,24 +67,38 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal private TimerTask timerTask; private int millisBetweenCleanUps; - + + protected Class defaultServiceTicketValidatorClass; + + protected Class defaultProxyTicketValidatorClass; + /** * Storage location of ProxyGrantingTickets and Proxy Ticket IOUs. */ private ProxyGrantingTicketStorage proxyGrantingTicketStorage = new ProxyGrantingTicketStorageImpl(); + public Cas20ProxyReceivingTicketValidationFilter() { + this(Protocol.CAS2); + this.defaultServiceTicketValidatorClass = Cas20ServiceTicketValidator.class; + this.defaultProxyTicketValidatorClass = Cas20ProxyTicketValidator.class; + } + + protected Cas20ProxyReceivingTicketValidationFilter(final Protocol protocol) { + super(protocol); + } + protected void initInternal(final FilterConfig filterConfig) throws ServletException { - setProxyReceptorUrl(getPropertyFromInitParams(filterConfig, "proxyReceptorUrl", null)); + setProxyReceptorUrl(getString(ConfigurationKeys.PROXY_RECEPTOR_URL)); - final String proxyGrantingTicketStorageClass = getPropertyFromInitParams(filterConfig, "proxyGrantingTicketStorageClass", null); + final Class 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); @@ -94,8 +112,7 @@ protected void initInternal(final FilterConfig filterConfig) throws ServletExcep } } - log.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); } @@ -113,6 +130,15 @@ public void init() { this.timer.schedule(this.timerTask, this.millisBetweenCleanUps, this.millisBetweenCleanUps); } + private T createNewTicketValidator(final Class ticketValidatorClass, final String casServerUrlPrefix, + final Class clazz) { + if (ticketValidatorClass == null) { + return ReflectUtils.newInstance(clazz, casServerUrlPrefix); + } + + return (T) ReflectUtils.newInstance(ticketValidatorClass, casServerUrlPrefix); + } + /** * Constructs a Cas20ServiceTicketValidator or a Cas20ProxyTicketValidator based on supplied parameters. * @@ -120,29 +146,37 @@ public void init() { * @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 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 ticketValidatorClass = getClass(ConfigurationKeys.TICKET_VALIDATOR_CLASS); final Cas20ServiceTicketValidator validator; - if (CommonUtils.isNotBlank(allowAnyProxy) || CommonUtils.isNotBlank(allowedProxyChains)) { - final Cas20ProxyTicketValidator v = new Cas20ProxyTicketValidator(casServerUrlPrefix); - v.setAcceptAnyProxy(parseBoolean(allowAnyProxy)); + if (allowAnyProxy || CommonUtils.isNotBlank(allowedProxyChains)) { + final Cas20ProxyTicketValidator v = createNewTicketValidator(ticketValidatorClass, casServerUrlPrefix, + this.defaultProxyTicketValidatorClass); + v.setAcceptAnyProxy(allowAnyProxy); v.setAllowedProxyChains(CommonUtils.createProxyList(allowedProxyChains)); validator = v; } else { - validator = new Cas20ServiceTicketValidator(casServerUrlPrefix); + validator = createNewTicketValidator(ticketValidatorClass, casServerUrlPrefix, + this.defaultServiceTicketValidatorClass); } - validator.setProxyCallbackUrl(getPropertyFromInitParams(filterConfig, "proxyCallbackUrl", null)); + validator.setProxyCallbackUrl(getString(ConfigurationKeys.PROXY_CALLBACK_URL)); validator.setProxyGrantingTicketStorage(this.proxyGrantingTicketStorage); - validator.setProxyRetriever(new Cas20ProxyRetriever(casServerUrlPrefix, getPropertyFromInitParams(filterConfig, "encoding", null))); - validator.setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false"))); - validator.setEncoding(getPropertyFromInitParams(filterConfig, "encoding", null)); - final Map additionalParameters = new HashMap(); + final HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(), + getSSLConfig()); + validator.setURLConnectionFactory(factory); + + validator.setProxyRetriever(new Cas20ProxyRetriever(casServerUrlPrefix, getString(ConfigurationKeys.ENCODING), factory)); + validator.setRenew(getBoolean(ConfigurationKeys.RENEW)); + validator.setEncoding(getString(ConfigurationKeys.ENCODING)); + + final Map additionalParameters = new HashMap(); final List 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)) { @@ -151,8 +185,6 @@ protected final TicketValidator getTicketValidator(final FilterConfig filterConf } validator.setCustomParameters(additionalParameters); - validator.setHostnameVerifier(getHostnameVerifier(filterConfig)); - return validator; } @@ -164,7 +196,8 @@ public void destroy() { /** * 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 { + protected final boolean preFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, + final FilterChain filterChain) throws IOException, ServletException { final HttpServletRequest request = (HttpServletRequest) servletRequest; final HttpServletResponse response = (HttpServletResponse) servletResponse; final String requestUri = request.getRequestURI(); @@ -176,7 +209,7 @@ protected final boolean preFilter(final ServletRequest servletRequest, final Ser try { CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage); } catch (final RuntimeException e) { - log.error(e.getMessage(), e); + logger.error(e.getMessage(), e); throw e; } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidator.java index cea0a2659..c97cf216c 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidator.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidator.java @@ -1,33 +1,31 @@ -/** +/* * 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: + * 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 + * 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 + * 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.util.XmlUtils; - +import java.util.Arrays; import java.util.List; +import org.jasig.cas.client.util.XmlUtils; /** * Extension to the traditional Service Ticket validation that will validate service tickets and proxy tickets. * * @author Scott Battaglia - * @version $Revision$ $Date$ * @since 3.1 */ public class Cas20ProxyTicketValidator extends Cas20ServiceTicketValidator { @@ -37,6 +35,9 @@ public class Cas20ProxyTicketValidator extends Cas20ServiceTicketValidator { /** This should be a list of an array of Strings */ private ProxyList allowedProxyChains = new ProxyList(); + /** Allows for an empty chain of proxy callback urls. **/ + private boolean allowEmptyProxyChain = true; + public Cas20ProxyTicketValidator(final String casServerUrlPrefix) { super(casServerUrlPrefix); } @@ -49,19 +50,38 @@ protected String getUrlSuffix() { return "proxyValidate"; } - protected void customParseResponse(final String response, final Assertion assertion) throws TicketValidationException { + protected void customParseResponse(final String response, final Assertion assertion) + throws TicketValidationException { final List proxies = XmlUtils.getTextForElements(response, "proxy"); - final String[] proxiedList = proxies.toArray(new String[proxies.size()]); + 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 (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; + } + + final String[] proxiedList = proxies.toArray(new String[proxies.size()]); if (this.allowedProxyChains.contains(proxiedList)) { 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()); } @@ -73,7 +93,20 @@ public final void setAllowedProxyChains(final ProxyList allowedProxyChains) { this.allowedProxyChains = allowedProxyChains; } - protected boolean isAcceptAnyProxy() { + protected final boolean isAcceptAnyProxy() { return this.acceptAnyProxy; } + + protected final boolean isAllowEmptyProxyChain() { + return this.allowEmptyProxyChain; + } + + /** + * Set to determine whether empty proxy chains are allowed. + * @see #customParseResponse(String, Assertion) + * @param allowEmptyProxyChain whether to allow empty proxy chains or not. True if so, false otherwise. + */ + public final void setAllowEmptyProxyChain(final boolean allowEmptyProxyChain) { + this.allowEmptyProxyChain = allowEmptyProxyChain; + } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidator.java index 9b132f82c..9ecc5b850 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidator.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidator.java @@ -1,24 +1,27 @@ -/** +/* * 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: + * 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 + * 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 + * 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.StringReader; +import java.util.*; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; import org.jasig.cas.client.authentication.AttributePrincipal; import org.jasig.cas.client.authentication.AttributePrincipalImpl; import org.jasig.cas.client.proxy.Cas20ProxyRetriever; @@ -26,17 +29,16 @@ import org.jasig.cas.client.proxy.ProxyRetriever; import org.jasig.cas.client.util.CommonUtils; import org.jasig.cas.client.util.XmlUtils; -import org.w3c.dom.NodeList; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +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; /** * Implementation of the TicketValidator that will validate Service Tickets in compliance with the CAS 2. * * @author Scott Battaglia - * @version $Revision$ $Date$ * @since 3.1 */ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTicketValidator { @@ -55,10 +57,11 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick * CAS server url prefix. * * @param casServerUrlPrefix the CAS Server URL prefix. + * @param urlFactory URL connection factory to use when communicating with the server */ public Cas20ServiceTicketValidator(final String casServerUrlPrefix) { super(casServerUrlPrefix); - this.proxyRetriever = new Cas20ProxyRetriever(casServerUrlPrefix, getEncoding()); + this.proxyRetriever = new Cas20ProxyRetriever(casServerUrlPrefix, getEncoding(), getURLConnectionFactory()); } /** @@ -66,8 +69,8 @@ public Cas20ServiceTicketValidator(final String casServerUrlPrefix) { * * @param urlParameters the Map containing the existing parameters to send to the server. */ - protected final void populateUrlAttributeMap(final Map urlParameters) { - urlParameters.put("pgtUrl", encodeUrl(this.proxyCallbackUrl)); + protected final void populateUrlAttributeMap(final Map urlParameters) { + urlParameters.put("pgtUrl", this.proxyCallbackUrl); } protected String getUrlSuffix() { @@ -83,12 +86,12 @@ protected final Assertion parseResponseFromServer(final String response) throws final String principal = XmlUtils.getTextForElement(response, "user"); final String proxyGrantingTicketIou = XmlUtils.getTextForElement(response, "proxyGrantingTicket"); - + final String proxyGrantingTicket; if (CommonUtils.isBlank(proxyGrantingTicketIou) || this.proxyGrantingTicketStorage == null) { - proxyGrantingTicket = null; + proxyGrantingTicket = null; } else { - proxyGrantingTicket = this.proxyGrantingTicketStorage.retrieve(proxyGrantingTicketIou); + proxyGrantingTicket = this.proxyGrantingTicketStorage.retrieve(proxyGrantingTicketIou); } if (CommonUtils.isEmpty(principal)) { @@ -96,9 +99,10 @@ protected final Assertion parseResponseFromServer(final String response) throws } final Assertion assertion; - final Map attributes = extractCustomAttributes(response); + final Map attributes = extractCustomAttributes(response); if (CommonUtils.isNotBlank(proxyGrantingTicket)) { - final AttributePrincipal attributePrincipal = new AttributePrincipalImpl(principal, attributes, proxyGrantingTicket, this.proxyRetriever); + final AttributePrincipal attributePrincipal = new AttributePrincipalImpl(principal, attributes, + proxyGrantingTicket, this.proxyRetriever); assertion = new AssertionImpl(attributePrincipal); } else { assertion = new AssertionImpl(new AttributePrincipalImpl(principal, attributes)); @@ -127,28 +131,19 @@ protected final Assertion parseResponseFromServer(final String response) throws * @param xml the XML to parse. * @return the map of attributes. */ - protected Map extractCustomAttributes(final String xml) { - - if (!xml.contains("")) { - return Collections.emptyMap(); - } - - final Map attributes = new HashMap(); - + protected Map extractCustomAttributes(final String xml) { + final SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setNamespaceAware(true); + spf.setValidating(false); try { - NodeList nodeList = XmlUtils.getNodeListForElements(xml,"cas:attributes"); - for (int i = 0; i < nodeList.getLength(); i++) { - final String nodeName = nodeList.item(i).getNodeName(); - final int beginIndex = nodeName.indexOf(":") + 1; - final int endIndex = nodeList.item(i).getNodeName().length(); - - final String attributeName = nodeName.substring(beginIndex, endIndex); // remove the "cas:" prefix from node name - final Object attributeValue = nodeList.item(i).getTextContent(); - attributes.put(attributeName, attributeValue); - } - return attributes; - - } catch (Exception e) { + final SAXParser saxParser = spf.newSAXParser(); + final XMLReader xmlReader = saxParser.getXMLReader(); + final CustomAttributeHandler handler = new CustomAttributeHandler(); + xmlReader.setContentHandler(handler); + xmlReader.parse(new InputSource(new StringReader(xml))); + return handler.getAttributes(); + } catch (final Exception e) { + logger.error(e.getMessage(), e); return Collections.emptyMap(); } } @@ -160,7 +155,8 @@ protected Map extractCustomAttributes(final String xml) { * @param assertion the partially constructed assertion. * @throws TicketValidationException if there is a problem constructing the Assertion. */ - protected void customParseResponse(final String response, final Assertion assertion) throws TicketValidationException { + protected void customParseResponse(final String response, final Assertion assertion) + throws TicketValidationException { // nothing to do } @@ -187,4 +183,67 @@ protected final ProxyGrantingTicketStorage getProxyGrantingTicketStorage() { protected final ProxyRetriever getProxyRetriever() { return this.proxyRetriever; } + + private class CustomAttributeHandler extends DefaultHandler { + + private Map attributes; + + private boolean foundAttributes; + + private String currentAttribute; + + private StringBuilder value; + + @Override + public void startDocument() throws SAXException { + this.attributes = new HashMap(); + } + + @Override + public void startElement(final String namespaceURI, final String localName, final String qName, + final Attributes attributes) throws SAXException { + if ("attributes".equals(localName)) { + this.foundAttributes = true; + } else if (this.foundAttributes) { + this.value = new StringBuilder(); + this.currentAttribute = localName; + } + } + + @Override + public void characters(final char[] chars, final int start, final int length) throws SAXException { + if (this.currentAttribute != null) { + value.append(chars, start, length); + } + } + + @Override + public void endElement(final String namespaceURI, final String localName, final String qName) + throws SAXException { + if ("attributes".equals(localName)) { + this.foundAttributes = false; + this.currentAttribute = null; + } else if (this.foundAttributes) { + final Object o = this.attributes.get(this.currentAttribute); + + if (o == null) { + this.attributes.put(this.currentAttribute, this.value.toString()); + } else { + final List items; + if (o instanceof List) { + items = (List) o; + } else { + items = new LinkedList(); + items.add(o); + this.attributes.put(this.currentAttribute, items); + } + items.add(this.value.toString()); + } + } + } + + public Map getAttributes() { + return this.attributes; + } + } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30ProxyReceivingTicketValidationFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30ProxyReceivingTicketValidationFilter.java new file mode 100644 index 000000000..4f11627c0 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30ProxyReceivingTicketValidationFilter.java @@ -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. + *

    + * 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; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30ProxyTicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30ProxyTicketValidator.java new file mode 100644 index 000000000..6dfffc850 --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30ProxyTicketValidator.java @@ -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"; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30ServiceTicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30ServiceTicketValidator.java new file mode 100644 index 000000000..cb155a7ff --- /dev/null +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas30ServiceTicketValidator.java @@ -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"; + } +} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/InvalidProxyChainTicketValidationException.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/InvalidProxyChainTicketValidationException.java index d0f9c002a..aa2a1345f 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/InvalidProxyChainTicketValidationException.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/InvalidProxyChainTicketValidationException.java @@ -1,22 +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: + * 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 + * 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 + * 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; /** @@ -29,11 +28,11 @@ public final class InvalidProxyChainTicketValidationException extends TicketValidationException { /** - * Unique Id for Serialization - */ - private static final long serialVersionUID = -7736653266370691534L; + * Unique Id for Serialization + */ + private static final long serialVersionUID = -7736653266370691534L; - /** + /** * Constructs an exception with the supplied message. * @param string the supplied message. */ diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/ProxyList.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/ProxyList.java index 812e97062..f20877917 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/ProxyList.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/ProxyList.java @@ -1,29 +1,31 @@ -/** +/* * 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: + * 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 + * 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 + * 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.util.CommonUtils; - import java.util.ArrayList; import java.util.List; -import java.util.Arrays; +import org.jasig.cas.client.authentication.ExactUrlPatternMatcherStrategy; +import org.jasig.cas.client.authentication.RegexUrlPatternMatcherStrategy; +import org.jasig.cas.client.authentication.UrlPatternMatcherStrategy; +import org.jasig.cas.client.util.CommonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Holding class for the proxy list to make Spring configuration easier. @@ -34,28 +36,66 @@ */ public final class ProxyList { - private final List proxyChains; + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final List> proxyChains; public ProxyList(final List proxyChains) { CommonUtils.assertNotNull(proxyChains, "List of proxy chains cannot be null."); - this.proxyChains = proxyChains; + + this.proxyChains = new ArrayList>(); + + for (final String[] list : proxyChains) { + final List chain = new ArrayList(); + + for (final String item : list) { + if (item.startsWith("^")) { + chain.add(new RegexUrlPatternMatcherStrategy(item)); + } else { + chain.add(new ExactUrlPatternMatcherStrategy(item)); + } + } + + this.proxyChains.add(chain); + } } public ProxyList() { this(new ArrayList()); } - public boolean contains(String[] proxiedList) { - for (final String[] list : this.proxyChains) { - if (Arrays.equals(proxiedList, list)) { - return true; + public boolean contains(final String[] proxiedList) { + StringBuilder loggingOutput; + + for (final List proxyChain : this.proxyChains) { + loggingOutput = new StringBuilder(); + + if (proxyChain.size() == proxiedList.length) { + for (int linkIndex = 0; linkIndex < proxyChain.size(); linkIndex++) { + final String linkToTest = proxiedList[linkIndex]; + loggingOutput.append(linkToTest); + + if (proxyChain.get(linkIndex).matches(linkToTest)) { + //If we are at the last link, we found a good proxyChain. + if (linkIndex == proxyChain.size() - 1) { + logger.info("Proxy chain matched: {}", loggingOutput.toString()); + return true; + } + + } else { + logger.warn("Proxy chain did not match at {}. Skipping to next allowedProxyChain", loggingOutput.toString()); + break; + } + loggingOutput.append("->"); + } } } + logger.warn("No proxy chain matched the allowedProxyChains list."); return false; } - + public String toString() { - return this.proxyChains.toString(); + return this.proxyChains.toString(); } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/ProxyListEditor.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/ProxyListEditor.java index ed5e78494..dd5ce68f4 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/ProxyListEditor.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/ProxyListEditor.java @@ -1,32 +1,30 @@ -/** +/* * 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: + * 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 + * 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 + * 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.util.CommonUtils; - import java.beans.PropertyEditorSupport; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.List; +import org.jasig.cas.client.util.CommonUtils; /** * Convert a String-formatted list of acceptable proxies to an array. @@ -38,27 +36,27 @@ */ public final class ProxyListEditor extends PropertyEditorSupport { - public void setAsText(final String text) throws IllegalArgumentException { - final BufferedReader reader = new BufferedReader(new StringReader(text)); - final List proxyChains = new ArrayList(); + public void setAsText(final String text) throws IllegalArgumentException { + final BufferedReader reader = new BufferedReader(new StringReader(text)); + final List proxyChains = new ArrayList(); - try { - String line; - while ((line = reader.readLine()) != null) { - if (CommonUtils.isNotBlank(line)) { - proxyChains.add(line.trim().split(" ")); - } - } - } catch (final IOException e) { - // ignore this - } finally { - try { - reader.close(); - } catch (final IOException e) { - // nothing to do - } - } + try { + String line; + while ((line = reader.readLine()) != null) { + if (CommonUtils.isNotBlank(line)) { + proxyChains.add(line.trim().split(" ")); + } + } + } catch (final IOException e) { + // ignore this + } finally { + try { + reader.close(); + } catch (final IOException e) { + // nothing to do + } + } - setValue(new ProxyList(proxyChains)); - } + setValue(new ProxyList(proxyChains)); + } } diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Saml11TicketValidationFilter.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Saml11TicketValidationFilter.java deleted file mode 100644 index 26a2f7454..000000000 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Saml11TicketValidationFilter.java +++ /dev/null @@ -1,62 +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: - * - * 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 javax.servlet.FilterConfig; -import javax.servlet.ServletException; - -/** - * Implementation of TicketValidationFilter that can instanciate a SAML 1.1 Ticket Validator. - *

    - * Deployers can provide the "casServerUrlPrefix" and "tolerance" properties of the Saml11TicketValidator via the - * context or filter init parameters. - * - * @author Scott Battaglia - * @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); - - log.warn("SAML1.1 compliance requires the [artifactParameterName] and [serviceParameterName] to be set to specified values."); - log.warn("This filter will overwrite any user-provided values (if any are provided)"); - - setArtifactParameterName("SAMLart"); - setServiceParameterName("TARGET"); - } - - 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"))); - validator.setHostnameVerifier(getHostnameVerifier(filterConfig)); - validator.setEncoding(getPropertyFromInitParams(filterConfig, "encoding", null)); - validator.setDisableXmlSchemaValidation(parseBoolean(getPropertyFromInitParams(filterConfig, "disableXmlSchemaValidation", "false"))); - return validator; - } -} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Saml11TicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/Saml11TicketValidator.java deleted file mode 100644 index 117cbe3ec..000000000 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/Saml11TicketValidator.java +++ /dev/null @@ -1,250 +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: - * - * 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.authentication.AttributePrincipal; -import org.jasig.cas.client.authentication.AttributePrincipalImpl; -import org.jasig.cas.client.util.CommonUtils; -import org.opensaml.*; - -import java.io.*; -import java.net.HttpURLConnection; -import java.net.URL; -import java.nio.charset.Charset; -import java.util.*; - -import javax.net.ssl.HttpsURLConnection; - -/** - * TicketValidator that can understand validating a SAML artifact. This includes the SOAP request/response. - * - * @author Scott Battaglia - * @version $Revision$ $Date$ - * @since 3.1 - */ -public final class Saml11TicketValidator extends AbstractUrlBasedTicketValidator { - - /** Time tolerance to allow for time drifting. */ - private long tolerance = 1000L; - - public Saml11TicketValidator(final String casServerUrlPrefix) { - super(casServerUrlPrefix); - } - - protected String getUrlSuffix() { - return "samlValidate"; - } - - protected void populateUrlAttributeMap(final Map 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) { - // according to our reading of the SAML 1.1 code, this should disable the schema checking. However, there may be a couple - // of error messages that slip through on start up! - XML.parserPool.setDefaultSchemas(null, 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 String removeStartOfSoapBody = response.substring(response.indexOf("") + 15); - final String removeEndOfSoapBody = removeStartOfSoapBody.substring(0, removeStartOfSoapBody.indexOf("")); - final SAMLResponse samlResponse = new SAMLResponse(new ByteArrayInputStream(getBytes(removeEndOfSoapBody))); - - if (!samlResponse.getAssertions().hasNext()) { - throw new TicketValidationException("No assertions found."); - } - - for (final Iterator iter = samlResponse.getAssertions(); iter.hasNext();) { - final SAMLAssertion assertion = (SAMLAssertion) iter.next(); - - if (!isValidAssertion(assertion)) { - continue; - } - - final SAMLAuthenticationStatement authenticationStatement = getSAMLAuthenticationStatement(assertion); - - if (authenticationStatement == null) { - throw new TicketValidationException("No AuthentiationStatement found in SAML Assertion."); - } - final SAMLSubject subject = authenticationStatement.getSubject(); - - if (subject == null) { - throw new TicketValidationException("No Subject found in SAML Assertion."); - } - - final SAMLAttribute[] attributes = getAttributesFor(assertion, subject); - final Map personAttributes = new HashMap(); - for (final SAMLAttribute samlAttribute : attributes) { - final List values = getValuesFrom(samlAttribute); - - personAttributes.put(samlAttribute.getName(), values.size() == 1 ? values.get(0) : values); - } - - final AttributePrincipal principal = new AttributePrincipalImpl(subject.getNameIdentifier().getName(), personAttributes); - - final Map authenticationAttributes = new HashMap(); - authenticationAttributes.put("samlAuthenticationStatement::authMethod", authenticationStatement.getAuthMethod()); - - return new AssertionImpl(principal, authenticationAttributes); - } - } catch (final SAMLException 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 SAMLAssertion assertion) { - final Date notBefore = assertion.getNotBefore(); - final Date notOnOrAfter = assertion.getNotOnOrAfter(); - - if (assertion.getNotBefore() == null || assertion.getNotOnOrAfter() == null) { - log.debug("Assertion has no bounding dates. Will not process."); - return false; - } - - final long currentTime = getCurrentTimeInUtc().getTime(); - - if (currentTime + tolerance < notBefore.getTime()) { - log.debug("skipping assertion that's not yet valid..."); - return false; - } - - if (notOnOrAfter.getTime() <= currentTime - tolerance) { - log.debug("skipping expired assertion..."); - return false; - } - - return true; - } - - private SAMLAuthenticationStatement getSAMLAuthenticationStatement(final SAMLAssertion assertion) { - for (final Iterator iter = assertion.getStatements(); iter.hasNext();) { - final SAMLStatement statement = (SAMLStatement) iter.next(); - - if (statement instanceof SAMLAuthenticationStatement) { - return (SAMLAuthenticationStatement) statement; - } - } - - return null; - } - - private SAMLAttribute[] getAttributesFor(final SAMLAssertion assertion, final SAMLSubject subject) { - final List attributes = new ArrayList(); - for (final Iterator iter = assertion.getStatements(); iter.hasNext();) { - final SAMLStatement statement = (SAMLStatement) iter.next(); - - if (statement instanceof SAMLAttributeStatement) { - final SAMLAttributeStatement attributeStatement = (SAMLAttributeStatement) statement; - // used because SAMLSubject does not implement equals - if (subject.getNameIdentifier().getName().equals(attributeStatement.getSubject().getNameIdentifier().getName())) { - for (final Iterator iter2 = attributeStatement.getAttributes(); iter2.hasNext();) - attributes.add((SAMLAttribute) iter2.next()); - } - } - } - - return attributes.toArray(new SAMLAttribute[attributes.size()]); - } - - private List getValuesFrom(final SAMLAttribute attribute) { - final List list = new ArrayList(); - for (final Iterator iter = attribute.getValues(); iter.hasNext();) { - list.add(iter.next()); - } - return list; - } - - private Date getCurrentTimeInUtc() { - final Calendar c = Calendar.getInstance(); - c.setTimeZone(TimeZone.getTimeZone("UTC")); - return c.getTime(); - } - - protected String retrieveResponseFromServer(final URL validationUrl, final String ticket) { - - String MESSAGE_TO_SEND; - - try { - MESSAGE_TO_SEND = "" - + "" + ticket - + ""; - } catch (final SAMLException e) { - throw new RuntimeException(e); - } - - HttpURLConnection conn = null; - - try { - conn = (HttpURLConnection) validationUrl.openConnection(); - if (this.hostnameVerifier != null && conn instanceof HttpsURLConnection) { - ((HttpsURLConnection)conn).setHostnameVerifier(this.hostnameVerifier); - } - 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); - - final DataOutputStream out = new DataOutputStream(conn.getOutputStream()); - out.writeBytes(MESSAGE_TO_SEND); - out.flush(); - out.close(); - - final BufferedReader 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 { - if (conn != null) { - conn.disconnect(); - } - } - } - - public void setTolerance(final long tolerance) { - this.tolerance = tolerance; - } -} diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/TicketValidationException.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/TicketValidationException.java index e383fedb8..d703bad6a 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/TicketValidationException.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/TicketValidationException.java @@ -1,22 +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: + * 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 + * 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 + * 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; /** @@ -29,11 +28,11 @@ public class TicketValidationException extends Exception { /** - * Unique Id for Serialization - */ - private static final long serialVersionUID = -7036248720402711806L; + * Unique Id for Serialization + */ + private static final long serialVersionUID = -7036248720402711806L; - /** + /** * Constructs an exception with the supplied message. * * @param string the message diff --git a/cas-client-core/src/main/java/org/jasig/cas/client/validation/TicketValidator.java b/cas-client-core/src/main/java/org/jasig/cas/client/validation/TicketValidator.java index 2efa2b9cb..69888e309 100644 --- a/cas-client-core/src/main/java/org/jasig/cas/client/validation/TicketValidator.java +++ b/cas-client-core/src/main/java/org/jasig/cas/client/validation/TicketValidator.java @@ -1,22 +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: + * 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 + * 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 + * 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; /** diff --git a/cas-client-core/src/main/resources/META-INF/cas/samlRequestTemplate.xml b/cas-client-core/src/main/resources/META-INF/cas/samlRequestTemplate.xml new file mode 100644 index 000000000..a67501025 --- /dev/null +++ b/cas-client-core/src/main/resources/META-INF/cas/samlRequestTemplate.xml @@ -0,0 +1,28 @@ + + + + + + %s + + + diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/PublicTestHttpServer.java b/cas-client-core/src/test/java/org/jasig/cas/client/PublicTestHttpServer.java index 57de8d2d3..0216f472a 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/PublicTestHttpServer.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/PublicTestHttpServer.java @@ -1,22 +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: + * 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 + * 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 + * 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; import java.io.*; @@ -46,11 +45,13 @@ public final class PublicTestHttpServer extends Thread { private static Map serverMap = new HashMap(); - private PublicTestHttpServer(String data, String encoding, String MIMEType, int port) throws UnsupportedEncodingException { + private PublicTestHttpServer(String data, String encoding, String MIMEType, int port) + throws UnsupportedEncodingException { this(data.getBytes(encoding), encoding, MIMEType, port); } - private PublicTestHttpServer(byte[] data, String encoding, String MIMEType, int port) throws UnsupportedEncodingException { + private PublicTestHttpServer(byte[] data, String encoding, String MIMEType, int port) + throws UnsupportedEncodingException { this.content = data; this.port = port; this.encoding = encoding; diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/SerializationTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/SerializationTests.java index 76de0bc7b..18a6e9cc1 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/SerializationTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/SerializationTests.java @@ -1,22 +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: + * 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 + * 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 + * 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; import java.io.ByteArrayInputStream; @@ -24,10 +23,8 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Collections; - import junit.framework.Assert; import junit.framework.TestCase; - import org.jasig.cas.client.authentication.AttributePrincipalImpl; import org.jasig.cas.client.authentication.SimpleGroup; import org.jasig.cas.client.authentication.SimplePrincipal; @@ -44,7 +41,7 @@ * */ public class SerializationTests extends TestCase { - + public void testSerializeDeserialize() throws Exception { final Object[] subjects = getTestSubjects(); for (int i = 0; i < subjects.length; i++) { @@ -57,7 +54,7 @@ public void testSerializeDeserialize() throws Exception { } finally { out.close(); } - + final ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray()); final ObjectInputStream in = new ObjectInputStream(byteIn); try { @@ -73,17 +70,11 @@ public void testSerializeDeserialize() throws Exception { private Object[] getTestSubjects() { final SimplePrincipal simplePrincipal = new SimplePrincipal("simple"); final SimpleGroup simpleGroup = new SimpleGroup("group"); - final AttributePrincipalImpl attributePrincipal = - new AttributePrincipalImpl("attr", Collections.singletonMap("LOA", "3")); - final AssertionPrincipal assertionPrincipal = new AssertionPrincipal( - "assertion", - new AssertionImpl(attributePrincipal, Collections.singletonMap("authenticationMethod", "username"))); - - return new Object[] { - simplePrincipal, - simpleGroup, - attributePrincipal, - assertionPrincipal, - }; + final AttributePrincipalImpl attributePrincipal = new AttributePrincipalImpl("attr", + Collections. singletonMap("LOA", "3")); + final AssertionPrincipal assertionPrincipal = new AssertionPrincipal("assertion", new AssertionImpl( + attributePrincipal, Collections. singletonMap("authenticationMethod", "username"))); + + return new Object[] { simplePrincipal, simpleGroup, attributePrincipal, assertionPrincipal, }; } } diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/authentication/AuthenticationFilterTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/authentication/AuthenticationFilterTests.java index b3a273f51..48479a5dd 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/authentication/AuthenticationFilterTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/authentication/AuthenticationFilterTests.java @@ -1,47 +1,49 @@ -/** +/* * 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: + * 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 + * 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 + * 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.authentication; -import junit.framework.TestCase; -import org.jasig.cas.client.util.AbstractCasFilter; -import org.jasig.cas.client.validation.AssertionImpl; -import org.springframework.mock.web.MockFilterConfig; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.mock.web.MockHttpSession; +import static org.junit.Assert.*; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.net.URL; +import java.net.URLEncoder; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; -import java.io.IOException; -import java.net.URLEncoder; + +import org.jasig.cas.client.util.AbstractCasFilter; +import org.jasig.cas.client.validation.AssertionImpl; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.mock.web.*; /** * Tests for the AuthenticationFilter. * * @author Scott Battaglia - * @version $Revision: 11753 $ $Date: 2007-01-03 13:37:26 -0500 (Wed, 03 Jan 2007) $ * @since 3.0 */ -public final class AuthenticationFilterTests extends TestCase { +public final class AuthenticationFilterTests { private static final String CAS_SERVICE_URL = "https://localhost:8443/service"; @@ -49,27 +51,28 @@ public final class AuthenticationFilterTests extends TestCase { private AuthenticationFilter filter; - protected void setUp() throws Exception { - // TODO CAS_SERVICE_URL, false, CAS_LOGIN_URL + @Before + public void setUp() throws Exception { this.filter = new AuthenticationFilter(); final MockFilterConfig config = new MockFilterConfig(); config.addInitParameter("casServerLoginUrl", CAS_LOGIN_URL); - config.addInitParameter("service", "https://localhost:8443/service"); + config.addInitParameter("service", CAS_SERVICE_URL); this.filter.init(config); } - protected void tearDown() throws Exception { + @After + public void tearDown() throws Exception { this.filter.destroy(); } + @Test public void testRedirect() throws Exception { final MockHttpSession session = new MockHttpSession(); final MockHttpServletRequest request = new MockHttpServletRequest(); final MockHttpServletResponse response = new MockHttpServletResponse(); final FilterChain filterChain = new FilterChain() { - public void doFilter(ServletRequest arg0, ServletResponse arg1) - throws IOException, ServletException { + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // nothing to do } }; @@ -77,11 +80,11 @@ public void doFilter(ServletRequest arg0, ServletResponse arg1) request.setSession(session); this.filter.doFilter(request, response, filterChain); - assertEquals(CAS_LOGIN_URL + "?service=" - + URLEncoder.encode(CAS_SERVICE_URL, "UTF-8"), response - .getRedirectedUrl()); + assertEquals(CAS_LOGIN_URL + "?service=" + URLEncoder.encode(CAS_SERVICE_URL, "UTF-8"), + response.getRedirectedUrl()); } + @Test public void testRedirectWithQueryString() throws Exception { final MockHttpSession session = new MockHttpSession(); final MockHttpServletRequest request = new MockHttpServletRequest(); @@ -91,8 +94,7 @@ public void testRedirectWithQueryString() throws Exception { request.setSecure(true); final FilterChain filterChain = new FilterChain() { - public void doFilter(ServletRequest arg0, ServletResponse arg1) - throws IOException, ServletException { + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // nothing to do } }; @@ -107,41 +109,41 @@ public void doFilter(ServletRequest arg0, ServletResponse arg1) this.filter.doFilter(request, response, filterChain); - assertEquals(CAS_LOGIN_URL - + "?service=" - + URLEncoder.encode("https://localhost:8443" - + request.getRequestURI() + "?" + request.getQueryString(), - "UTF-8"), response.getRedirectedUrl()); + assertEquals( + CAS_LOGIN_URL + + "?service=" + + URLEncoder.encode( + "https://localhost:8443" + request.getRequestURI() + "?" + request.getQueryString(), + "UTF-8"), response.getRedirectedUrl()); } + @Test public void testAssertion() throws Exception { final MockHttpSession session = new MockHttpSession(); final MockHttpServletRequest request = new MockHttpServletRequest(); final MockHttpServletResponse response = new MockHttpServletResponse(); final FilterChain filterChain = new FilterChain() { - public void doFilter(ServletRequest arg0, ServletResponse arg1) - throws IOException, ServletException { + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // nothing to do } }; request.setSession(session); - session.setAttribute(AbstractCasFilter.CONST_CAS_ASSERTION, - new AssertionImpl("test")); + session.setAttribute(AbstractCasFilter.CONST_CAS_ASSERTION, new AssertionImpl("test")); this.filter.doFilter(request, response, filterChain); assertNull(response.getRedirectedUrl()); } + @Test public void testRenew() throws Exception { final MockHttpSession session = new MockHttpSession(); final MockHttpServletRequest request = new MockHttpServletRequest(); final MockHttpServletResponse response = new MockHttpServletResponse(); final FilterChain filterChain = new FilterChain() { - public void doFilter(ServletRequest arg0, ServletResponse arg1) - throws IOException, ServletException { + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // nothing to do } }; @@ -154,14 +156,14 @@ public void doFilter(ServletRequest arg0, ServletResponse arg1) assertTrue(response.getRedirectedUrl().indexOf("renew=true") != -1); } + @Test public void testGateway() throws Exception { final MockHttpSession session = new MockHttpSession(); final MockHttpServletRequest request = new MockHttpServletRequest(); final MockHttpServletResponse response = new MockHttpServletResponse(); final FilterChain filterChain = new FilterChain() { - public void doFilter(ServletRequest arg0, ServletResponse arg1) - throws IOException, ServletException { + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { // nothing to do } }; @@ -178,4 +180,192 @@ public void doFilter(ServletRequest arg0, ServletResponse arg1) assertNull(session.getAttribute(DefaultGatewayResolverImpl.CONST_CAS_GATEWAY)); assertNull(response2.getRedirectedUrl()); } + + @Test + public void testRenewInitParamThrows() throws Exception { + final AuthenticationFilter f = new AuthenticationFilter(); + final MockFilterConfig config = new MockFilterConfig(); + config.addInitParameter("casServerLoginUrl", CAS_LOGIN_URL); + config.addInitParameter("service", CAS_SERVICE_URL); + config.addInitParameter("renew", "true"); + try { + f.init(config); + fail("Should have thrown IllegalArgumentException."); + } catch (final IllegalArgumentException e) { + assertTrue(e.getMessage().contains("Renew MUST")); + } + } + + @Test + public void testAllowsRenewContextParam() throws Exception { + final AuthenticationFilter f = new AuthenticationFilter(); + final MockServletContext context = new MockServletContext(); + context.addInitParameter("casServerLoginUrl", CAS_LOGIN_URL); + context.addInitParameter("service", CAS_SERVICE_URL); + context.addInitParameter("renew", "true"); + f.init(new MockFilterConfig(context)); + final Field renewField = AuthenticationFilter.class.getDeclaredField("renew"); + renewField.setAccessible(true); + assertTrue((Boolean) renewField.get(f)); + } + + @Test + public void customRedirectStrategy() throws Exception { + final AuthenticationFilter f = new AuthenticationFilter(); + final MockServletContext context = new MockServletContext(); + context.addInitParameter("casServerLoginUrl", CAS_LOGIN_URL); + context.addInitParameter("service", CAS_SERVICE_URL); + context.addInitParameter("authenticationRedirectStrategyClass", + "org.jasig.cas.client.authentication.FacesCompatibleAuthenticationRedirectStrategy"); + f.init(new MockFilterConfig(context)); + } + + @Test + public void testIgnorePatterns() throws Exception { + final AuthenticationFilter f = new AuthenticationFilter(); + final MockServletContext context = new MockServletContext(); + context.addInitParameter("casServerLoginUrl", CAS_LOGIN_URL); + + context.addInitParameter("ignorePattern", "=valueTo(\\w+)"); + context.addInitParameter("service", CAS_SERVICE_URL); + f.init(new MockFilterConfig(context)); + + final MockHttpServletRequest request = new MockHttpServletRequest(); + final String URL = CAS_SERVICE_URL + "?param=valueToIgnore"; + request.setRequestURI(URL); + + final MockHttpSession session = new MockHttpSession(); + request.setSession(session); + + final MockHttpServletResponse response = new MockHttpServletResponse(); + + final FilterChain filterChain = new FilterChain() { + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + } + }; + + f.doFilter(request, response, filterChain); + assertNull(response.getRedirectedUrl()); + } + + @Test + public void testIgnorePatternsWithContainsMatching() throws Exception { + final AuthenticationFilter f = new AuthenticationFilter(); + final MockServletContext context = new MockServletContext(); + context.addInitParameter("casServerLoginUrl", CAS_LOGIN_URL); + + context.addInitParameter("ignorePattern", "=valueToIgnore"); + context.addInitParameter("ignoreUrlPatternType", "CONTAINS"); + context.addInitParameter("service", CAS_SERVICE_URL); + f.init(new MockFilterConfig(context)); + + final MockHttpServletRequest request = new MockHttpServletRequest(); + final String URL = CAS_SERVICE_URL + "?param=valueToIgnore"; + request.setRequestURI(URL); + + final MockHttpSession session = new MockHttpSession(); + request.setSession(session); + + final MockHttpServletResponse response = new MockHttpServletResponse(); + + final FilterChain filterChain = new FilterChain() { + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + } + }; + + f.doFilter(request, response, filterChain); + assertNull(response.getRedirectedUrl()); + } + + @Test + public void testIgnorePatternsWithExactMatching() throws Exception { + final AuthenticationFilter f = new AuthenticationFilter(); + final MockServletContext context = new MockServletContext(); + context.addInitParameter("casServerLoginUrl", CAS_LOGIN_URL); + + final URL url = new URL(CAS_SERVICE_URL + "?param=valueToIgnore"); + + context.addInitParameter("ignorePattern", url.toExternalForm()); + context.addInitParameter("ignoreUrlPatternType", "EXACT"); + context.addInitParameter("service", CAS_SERVICE_URL); + f.init(new MockFilterConfig(context)); + + final MockHttpServletRequest request = new MockHttpServletRequest(); + request.setScheme(url.getProtocol()); + request.setServerName(url.getHost()); + request.setServerPort(url.getPort()); + request.setQueryString(url.getQuery()); + request.setRequestURI(url.getPath()); + + final MockHttpSession session = new MockHttpSession(); + request.setSession(session); + + final MockHttpServletResponse response = new MockHttpServletResponse(); + + final FilterChain filterChain = new FilterChain() { + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + } + }; + + f.doFilter(request, response, filterChain); + assertNull(response.getRedirectedUrl()); + } + + @Test + public void testIgnorePatternsWithExactClassname() throws Exception { + final AuthenticationFilter f = new AuthenticationFilter(); + final MockServletContext context = new MockServletContext(); + context.addInitParameter("casServerLoginUrl", CAS_LOGIN_URL); + + context.addInitParameter("ignorePattern", "=valueToIgnore"); + context.addInitParameter("ignoreUrlPatternType", ContainsPatternUrlPatternMatcherStrategy.class.getName()); + context.addInitParameter("service", CAS_SERVICE_URL); + f.init(new MockFilterConfig(context)); + + final MockHttpServletRequest request = new MockHttpServletRequest(); + final String URL = CAS_SERVICE_URL + "?param=valueToIgnore"; + request.setRequestURI(URL); + + final MockHttpSession session = new MockHttpSession(); + request.setSession(session); + + final MockHttpServletResponse response = new MockHttpServletResponse(); + + final FilterChain filterChain = new FilterChain() { + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + } + }; + + f.doFilter(request, response, filterChain); + assertNull(response.getRedirectedUrl()); + } + + @Test + public void testIgnorePatternsWithInvalidClassname() throws Exception { + final AuthenticationFilter f = new AuthenticationFilter(); + final MockServletContext context = new MockServletContext(); + context.addInitParameter("casServerLoginUrl", CAS_LOGIN_URL); + + context.addInitParameter("ignorePattern", "=valueToIgnore"); + context.addInitParameter("ignoreUrlPatternType", "unknown.class.name"); + context.addInitParameter("service", CAS_SERVICE_URL); + f.init(new MockFilterConfig(context)); + + final MockHttpServletRequest request = new MockHttpServletRequest(); + final String URL = CAS_SERVICE_URL + "?param=valueToIgnore"; + request.setRequestURI(URL); + + final MockHttpSession session = new MockHttpSession(); + request.setSession(session); + + final MockHttpServletResponse response = new MockHttpServletResponse(); + + final FilterChain filterChain = new FilterChain() { + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { + } + }; + + f.doFilter(request, response, filterChain); + System.out.println(response.getRedirectedUrl()); + } } diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/authentication/DefaultAuthenticationRedirectStrategyTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/authentication/DefaultAuthenticationRedirectStrategyTests.java new file mode 100644 index 000000000..39c433586 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/authentication/DefaultAuthenticationRedirectStrategyTests.java @@ -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.authentication; + +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; + +public class DefaultAuthenticationRedirectStrategyTests { + + private DefaultAuthenticationRedirectStrategy strategy; + + @Before + public void setUp() throws Exception { + this.strategy = new DefaultAuthenticationRedirectStrategy(); + } + + @Test + public void didWeRedirect() throws Exception { + final String redirectUrl = "http://www.jasig.org"; + final MockHttpServletRequest request = new MockHttpServletRequest(); + final MockHttpServletResponse response = new MockHttpServletResponse(); + + this.strategy.redirect(request, response, redirectUrl); + assertEquals(redirectUrl, response.getRedirectedUrl()); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/authentication/FacesCompatibleAuthenticationRedirectStrategyTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/authentication/FacesCompatibleAuthenticationRedirectStrategyTests.java new file mode 100644 index 000000000..90d148f42 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/authentication/FacesCompatibleAuthenticationRedirectStrategyTests.java @@ -0,0 +1,56 @@ +/* + * 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.authentication; + +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; + +public class FacesCompatibleAuthenticationRedirectStrategyTests { + + private FacesCompatibleAuthenticationRedirectStrategy strategy; + + @Before + public void setUp() throws Exception { + this.strategy = new FacesCompatibleAuthenticationRedirectStrategy(); + } + + @Test + public void didWeRedirect() throws Exception { + final String redirectUrl = "http://www.jasig.org"; + final MockHttpServletRequest request = new MockHttpServletRequest(); + final MockHttpServletResponse response = new MockHttpServletResponse(); + + this.strategy.redirect(request, response, redirectUrl); + assertEquals(redirectUrl, response.getRedirectedUrl()); + } + + @Test + public void facesPartialResponse() throws Exception { + final String redirectUrl = "http://www.jasig.org"; + final MockHttpServletRequest request = new MockHttpServletRequest(); + final MockHttpServletResponse response = new MockHttpServletResponse(); + request.setParameter("javax.faces.partial.ajax", "true"); + this.strategy.redirect(request, response, redirectUrl); + assertNull(response.getRedirectedUrl()); + assertTrue(response.getContentAsString().contains(redirectUrl)); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/configuration/ConfigurationKeyTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/configuration/ConfigurationKeyTests.java new file mode 100644 index 000000000..3a93a4179 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/configuration/ConfigurationKeyTests.java @@ -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 configurationKey = new ConfigurationKey(name); + assertEquals(name, configurationKey.getName()); + assertNull(configurationKey.getDefaultValue()); + } + + + @Test + public void gettersWithDefaultValue() { + final String name = "name"; + final Boolean defaultValue = Boolean.TRUE; + final ConfigurationKey configurationKey = new ConfigurationKey(name, defaultValue); + assertEquals(name, configurationKey.getName()); + assertEquals(defaultValue, configurationKey.getDefaultValue()); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/configuration/ConfigurationStrategyNameTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/configuration/ConfigurationStrategyNameTests.java new file mode 100644 index 000000000..5c2da1e2c --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/configuration/ConfigurationStrategyNameTests.java @@ -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!")); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/configuration/SystemPropertiesConfigurationStrategyImplTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/configuration/SystemPropertiesConfigurationStrategyImplTests.java new file mode 100644 index 000000000..9b362d04b --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/configuration/SystemPropertiesConfigurationStrategyImplTests.java @@ -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 key = ConfigurationKeys.SERVER_NAME; + // no system property defined + assertEquals(key.getDefaultValue(), impl.getString(key)); + } + + @Test + public void testWithSystemProperty() { + final ConfigurationKey key = ConfigurationKeys.ARTIFACT_PARAMETER_NAME; + System.setProperty(key.getName(), PARAMETER_NAME); + assertEquals(PARAMETER_NAME, impl.getString(key)); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/configuration/WebXmlConfigurationStrategyImplTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/configuration/WebXmlConfigurationStrategyImplTests.java new file mode 100644 index 000000000..57c66d50c --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/configuration/WebXmlConfigurationStrategyImplTests.java @@ -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 key = ConfigurationKeys.ACCEPT_ANY_PROXY; + assertEquals(key.getDefaultValue(), this.impl.getBoolean(key)); + } + + + @Test + public void noKeyFoundGetDefaultForString() { + final ConfigurationKey key = ConfigurationKeys.ARTIFACT_PARAMETER_NAME; + assertEquals(key.getDefaultValue(), this.impl.getString(key)); + } + + + @Test + public void noKeyFoundGetDefaultForLong() { + final ConfigurationKey key = ConfigurationKeys.TOLERANCE; + assertEquals(key.getDefaultValue().longValue(), this.impl.getLong(key)); + } + + + @Test + public void noKeyFoundGetDefaultForInt() { + final ConfigurationKey key = ConfigurationKeys.MILLIS_BETWEEN_CLEAN_UPS; + assertEquals(key.getDefaultValue().intValue(), this.impl.getInt(key)); + } + + @Test + public void filterConfigValueForBoolean() { + final ConfigurationKey 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 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 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 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 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 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 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 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)); + } + +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/jaas/CasLoginModuleTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/jaas/CasLoginModuleTests.java index 623730af7..d255f3948 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/jaas/CasLoginModuleTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/jaas/CasLoginModuleTests.java @@ -1,37 +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: + * 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 + * 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 + * 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.jaas; +import static org.junit.Assert.*; import java.security.Principal; import java.security.acl.Group; import java.util.HashMap; import java.util.Map; import java.util.Set; - import javax.security.auth.Subject; import javax.security.auth.login.LoginException; - -import static org.junit.Assert.*; - import org.jasig.cas.client.PublicTestHttpServer; -import org.junit.AfterClass; +import org.jasig.cas.client.validation.TicketValidationException; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -47,23 +44,23 @@ public class CasLoginModuleTests { private static final PublicTestHttpServer server = PublicTestHttpServer.instance(8091); private static final String CONST_CAS_SERVER_URL = "http://localhost:8091/"; - + private CasLoginModule module; - + private Subject subject; - - private Map options; - /* @AfterClass - public static void classCleanUp() { - server.shutdown(); - }*/ + private Map options; + + /* @AfterClass + public static void classCleanUp() { + server.shutdown(); + }*/ @Before public void setUp() throws Exception { module = new CasLoginModule(); subject = new Subject(); - options = new HashMap(); + options = new HashMap(); options.put("service", "https://service.example.com/webapp"); options.put("ticketValidatorClass", "org.jasig.cas.client.validation.Cas20ServiceTicketValidator"); options.put("casServerUrlPrefix", CONST_CAS_SERVER_URL); @@ -84,15 +81,14 @@ public void testLoginSuccess() throws Exception { final String SERVICE = "https://example.com/service"; final String TICKET = "ST-100000-aA5Yuvrxzpv8Tau1cYQ7-srv1"; final String RESPONSE = "" - + "" - + USERNAME + + "" + USERNAME + ""; server.content = RESPONSE.getBytes(server.encoding); - + module.initialize( subject, new ServiceAndTicketCallbackHandler(SERVICE, TICKET), - new HashMap(), + new HashMap(), options); module.login(); module.commit(); @@ -116,13 +112,13 @@ public void testLoginFailure() throws Exception { module.initialize( subject, new ServiceAndTicketCallbackHandler(SERVICE, TICKET), - new HashMap(), + new HashMap(), options); try { module.login(); - fail("Login did not throw LoginException as expected."); - } catch (Exception e) { - assertTrue(e instanceof LoginException); + fail("Login did not throw FailedLoginException as expected."); + } catch (LoginException e) { + assertEquals(TicketValidationException.class, e.getCause().getClass()); } module.commit(); assertNull(module.ticket); @@ -142,7 +138,7 @@ public void testLogout() throws Exception { } /** - * Test assertion cache allows successive logins with same ticket to succeed. + * Confirm that CasLoginModule#logout() destroys cached data and prevents subsequent login w/expired ticket. * @throws Exception On errors. */ @Test @@ -150,42 +146,96 @@ public void testAssertionCaching() throws Exception { final String USERNAME = "username"; final String SERVICE = "https://example.com/service"; final String TICKET = "ST-300000-aA5Yuvrxzpv8Tau1cYQ7-srv1"; - final String RESPONSE1 = "" - + "" - + USERNAME + final String SUCCESS_RESPONSE = "" + + "" + USERNAME + ""; - final String RESPONSE2 = "Ticket ST-300000-aA5Yuvrxzpv8Tau1cYQ7-srv1 not recognized"; - server.content = RESPONSE1.getBytes(server.encoding); - + final String FAILURE_RESPONSE = "Ticket ST-300000-aA5Yuvrxzpv8Tau1cYQ7-srv1 not recognized"; + options.put("cacheAssertions", "true"); options.put("cacheTimeout", "1"); + + server.content = SUCCESS_RESPONSE.getBytes(server.encoding); module.initialize( subject, new ServiceAndTicketCallbackHandler(SERVICE, TICKET), - new HashMap(), + new HashMap(), options); module.login(); module.commit(); assertEquals(this.subject.getPrincipals().size(), 3); assertEquals(TICKET, this.subject.getPrivateCredentials().iterator().next().toString()); - - Thread.sleep(2000); + + // Logout should destroy all authenticated state data including assertion cache entries module.logout(); assertEquals(0, subject.getPrincipals().size()); assertEquals(0, subject.getPrivateCredentials().size()); - server.content = RESPONSE2.getBytes(server.encoding); + server.content = FAILURE_RESPONSE.getBytes(server.encoding); + + // Verify we can't log in again with same ticket module.initialize( subject, new ServiceAndTicketCallbackHandler(SERVICE, TICKET), - new HashMap(), + new HashMap(), options); - module.login(); + try { + module.login(); + module.commit(); + Assert.fail("Login should have failed."); + } catch (LoginException e) { + assertEquals(TicketValidationException.class, e.getCause().getClass()); + } + assertEquals(0, this.subject.getPrincipals().size()); + assertEquals(0, this.subject.getPrivateCredentials().size()); + } + + /** + * Verify that cached assertions that are expired are never be accessible + * by {@link org.jasig.cas.client.jaas.CasLoginModule#login()} method. + * + * @throws Exception On errors. + */ + @Test + public void testAssertionCachingExpiration() throws Exception { + final String USERNAME = "hizzy"; + final String SERVICE = "https://example.com/service"; + final String TICKET = "ST-12345-ABCDEFGHIJKLMNOPQRSTUVWXYZ-hosta"; + final String SUCCESS_RESPONSE = "" + + "" + USERNAME + + ""; + final String FAILURE_RESPONSE = "Ticket ST-12345-ABCDEFGHIJKLMNOPQRSTUVWXYZ-hosta not recognized"; + + options.put("cacheAssertions", "true"); + // Cache timeout is 1 second + options.put("cacheTimeoutUnit", "SECONDS"); + options.put("cacheTimeout", "1"); + + server.content = SUCCESS_RESPONSE.getBytes(server.encoding); + module.initialize( + subject, + new ServiceAndTicketCallbackHandler(SERVICE, TICKET), + new HashMap(), + options); + assertTrue(module.login()); module.commit(); - assertEquals(this.subject.getPrincipals().size(), 3); - assertEquals(TICKET, this.subject.getPrivateCredentials().iterator().next().toString()); + + Thread.sleep(1100); + // Assertion should now be expired from cache + server.content = FAILURE_RESPONSE.getBytes(server.encoding); + module.initialize( + subject, + new ServiceAndTicketCallbackHandler(SERVICE, TICKET), + new HashMap(), + options); + try { + module.login(); + fail("Should have thrown FailedLoginException."); + } catch (LoginException e) { + assertEquals(TicketValidationException.class, e.getCause().getClass()); + } } - - private boolean hasPrincipalName(final Subject subject, final Class principalClass, final String name) { + + private boolean hasPrincipalName(final Subject subject, final Class principalClass, + final String name) { final Set principals = subject.getPrincipals(principalClass); for (Principal p : principals) { if (p.getName().equals(name)) { diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/proxy/CleanUpTimerTaskTest.java b/cas-client-core/src/test/java/org/jasig/cas/client/proxy/CleanUpTimerTaskTest.java index 55d0d5d01..2b865025d 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/proxy/CleanUpTimerTaskTest.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/proxy/CleanUpTimerTaskTest.java @@ -1,29 +1,26 @@ -/** +/* * 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: + * 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 + * 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 + * 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.proxy; import java.util.TimerTask; - -import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter; - import junit.framework.TestCase; +import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter; /** * Unit test for the {@link CleanUpTimerTask} @@ -41,7 +38,7 @@ public void testRun() throws Exception { timerTask.run(); assertTrue(storage.cleanUpWasCalled()); } - + /** * implementation of the storage interface used only for testing * @@ -49,11 +46,11 @@ public void testRun() throws Exception { */ private static final class ProxyGrantingTicketStorageTestImpl implements ProxyGrantingTicketStorage { private boolean cleanUpCalled = false; - + public boolean cleanUpWasCalled() { return cleanUpCalled; } - + public void cleanUp() { cleanUpCalled = true; } diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorageImplTest.java b/cas-client-core/src/test/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorageImplTest.java index 21b31d519..465885c30 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorageImplTest.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/proxy/ProxyGrantingTicketStorageImplTest.java @@ -1,44 +1,54 @@ -/** +/* * 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: + * 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 + * 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 + * 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.proxy; -import junit.framework.TestCase; +import org.junit.Assert; +import org.junit.Test; /** * Unit test for {@link ProxyGrantingTicketStorageImpl} * * @author Brad Cupit (brad [at] lsu {dot} edu) */ -public class ProxyGrantingTicketStorageImplTest extends TestCase { - public void testCleanUp() throws Exception { +public class ProxyGrantingTicketStorageImplTest { + + private static final int TIME_OUT = 250; + + private ProxyGrantingTicketStorage storage = new ProxyGrantingTicketStorageImpl(TIME_OUT); + + @Test + public void cleanUp() throws Exception { String proxyGrantingTicketIou = "proxyGrantingTicketIou"; - + int timeout = 250; - ProxyGrantingTicketStorageImpl storage = new ProxyGrantingTicketStorageImpl(timeout); - storage.save(proxyGrantingTicketIou, "proxyGrantingTicket"); - + this.storage.save(proxyGrantingTicketIou, "proxyGrantingTicket"); + // sleep long enough for the ticket to timeout Thread.sleep(timeout * 2); - - storage.cleanUp(); - - assertNull(storage.retrieve(proxyGrantingTicketIou)); + + this.storage.cleanUp(); + + Assert.assertNull(this.storage.retrieve(proxyGrantingTicketIou)); + } + + @Test + public void nullPGTIOU() { + Assert.assertNull(this.storage.retrieve(null)); } } diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/session/LogoutMessageGenerator.java b/cas-client-core/src/test/java/org/jasig/cas/client/session/LogoutMessageGenerator.java new file mode 100644 index 000000000..861fffc4f --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/session/LogoutMessageGenerator.java @@ -0,0 +1,56 @@ +/* + * 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; +import java.util.Date; +import java.util.zip.Deflater; + +import org.apache.commons.codec.binary.Base64; + +/** + * Logout message generator to perform tests on Single Sign Out feature. + * Greatly inspired by the source code in the CAS server itself. + * + * @author Jerome Leleu + * @since 3.4.0 + */ +public final class LogoutMessageGenerator { + + private static final String LOGOUT_REQUEST_TEMPLATE = + "@NOT_USED@" + + "%s"; + + public static String generateBackChannelLogoutMessage(final String sessionIndex) { + return String.format(LOGOUT_REQUEST_TEMPLATE, new Date(), sessionIndex); + } + + public static String generateFrontChannelLogoutMessage(final String sessionIndex) { + final String logoutMessage = generateBackChannelLogoutMessage(sessionIndex); + final Deflater deflater = new Deflater(); + deflater.setInput(logoutMessage.getBytes(Charset.forName("ASCII"))); + deflater.finish(); + final byte[] buffer = new byte[logoutMessage.length()]; + final int resultSize = deflater.deflate(buffer); + final byte[] output = new byte[resultSize]; + System.arraycopy(buffer, 0, output, 0, resultSize); + return Base64.encodeBase64String(output); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/session/SingleSignOutFilterTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/session/SingleSignOutFilterTests.java new file mode 100644 index 000000000..93fd66515 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/session/SingleSignOutFilterTests.java @@ -0,0 +1,115 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +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; +import org.springframework.mock.web.MockFilterConfig; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +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"; + + private SingleSignOutFilter filter = new SingleSignOutFilter(); + private MockHttpServletRequest request; + private MockHttpServletResponse response; + private MockFilterChain filterChain; + + @Before + public void setUp() throws Exception { + filter = new SingleSignOutFilter(); + filter.setCasServerUrlPrefix(CAS_SERVER_URL_PREFIX); + filter.setIgnoreInitConfiguration(true); + filter.init(new MockFilterConfig()); + request = new MockHttpServletRequest(); + response = new MockHttpServletResponse(); + filterChain = new MockFilterChain(); + } + + @Test + public void tokenRequest() throws IOException, ServletException { + 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); + assertEquals(session, SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage().removeSessionByMappingId(TICKET)); + } + + @Test + public void backChannelRequest() throws IOException, ServletException { + request.setParameter(ConfigurationKeys.LOGOUT_PARAMETER_NAME.getDefaultValue(), + LogoutMessageGenerator.generateBackChannelLogoutMessage(TICKET)); + request.setMethod("POST"); + final MockHttpSession session = new MockHttpSession(); + SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage().addSessionById(TICKET, session); + filter.doFilter(request, response, filterChain); + assertNull(SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage().removeSessionByMappingId(TICKET)); + } + + @Test + public void frontChannelRequest() throws IOException, ServletException { + final String logoutMessage = LogoutMessageGenerator.generateFrontChannelLogoutMessage(TICKET); + 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); + filter.doFilter(request, response, filterChain); + assertNull(SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage().removeSessionByMappingId(TICKET)); + assertNull(response.getRedirectedUrl()); + } + + @Test + public void frontChannelRequestRelayState() throws IOException, ServletException { + final String logoutMessage = LogoutMessageGenerator.generateFrontChannelLogoutMessage(TICKET); + 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&" + + ConfigurationKeys.RELAY_STATE_PARAMETER_NAME.getDefaultValue() + "=" + RELAY_STATE, response.getRedirectedUrl()); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/session/SingleSignOutHandlerTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/session/SingleSignOutHandlerTests.java new file mode 100644 index 000000000..365a25ea5 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/session/SingleSignOutHandlerTests.java @@ -0,0 +1,182 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockHttpSession; + +/** + * @author Matt Brown + * @version $Revision$ $Date$ + * @since 3.2.1 + */ +public final class SingleSignOutHandlerTests { + + private final static String ANOTHER_PARAMETER = "anotherParameter"; + private final static String TICKET = "ST-xxxxxxxx"; + private final static String URL = "http://mycasserver"; + private final static String LOGOUT_PARAMETER_NAME = "logoutRequest2"; + private final static String FRONT_LOGOUT_PARAMETER_NAME = "SAMLRequest2"; + private final static String RELAY_STATE_PARAMETER_NAME = "RelayState2"; + private final static String ARTIFACT_PARAMETER_NAME = "ticket2"; + + private SingleSignOutHandler handler; + private MockHttpServletRequest request; + private MockHttpServletResponse response; + + @Before + public void setUp() throws Exception { + handler = new SingleSignOutHandler(); + handler.setLogoutParameterName(LOGOUT_PARAMETER_NAME); + handler.setFrontLogoutParameterName(FRONT_LOGOUT_PARAMETER_NAME); + handler.setRelayStateParameterName(RELAY_STATE_PARAMETER_NAME); + handler.setArtifactParameterName(ARTIFACT_PARAMETER_NAME); + handler.setCasServerUrlPrefix(URL); + handler.init(); + request = new MockHttpServletRequest(); + response = new MockHttpServletResponse(); + } + + @Test + public void tokenRequestFailsIfNoSession() { + handler.setEagerlyCreateSessions(false); + request.setSession(null); + request.setParameter(ARTIFACT_PARAMETER_NAME, TICKET); + request.setQueryString(ARTIFACT_PARAMETER_NAME + "=" + TICKET); + assertTrue(handler.process(request, response)); + final SessionMappingStorage storage = handler.getSessionMappingStorage(); + assertNull(storage.removeSessionByMappingId(TICKET)); + } + + @Test + public void tokenRequestFailsIfBadParameter() { + final MockHttpSession session = new MockHttpSession(); + request.setSession(session); + request.setParameter(ANOTHER_PARAMETER, TICKET); + request.setQueryString(ANOTHER_PARAMETER + "=" + TICKET); + assertTrue(handler.process(request, response)); + final SessionMappingStorage storage = handler.getSessionMappingStorage(); + assertNull(storage.removeSessionByMappingId(TICKET)); + } + + @Test + public void tokenRequestOK() { + final MockHttpSession session = new MockHttpSession(); + request.setSession(session); + request.setParameter(ARTIFACT_PARAMETER_NAME, TICKET); + request.setQueryString(ARTIFACT_PARAMETER_NAME + "=" + TICKET); + assertTrue(handler.process(request, response)); + final SessionMappingStorage storage = handler.getSessionMappingStorage(); + assertEquals(session, storage.removeSessionByMappingId(TICKET)); + } + + @Test + public void backChannelLogoutFailsIfMultipart() { + final String logoutMessage = LogoutMessageGenerator.generateBackChannelLogoutMessage(TICKET); + request.setParameter(LOGOUT_PARAMETER_NAME, logoutMessage); + request.setMethod("POST"); + request.setContentType("multipart/form-data"); + final MockHttpSession session = new MockHttpSession(); + handler.getSessionMappingStorage().addSessionById(TICKET, session); + assertTrue(handler.process(request, response)); + assertFalse(session.isInvalid()); + } + + @Test + public void backChannelLogoutFailsIfNoSessionIndex() { + final String logoutMessage = LogoutMessageGenerator.generateBackChannelLogoutMessage(""); + request.setParameter(LOGOUT_PARAMETER_NAME, logoutMessage); + request.setMethod("POST"); + final MockHttpSession session = new MockHttpSession(); + handler.getSessionMappingStorage().addSessionById(TICKET, session); + assertFalse(handler.process(request, response)); + assertFalse(session.isInvalid()); + } + + @Test + public void backChannelLogoutOK() { + final String logoutMessage = LogoutMessageGenerator.generateBackChannelLogoutMessage(TICKET); + request.setParameter(LOGOUT_PARAMETER_NAME, logoutMessage); + request.setMethod("POST"); + final MockHttpSession session = new MockHttpSession(); + handler.getSessionMappingStorage().addSessionById(TICKET, session); + assertFalse(handler.process(request, response)); + assertTrue(session.isInvalid()); + } + + @Test + public void frontChannelLogoutFailsIfBadParameter() { + final String logoutMessage = LogoutMessageGenerator.generateFrontChannelLogoutMessage(TICKET); + request.setParameter(ANOTHER_PARAMETER, logoutMessage); + request.setMethod("GET"); + request.setQueryString(ANOTHER_PARAMETER + "=" + logoutMessage); + final MockHttpSession session = new MockHttpSession(); + handler.getSessionMappingStorage().addSessionById(TICKET, session); + assertTrue(handler.process(request, response)); + assertFalse(session.isInvalid()); + } + + @Test + public void frontChannelLogoutFailsIfNoSessionIndex() { + final String logoutMessage = LogoutMessageGenerator.generateFrontChannelLogoutMessage(""); + request.setParameter(FRONT_LOGOUT_PARAMETER_NAME, logoutMessage); + request.setQueryString(FRONT_LOGOUT_PARAMETER_NAME + "=" + logoutMessage); + request.setMethod("GET"); + final MockHttpSession session = new MockHttpSession(); + handler.getSessionMappingStorage().addSessionById(TICKET, session); + assertFalse(handler.process(request, response)); + assertFalse(session.isInvalid()); + } + + @Test + public void frontChannelLogoutOK() { + final String logoutMessage = LogoutMessageGenerator.generateFrontChannelLogoutMessage(TICKET); + request.setParameter(FRONT_LOGOUT_PARAMETER_NAME, logoutMessage); + request.setQueryString(FRONT_LOGOUT_PARAMETER_NAME + "=" + logoutMessage); + request.setMethod("GET"); + final MockHttpSession session = new MockHttpSession(); + handler.getSessionMappingStorage().addSessionById(TICKET, session); + assertFalse(handler.process(request, response)); + assertTrue(session.isInvalid()); + assertNull(response.getRedirectedUrl()); + } + + @Test + public void frontChannelLogoutRelayStateOK() { + final String logoutMessage = LogoutMessageGenerator.generateFrontChannelLogoutMessage(TICKET); + request.setParameter(FRONT_LOGOUT_PARAMETER_NAME, logoutMessage); + request.setParameter(RELAY_STATE_PARAMETER_NAME, TICKET); + request.setQueryString(FRONT_LOGOUT_PARAMETER_NAME + "=" + logoutMessage + "&" + RELAY_STATE_PARAMETER_NAME + "=" + TICKET); + request.setMethod("GET"); + final MockHttpSession session = new MockHttpSession(); + handler.getSessionMappingStorage().addSessionById(TICKET, session); + assertFalse(handler.process(request, response)); + assertTrue(session.isInvalid()); + assertEquals(URL + "/logout?_eventId=next&" + RELAY_STATE_PARAMETER_NAME + "=" + TICKET, + response.getRedirectedUrl()); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/session/SingleSignoutHandlerTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/session/SingleSignoutHandlerTests.java deleted file mode 100644 index 16a18c0e3..000000000 --- a/cas-client-core/src/test/java/org/jasig/cas/client/session/SingleSignoutHandlerTests.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.jasig.cas.client.session; - -import org.junit.Before; -import org.junit.Test; -import org.springframework.mock.web.MockHttpServletRequest; - -import static org.junit.Assert.*; - -/** - * @author Matt Brown - * @version $Revision$ $Date$ - * @since 3.2.1 - */ -public final class SingleSignoutHandlerTests { - - private SingleSignOutHandler handler; - private MockHttpServletRequest request; - private final static String logoutParameterName = "logoutRequest"; - - @Before - public void setUp() throws Exception { - handler = new SingleSignOutHandler(); - handler.setLogoutParameterName(logoutParameterName); - handler.init(); - request = new MockHttpServletRequest(); - } - - @Test - public void isLogoutRequest() throws Exception { - request.setParameter(logoutParameterName, "true"); - request.setMethod("POST"); - - assertTrue(handler.isLogoutRequest(request)); - } - - /** - * Tests that a multipart request is not considered logoutRequest. Verifies issue CASC-147. - * - * @throws Exception - */ - @Test - public void isLogoutRequestMultipart() throws Exception { - request.setParameter(logoutParameterName, "true"); - request.setMethod("POST"); - request.setContentType("multipart/form-data"); - - assertFalse(handler.isLogoutRequest(request)); - } - -} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/ssl/HttpsURLConnectionFactoryTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/ssl/HttpsURLConnectionFactoryTests.java new file mode 100644 index 000000000..321d9781b --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/ssl/HttpsURLConnectionFactoryTests.java @@ -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); + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/ssl/RegexHostnameVerifierTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/ssl/RegexHostnameVerifierTests.java index 91996ff82..31448fe1d 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/ssl/RegexHostnameVerifierTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/ssl/RegexHostnameVerifierTests.java @@ -1,22 +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: + * 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 + * 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 + * 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. */ - /* $Id$ diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/ssl/WhitelistHostnameVerifierTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/ssl/WhitelistHostnameVerifierTests.java index 9fe857e34..92a41f58b 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/ssl/WhitelistHostnameVerifierTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/ssl/WhitelistHostnameVerifierTests.java @@ -1,22 +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: + * 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 + * 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 + * 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. */ - /* $Id$ @@ -44,11 +43,10 @@ */ 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"); + final WhitelistHostnameVerifier verifier = new WhitelistHostnameVerifier("red.vt.edu, green.vt.edu,blue.vt.edu"); Assert.assertTrue(verifier.verify("red.vt.edu", null)); Assert.assertTrue(verifier.verify("green.vt.edu", null)); Assert.assertTrue(verifier.verify("blue.vt.edu", null)); diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/util/CasFilterTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/util/CasFilterTests.java index 01da8bf8b..89608700b 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/util/CasFilterTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/util/CasFilterTests.java @@ -1,16 +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.util; -import org.junit.Test; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; - +import static org.junit.Assert.assertTrue; +import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; -import java.io.IOException; -import static org.junit.Assert.*; +import org.jasig.cas.client.Protocol; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; /** * @author Scott Battaglia @@ -36,12 +54,15 @@ public void serverName() { testCasFilter.setServerName(serverNameWithSlash); assertTrue(testCasFilter.constructServiceUrl(request, response).startsWith("http://www.cnn.com/cas/test")); - - } private static class TestCasFilter extends AbstractCasFilter { - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + + public TestCasFilter() { + super(Protocol.CAS2); + } + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, + ServletException { // nothing to do } } diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/util/CommonUtilsTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/util/CommonUtilsTests.java index e2435c24b..b2a9d136c 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/util/CommonUtilsTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/util/CommonUtilsTests.java @@ -1,31 +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: + * 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 + * 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 + * 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.net.URL; +import java.util.ArrayList; +import java.util.Collection; + import junit.framework.TestCase; +import org.jasig.cas.client.Protocol; +import org.jasig.cas.client.PublicTestHttpServer; +import org.jasig.cas.client.ssl.HttpsURLConnectionFactory; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; -import java.util.ArrayList; -import java.util.Collection; - /** * Tests for the CommonUtils. * @@ -35,6 +38,8 @@ */ public final class CommonUtilsTests extends TestCase { + private static final PublicTestHttpServer server = PublicTestHttpServer.instance(8090); + public void testRedirectUrlWithParam() { final String loginUrl = "http://localhost:8080/login?myName=foo"; final String fullyConstructedUrl = CommonUtils.constructRedirectUrl(loginUrl, "foo", "foo", false, false); @@ -44,7 +49,7 @@ public void testRedirectUrlWithParam() { for (int i = 0; i < chars.length; i++) { if (chars[i] == '?') { - count ++; + count++; } } @@ -127,11 +132,146 @@ public void testConstructServiceUrlWithTrailingSlash() { request.setScheme("https"); request.setSecure(true); final MockHttpServletResponse response = new MockHttpServletResponse(); - final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, "www.myserver.com", "ticket", false); + final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, "www.myserver.com", + "service", "ticket", false); + + assertEquals(CONST_MY_URL, constructedUrl); + } + + public void testConstructServiceUrlWithParamsCas() { + final String CONST_MY_URL = "https://www.myserver.com/hello/hithere/"; + final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hello/hithere/"); + request.setScheme("https"); + request.setSecure(true); + request.setQueryString("service=this&ticket=that&custom=custom"); + + final MockHttpServletResponse response = new MockHttpServletResponse(); + final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, "www.myserver.com", + Protocol.CAS3.getServiceParameterName(), Protocol.CAS3.getArtifactParameterName() , false); + + assertEquals("https://www.myserver.com/hello/hithere/?custom=custom", constructedUrl); + } + + public void testConstructServiceUrlWithParamsCasAndServerNameWithSchema() { + final String CONST_MY_URL = "https://www.myserver.com/hello/hithere/"; + final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hello/hithere/"); + request.setScheme("https"); + request.setSecure(true); + request.setQueryString("service=this&ticket=that&custom=custom"); + + final MockHttpServletResponse response = new MockHttpServletResponse(); + final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, "https://www.myserver.com", + Protocol.CAS3.getServiceParameterName(), Protocol.CAS3.getArtifactParameterName() , false); + + assertEquals("https://www.myserver.com/hello/hithere/?custom=custom", constructedUrl); + } + + + public void testConstructServiceUrlWithParamsSaml() { + final String CONST_MY_URL = "https://www.myserver.com/hello/hithere/"; + final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hello/hithere/"); + request.setScheme("https"); + request.setSecure(true); + request.setQueryString("TARGET=this&SAMLart=that&custom=custom"); + + final MockHttpServletResponse response = new MockHttpServletResponse(); + final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, "www.myserver.com", + Protocol.SAML11.getServiceParameterName(), Protocol.SAML11.getArtifactParameterName() , false); + + assertEquals("https://www.myserver.com/hello/hithere/?custom=custom", constructedUrl); + } + + public void testConstructServiceUrlWithEncodedParamsSaml() { + final String CONST_MY_URL = "https://www.myserver.com/hello/hithere/"; + final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hello/hithere/"); + request.setScheme("https"); + request.setSecure(true); + request.setQueryString("TARGET%3Dthis%26SAMLart%3Dthat%26custom%3Dcustom"); + + final MockHttpServletResponse response = new MockHttpServletResponse(); + final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, "www.myserver.com", + Protocol.SAML11.getServiceParameterName(), Protocol.SAML11.getArtifactParameterName() , false); + + assertEquals("https://www.myserver.com/hello/hithere/?custom=custom", constructedUrl); + } + + public void testConstructServiceUrlWithNoServiceParametersPassed() { + final String CONST_MY_URL = "https://www.myserver.com/hello/hithere/"; + final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hello/hithere/"); + request.setScheme("https"); + request.setSecure(true); + request.setQueryString("TARGET=Test1&service=Test2&custom=custom"); + + final MockHttpServletResponse response = new MockHttpServletResponse(); + final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, "www.myserver.com", + Protocol.SAML11.getArtifactParameterName() , true); + + assertEquals("https://www.myserver.com/hello/hithere/?custom=custom", constructedUrl); + } + + public void testConstructServiceUrlWithEncodedParams2Saml() { + final String CONST_MY_URL = "https://www.myserver.com/hello/hithere/"; + final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hello/hithere/"); + request.setScheme("https"); + request.setSecure(true); + request.setQueryString("TARGET%3Dthis%26SAMLart%3Dthat%26custom%3Dcustom%20value%20here%26another%3Dgood"); + + final MockHttpServletResponse response = new MockHttpServletResponse(); + final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, "www.myserver.com", + Protocol.SAML11.getServiceParameterName(), Protocol.SAML11.getArtifactParameterName() , true); + + assertEquals("https://www.myserver.com/hello/hithere/?custom=custom+value+here&another=good", constructedUrl); + } + + public void testConstructServiceUrlWithoutEncodedParamsSamlAndNoEncoding() { + final String CONST_MY_URL = "https://www.myserver.com/hello/hithere/"; + final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hello/hithere/"); + request.setScheme("https"); + request.setSecure(true); + request.setQueryString("TARGET=this&SAMLart=that&custom=custom value here&another=good"); + + final MockHttpServletResponse response = new MockHttpServletResponse(); + final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, "www.myserver.com", + Protocol.SAML11.getServiceParameterName(), Protocol.SAML11.getArtifactParameterName() , false); + assertEquals("https://www.myserver.com/hello/hithere/?custom=custom value here&another=good", constructedUrl); + } + + public void testConstructServiceUrlWithEncodedParamsSamlAndNoEncoding() { + final String CONST_MY_URL = "https://www.myserver.com/hello/hithere/"; + final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hello/hithere/"); + request.setScheme("https"); + request.setSecure(true); + request.setQueryString("TARGET=this&SAMLart=that&custom=custom+value+here&another=good"); + + final MockHttpServletResponse response = new MockHttpServletResponse(); + final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, "www.myserver.com", + Protocol.SAML11.getServiceParameterName(), Protocol.SAML11.getArtifactParameterName() , true); + + assertEquals("https://www.myserver.com/hello/hithere/?custom=custom+value+here&another=good", constructedUrl); + } + + private void constructUrlNonStandardPortAndNoPortInConfigTest(final String serverNameList) { + final String CONST_MY_URL = "https://www.myserver.com:555/hello/hithere/"; + final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hello/hithere/"); + request.addHeader("Host", "www.myserver.com"); + request.setScheme("https"); + request.setSecure(true); + request.setServerPort(555); + final MockHttpServletResponse response = new MockHttpServletResponse(); + final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, + serverNameList, "service", "ticket", false); assertEquals(CONST_MY_URL, constructedUrl); } + public void testConstructUrlNonStandardPortAndNoScheme() { + constructUrlNonStandardPortAndNoPortInConfigTest("www.myserver.com"); + } + + public void testConstructUrlNonStandardPortAndScheme() { + constructUrlNonStandardPortAndNoPortInConfigTest("https://www.myserver.com"); + } + public void testConstructUrlWithMultipleHostsNoPortsOrProtocol() { final String CONST_MY_URL = "https://www.myserver.com/hello/hithere/"; final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hello/hithere/"); @@ -139,7 +279,8 @@ public void testConstructUrlWithMultipleHostsNoPortsOrProtocol() { request.setScheme("https"); request.setSecure(true); final MockHttpServletResponse response = new MockHttpServletResponse(); - final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, "www.amazon.com www.bestbuy.com www.myserver.com", "ticket", false); + final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, + "www.amazon.com www.bestbuy.com www.myserver.com", "service", "ticket", false); assertEquals(CONST_MY_URL, constructedUrl); } @@ -150,7 +291,20 @@ public void testConstructURlWithMultipleHostsAndPorts() { request.setScheme("https"); request.setSecure(true); final MockHttpServletResponse response = new MockHttpServletResponse(); - final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, "http://www.amazon.com https://www.bestbuy.com https://www.myserver.com", "ticket", false); + final String constructedUrl = CommonUtils.constructServiceUrl(request, response, null, + "http://www.amazon.com https://www.bestbuy.com https://www.myserver.com", "service", "ticket", false); assertEquals(CONST_MY_URL, constructedUrl); } + + public void testGetResponseFromServer() throws Exception { + final String RESPONSE = "test1\r\ntest2"; + server.content = RESPONSE.getBytes(server.encoding); + + final String responsedContent = CommonUtils.getResponseFromServer(new URL("http://localhost:8090"), new HttpsURLConnectionFactory(), null); + assertEquals(RESPONSE, responsedContent); + } + + public void testUrlEncode() { + assertEquals("this+is+a+very+special+parameter+with+%3D%25%2F", CommonUtils.urlEncode("this is a very special parameter with =%/")); + } } diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/util/ErrorRedirectFilterTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/util/ErrorRedirectFilterTests.java new file mode 100644 index 000000000..69ad9aa82 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/util/ErrorRedirectFilterTests.java @@ -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()); + + } +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/util/HttpServletRequestWrapperFilterTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/util/HttpServletRequestWrapperFilterTests.java index 74b1c619b..addd3899c 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/util/HttpServletRequestWrapperFilterTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/util/HttpServletRequestWrapperFilterTests.java @@ -1,26 +1,33 @@ -/** +/* * 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: + * 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 + * 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 + * 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.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; import junit.framework.TestCase; - import org.jasig.cas.client.authentication.AttributePrincipal; import org.jasig.cas.client.authentication.AttributePrincipalImpl; import org.jasig.cas.client.validation.AssertionImpl; @@ -29,16 +36,6 @@ import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpSession; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - /** * Tests for the HttpServletRequestWrapperFilter. * @@ -57,17 +54,15 @@ public void testWrappedRequest() throws Exception { final MockHttpServletRequest request = new MockHttpServletRequest(); final MockHttpSession session = new MockHttpSession(); - session.setAttribute( - AbstractCasFilter.CONST_CAS_ASSERTION, - new AssertionImpl("test")); + session.setAttribute(AbstractCasFilter.CONST_CAS_ASSERTION, new AssertionImpl("test")); request.setSession(session); filter.doFilter(request, new MockHttpServletResponse(), createFilterChain()); assertEquals("test", this.mockRequest.getRemoteUser()); - + filter.destroy(); } - + public void testIsUserInRole() throws Exception { final MockHttpServletRequest request = new MockHttpServletRequest(); final MockHttpSession session = new MockHttpSession(); @@ -76,13 +71,11 @@ public void testIsUserInRole() throws Exception { config.addInitParameter("roleAttribute", "memberOf"); final HttpServletRequestWrapperFilter filter = new HttpServletRequestWrapperFilter(); filter.init(config); - - final Map attributes = new HashMap(); + + final Map attributes = new HashMap(); attributes.put("memberOf", "administrators"); final AttributePrincipal principal = new AttributePrincipalImpl("alice", attributes); - session.setAttribute( - AbstractCasFilter.CONST_CAS_ASSERTION, - new AssertionImpl(principal)); + session.setAttribute(AbstractCasFilter.CONST_CAS_ASSERTION, new AssertionImpl(principal)); request.setSession(session); @@ -95,7 +88,7 @@ public void testIsUserInRole() throws Exception { filter.destroy(); } - + public void testIsUserInRoleCaseInsensitive() throws Exception { final MockHttpServletRequest request = new MockHttpServletRequest(); final MockHttpSession session = new MockHttpSession(); @@ -105,13 +98,11 @@ public void testIsUserInRoleCaseInsensitive() throws Exception { config.addInitParameter("ignoreCase", "true"); final HttpServletRequestWrapperFilter filter = new HttpServletRequestWrapperFilter(); filter.init(config); - - final Map attributes = new HashMap(); - attributes.put("groupMembership", Arrays.asList(new Object[] {"animals", "ducks"})); + + final Map attributes = new HashMap(); + attributes.put("groupMembership", Arrays.asList(new Object[] { "animals", "ducks" })); final AttributePrincipal principal = new AttributePrincipalImpl("daffy", attributes); - session.setAttribute( - AbstractCasFilter.CONST_CAS_ASSERTION, - new AssertionImpl(principal)); + session.setAttribute(AbstractCasFilter.CONST_CAS_ASSERTION, new AssertionImpl(principal)); request.setSession(session); @@ -129,8 +120,7 @@ public void testIsUserInRoleCaseInsensitive() throws Exception { private FilterChain createFilterChain() { return new FilterChain() { - public void doFilter(ServletRequest request, - ServletResponse response) throws IOException, ServletException { + public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { HttpServletRequestWrapperFilterTests.this.mockRequest = (HttpServletRequest) request; } diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/util/MethodFlag.java b/cas-client-core/src/test/java/org/jasig/cas/client/util/MethodFlag.java index cf39d46e4..a0fd65795 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/util/MethodFlag.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/util/MethodFlag.java @@ -1,22 +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: + * 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 + * 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 + * 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; /** @@ -34,7 +33,7 @@ public class MethodFlag { public boolean wasCalled() { return called; } - + public void setCalled() { called = true; } diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/util/ReflectUtilsTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/util/ReflectUtilsTests.java index 47d50f18a..d39e3bbf1 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/util/ReflectUtilsTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/util/ReflectUtilsTests.java @@ -1,22 +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: + * 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 + * 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 + * 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 junit.framework.TestCase; @@ -34,9 +33,8 @@ public class ReflectUtilsTests extends TestCase { * Test method for {@link org.jasig.cas.client.util.ReflectUtils#newInstance(java.lang.String, java.lang.Object[])}. */ public void testNewInstanceStringObjectArray() { - final Object result = ReflectUtils.newInstance( - "org.jasig.cas.client.validation.Cas10TicketValidator", - new Object[] {"https://localhost/cas"} ); + final Object result = ReflectUtils.newInstance("org.jasig.cas.client.validation.Cas10TicketValidator", + new Object[] { "https://localhost/cas" }); assertNotNull(result); } @@ -45,13 +43,13 @@ public void testNewInstanceStringObjectArray() { */ public void testSetPropertyStringObjectObject() { final TestBean bean = new TestBean(); - + ReflectUtils.setProperty("count", new Integer(30000), bean); assertEquals(30000, bean.getCount()); - + ReflectUtils.setProperty("name", "bob", bean); assertEquals("bob", bean.getName()); - + ReflectUtils.setProperty("flag", Boolean.TRUE, bean); assertTrue(bean.isFlag()); } diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/util/URIBuilderTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/util/URIBuilderTests.java new file mode 100644 index 000000000..5144d131c --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/util/URIBuilderTests.java @@ -0,0 +1,316 @@ +/* + + * 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.Test; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * @author Misagh Moayyed + */ +public class URIBuilderTests { + + @Test + public void allPartsUsed() { + URIBuilder builder = new URIBuilder() + .setScheme("http") + .setHost("apache.org") + .setPath("/shindig") + .setCustomQuery("hello=world") + .setFragment("foo"); + assertEquals("http://apache.org/shindig?hello=world#foo", builder.toString()); + } + + @Test + public void noSchemeUsed() { + URIBuilder builder = new URIBuilder() + .setHost("apache.org") + .setPath("/shindig") + .setCustomQuery("hello=world") + .setFragment("foo"); + assertEquals("//apache.org/shindig?hello=world#foo", builder.toString()); + } + + @Test + public void noAuthorityUsed() { + URIBuilder builder = new URIBuilder() + .setScheme("http") + .setPath("/shindig") + .setCustomQuery("hello=world") + .setFragment("foo"); + assertEquals("http:/shindig?hello=world#foo", builder.toString()); + } + + @Test + public void noPathUsed() { + URIBuilder builder = new URIBuilder() + .setScheme("http") + .setHost("apache.org") + .setCustomQuery("hello=world") + .setFragment("foo"); + assertEquals("http://apache.org?hello=world#foo", builder.toString()); + } + + @Test + public void noQueryUsed() { + URIBuilder builder = new URIBuilder() + .setScheme("http") + .setHost("apache.org") + .setPath("/shindig") + .setFragment("foo"); + assertEquals("http://apache.org/shindig#foo", builder.toString()); + } + + @Test + public void noFragmentUsed() { + URIBuilder builder = new URIBuilder() + .setScheme("http") + .setHost("apache.org") + .setPath("/shindig") + .setCustomQuery("hello=world"); + assertEquals("http://apache.org/shindig?hello=world", builder.toString()); + } + + @Test + public void hostRelativePaths() { + URIBuilder builder = new URIBuilder() + .setPath("/shindig") + .setCustomQuery("hello=world") + .setFragment("foo"); + assertEquals("/shindig?hello=world#foo", builder.toString()); + } + + @Test + public void relativePaths() { + URIBuilder builder = new URIBuilder() + .setPath("foo") + .setCustomQuery("hello=world") + .setFragment("foo"); + assertEquals("foo?hello=world#foo", builder.toString()); + } + + @Test + public void noPathNoHostNoAuthority() { + URIBuilder builder = new URIBuilder() + .setCustomQuery("hello=world") + .setFragment("foo"); + assertEquals("?hello=world#foo", builder.toString()); + } + + @Test + public void justSchemeAndAuthority() { + URIBuilder builder = new URIBuilder() + .setScheme("http") + .setHost("apache.org"); + assertEquals("http://apache.org", builder.toString()); + } + + @Test + public void justPath() { + URIBuilder builder = new URIBuilder() + .setPath("/shindig"); + assertEquals("/shindig", builder.toString()); + } + + @Test + public void justAuthorityAndPath() { + URIBuilder builder = new URIBuilder() + .setHost("apache.org") + .setPath("/shindig"); + assertEquals("//apache.org/shindig", builder.toString()); + } + + @Test + public void justQuery() { + URIBuilder builder = new URIBuilder() + .setCustomQuery("hello=world"); + assertEquals("?hello=world", builder.toString()); + } + + @Test + public void justFragment() { + URIBuilder builder = new URIBuilder() + .setFragment("foo"); + assertEquals("#foo", builder.toString()); + } + + @Test + public void addSingleQueryParameter() { + URIBuilder builder = new URIBuilder() + .setScheme("http") + .setHost("apache.org") + .setPath("/shindig") + .addParameter("hello", "world") + .setFragment("foo"); + assertEquals("http://apache.org/shindig?hello=world#foo", builder.toString()); + } + + @Test + public void addTwoQueryParameters() { + URIBuilder builder = new URIBuilder() + .setScheme("http") + .setHost("apache.org") + .setPath("/shindig") + .addParameter("hello", "world") + .addParameter("foo", "bar") + .setFragment("foo"); + assertEquals("http://apache.org/shindig?hello=world&foo=bar#foo", builder.toString()); + } + + @Test + public void iterableQueryParameters() { + List list = new ArrayList(); + list.add(new URIBuilder.BasicNameValuePair("hello", "world")); + list.add(new URIBuilder.BasicNameValuePair("hello", "monde")); + URIBuilder builder = new URIBuilder() + .setScheme("http") + .setHost("apache.org") + .setPath("/shindig") + .addParameters(list) + .setFragment("foo"); + assertEquals("http://apache.org/shindig?hello=world&hello=monde#foo", builder.toString()); + } + + @Test + public void removeQueryParameter() { + URIBuilder uri = new URIBuilder("http://www.example.com/foo?bar=baz&quux=baz"); + uri.removeQuery(); + assertEquals("http://www.example.com/foo", uri.toString()); + } + + @Test + public void addIdenticalParameters() { + URIBuilder builder = new URIBuilder() + .setScheme("http") + .setHost("apache.org") + .setPath("/shindig") + .addParameter("hello", "world") + .addParameter("hello", "goodbye") + .setFragment("foo"); + assertEquals("http://apache.org/shindig?hello=world&hello=goodbye#foo", builder.toString()); + } + + @Test + public void queryStringIsUnescaped() { + URIBuilder builder = new URIBuilder() + .setScheme("http") + .setHost("apache.org") + .setPath("/shindig") + .setCustomQuery("hello+world=world%26bar"); + assertEquals("world&bar", builder.build().getQuery().split("=")[1]); + } + + @Test + public void queryParamsAreEscaped() { + URIBuilder builder = new URIBuilder(true) + .setScheme("http") + .setHost("apache.org") + .setEncodedPath("/shindig") + .addParameter("hello world", "foo&bar") + .setFragment("foo"); + assertEquals("http://apache.org/shindig?hello+world=foo%26bar#foo", builder.toString()); + assertEquals("hello+world=foo&bar", builder.build().getQuery()); + } + + @Test + public void addSingleFragmentParameter() { + URIBuilder builder = new URIBuilder() + .setScheme("http") + .setHost("apache.org") + .setPath("/shindig") + .setFragment("hello=world") + .setCustomQuery("foo"); + assertEquals("http://apache.org/shindig?foo#hello=world", builder.toString()); + } + + @Test + public void fragmentStringIsUnescaped() { + URIBuilder builder = new URIBuilder(true) + .setScheme("http") + .setHost("apache.org") + .setPath("/shindig") + .setEncodedFragment("hello+world=world%26bar"); + + assertEquals("world&bar", builder.build().getFragment().split("=")[1]); + } + + @Test + public void parse() { + URIBuilder builder = new URIBuilder() + .digestURI(URI.create("http://apache.org/shindig?foo=bar%26baz&foo=three#blah")); + + assertEquals("http", builder.getScheme()); + assertEquals("apache.org", builder.getHost()); + assertEquals("/shindig", builder.getPath()); + + List list = builder.getQueryParams(); + for (URIBuilder.BasicNameValuePair pair : list) { + assertEquals(pair.getName(), "foo"); + assertTrue(pair.getValue().equals("three") || pair.getValue().equals("bar")); + } + assertEquals(list.size(), 2); + assertEquals("blah", builder.getFragment()); + } + + @Test + public void constructFromUriAndBack() { + URI uri = URI.create("http://apache.org/foo/bar?foo=bar&a=b&c=d&y=z&foo=zoo#foo"); + URIBuilder builder = new URIBuilder(uri); + + assertEquals(uri, builder.build()); + } + + @Test + public void constructFromUriAndModify() { + URI uri = URI.create("http://apache.org/foo/bar?foo=bar#foo"); + URIBuilder builder = new URIBuilder(uri); + + builder.setHost("example.org"); + builder.addParameter("bar", "foo"); + + assertEquals("http://example.org/foo/bar?foo=bar&bar=foo#foo", builder.toString()); + } + + @Test + public void equalsAndHashCodeOk() { + URIBuilder uri = new URIBuilder().digestURI(URI.create("http://example.org/foo/bar/baz?blah=blah#boo")); + URIBuilder uri2 = new URIBuilder(URI.create("http://example.org/foo/bar/baz?blah=blah#boo")); + + assertEquals(uri, uri2); + assertEquals(uri2, uri); + + assertEquals(uri, uri); + + assertNotNull(uri); + assertNotSame(uri, "http://example.org/foo/bar/baz?blah=blah#boo"); + assertNotSame(uri, URI.create("http://example.org/foo/bar/baz?blah=blah#boo")); + assertEquals(uri.hashCode(), uri2.hashCode()); + } + + +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/validation/AbstractTicketValidatorTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/validation/AbstractTicketValidatorTests.java index fa6036f63..77af2b856 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/validation/AbstractTicketValidatorTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/validation/AbstractTicketValidatorTests.java @@ -1,22 +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: + * 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 + * 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 + * 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; /** diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/validation/AssertionImplTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/validation/AssertionImplTests.java index d94d2a896..de6e8562e 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/validation/AssertionImplTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/validation/AssertionImplTests.java @@ -1,31 +1,29 @@ -/** +/* * 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: + * 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 + * 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 + * 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.util.HashMap; +import java.util.Map; import junit.framework.TestCase; import org.jasig.cas.client.authentication.AttributePrincipal; import org.jasig.cas.client.authentication.AttributePrincipalImpl; -import java.util.HashMap; -import java.util.Map; - /** * Test cases for the {@link AssertionImpl}. * @@ -37,7 +35,7 @@ public final class AssertionImplTests extends TestCase { private static final AttributePrincipal CONST_PRINCIPAL = new AttributePrincipalImpl("test"); - private static final Map CONST_ATTRIBUTES = new HashMap(); + private static final Map CONST_ATTRIBUTES = new HashMap(); static { CONST_ATTRIBUTES.put("test", "test"); @@ -52,8 +50,7 @@ public void testPrincipalConstructor() { } public void testCompleteConstructor() { - final Assertion assertion = new AssertionImpl(CONST_PRINCIPAL, - CONST_ATTRIBUTES); + final Assertion assertion = new AssertionImpl(CONST_PRINCIPAL, CONST_ATTRIBUTES); assertEquals(CONST_PRINCIPAL, assertion.getPrincipal()); assertEquals(CONST_ATTRIBUTES, assertion.getAttributes()); diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas10TicketValidationFilterTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas10TicketValidationFilterTests.java new file mode 100644 index 000000000..f2da17388 --- /dev/null +++ b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas10TicketValidationFilterTests.java @@ -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.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; + +/** + * Unit test for {@link Cas10TicketValidationFilter}. + * + * @author Marvin S. Addison + */ +public class Cas10TicketValidationFilterTests { + @Test + public void testThrowsRenewInitParam() throws Exception { + final Cas10TicketValidationFilter f = new Cas10TicketValidationFilter(); + final MockFilterConfig config = new MockFilterConfig(); + config.addInitParameter("casServerUrlPrefix", "https://cas.example.com"); + config.addInitParameter("renew", "true"); + try { + f.init(config); + fail("Should have thrown IllegalArgumentException."); + } catch (final IllegalArgumentException e) { + assertTrue(e.getMessage().contains("Renew MUST")); + } + } + + @Test + public void testAllowsRenewContextParam() throws Exception { + final Cas10TicketValidationFilter f = new Cas10TicketValidationFilter(); + final MockServletContext context = new MockServletContext(); + context.addInitParameter("casServerUrlPrefix", "https://cas.example.com"); + context.addInitParameter("renew", "true"); + 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()); + } +} \ No newline at end of file diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas10TicketValidatorTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas10TicketValidatorTests.java index 7a8a991d3..280e0eb20 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas10TicketValidatorTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas10TicketValidatorTests.java @@ -1,34 +1,29 @@ -/** +/* * 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: + * 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 + * 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 + * 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 static org.junit.Assert.*; +import java.io.UnsupportedEncodingException; import org.jasig.cas.client.PublicTestHttpServer; -import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; -import java.io.UnsupportedEncodingException; - -import static org.junit.Assert.*; - /** * Test cases for the {@link Cas10TicketValidator}. * @@ -60,8 +55,7 @@ public void setUp() throws Exception { public void testNoResponse() throws Exception { server.content = "no\n\n".getBytes(server.encoding); try { - this.ticketValidator.validate("testTicket", - "myService"); + this.ticketValidator.validate("testTicket", "myService"); fail("ValidationException expected."); } catch (final TicketValidationException e) { // expected @@ -69,24 +63,31 @@ public void testNoResponse() throws Exception { } @Test - public void testYesResponse() throws TicketValidationException, - UnsupportedEncodingException { + public void testYesResponse() throws TicketValidationException, UnsupportedEncodingException { server.content = "yes\nusername\n\n".getBytes(server.encoding); - final Assertion assertion = this.ticketValidator.validate("testTicket", - "myService"); + final Assertion assertion = this.ticketValidator.validate("testTicket", "myService"); assertEquals(CONST_USERNAME, assertion.getPrincipal().getName()); } @Test public void testBadResponse() throws UnsupportedEncodingException { - server.content = "falalala\n\n" - .getBytes(server.encoding); + server.content = "falalala\n\n".getBytes(server.encoding); try { - this.ticketValidator.validate("testTicket", - "myService"); + this.ticketValidator.validate("testTicket", "myService"); fail("ValidationException expected."); } catch (final TicketValidationException e) { // 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)); + } } diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ProxyReceivingTicketValidationFilterTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ProxyReceivingTicketValidationFilterTests.java index 8539f952f..7c11042c1 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ProxyReceivingTicketValidationFilterTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ProxyReceivingTicketValidationFilterTests.java @@ -1,33 +1,33 @@ -/** +/* * 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: + * 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 + * 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 + * 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.util.Timer; +import java.util.TimerTask; import junit.framework.TestCase; 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 java.util.Timer; -import java.util.TimerTask; +import org.springframework.mock.web.MockServletContext; /** * Unit test for {@link org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter} @@ -82,7 +82,7 @@ public void cancel() { assertTrue(cancelMethodFlag.wasCalled()); } -public void testCallsCleanAllOnSchedule() throws Exception { + public void testCallsCleanAllOnSchedule() throws Exception { final MethodFlag timerTaskFlag = new MethodFlag(); final Cas20ProxyReceivingTicketValidationFilter filter = newCas20ProxyReceivingTicketValidationFilter(); @@ -149,38 +149,68 @@ public void testThrowsForNullStorage() throws Exception { 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("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", + 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)); } - + public void testRenewInitParamThrows() throws Exception { + final Cas20ProxyReceivingTicketValidationFilter f = new Cas20ProxyReceivingTicketValidationFilter(); + final MockFilterConfig config = new MockFilterConfig(); + config.addInitParameter("casServerUrlPrefix", "https://cas.example.com"); + config.addInitParameter("renew", "true"); + try { + f.init(config); + fail("Should have thrown IllegalArgumentException."); + } catch (final IllegalArgumentException e) { + assertTrue(e.getMessage().contains("Renew MUST")); + } + } + + public void testAllowsRenewContextParam() throws Exception { + final Cas20ProxyReceivingTicketValidationFilter f = new Cas20ProxyReceivingTicketValidationFilter(); + final MockServletContext context = new MockServletContext(); + context.addInitParameter("casServerUrlPrefix", "https://cas.example.com"); + context.addInitParameter("renew", "true"); + 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()); + } /** * construct a working {@link org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter} @@ -192,4 +222,4 @@ private Cas20ProxyReceivingTicketValidationFilter newCas20ProxyReceivingTicketVa return filter; } -} \ No newline at end of file +} diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidatorTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidatorTests.java index 691d89bb2..b0db574ca 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidatorTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ProxyTicketValidatorTests.java @@ -1,39 +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: + * 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 + * 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 + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; import org.jasig.cas.client.PublicTestHttpServer; import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl; import org.jasig.cas.client.proxy.ProxyRetriever; -import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.Assert.*; - /** * Test cases for the {@link Cas20ProxyTicketValidator}. * @@ -59,7 +56,7 @@ public static void classCleanUp() { @Before public void setUp() throws Exception { final List list = new ArrayList(); - list.add(new String[] {"proxy1", "proxy2", "proxy3"}); + list.add(new String[] { "proxy1", "proxy2", "proxy3" }); this.ticketValidator = new Cas20ProxyTicketValidator(CONST_CAS_SERVER_URL_PREFIX + "8089"); this.ticketValidator.setRenew(true); @@ -77,29 +74,26 @@ private ProxyRetriever getProxyRetriever() { return new ProxyRetriever() { /** Unique Id For serialization. */ - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - public String getProxyTicketIdFor(String proxyGrantingTicketId, String targetService) { + public String getProxyTicketIdFor(String proxyGrantingTicketId, String targetService) { return "test"; } }; } @Test - public void testProxyChainWithValidProxy() throws TicketValidationException, - UnsupportedEncodingException { + public void testProxyChainWithValidProxy() throws TicketValidationException, UnsupportedEncodingException { final String USERNAME = "username"; final String RESPONSE = "usernamePGTIOU-84678-8a9d...proxy1proxy2proxy3"; server.content = RESPONSE.getBytes(server.encoding); - final Assertion assertion = this.ticketValidator.validate("test", - "test"); + final Assertion assertion = this.ticketValidator.validate("test", "test"); assertEquals(USERNAME, assertion.getPrincipal().getName()); } @Test - public void testProxyChainWithInvalidProxy() throws TicketValidationException, - UnsupportedEncodingException { + public void testProxyChainWithInvalidProxy() throws TicketValidationException, UnsupportedEncodingException { final String RESPONSE = "usernamePGTIOU-84678-8a9d...proxy7proxy2proxy3"; server.content = RESPONSE.getBytes(server.encoding); @@ -111,16 +105,48 @@ public void testProxyChainWithInvalidProxy() throws TicketValidationException, } } + @Test + public void testRegexProxyChainWithValidProxy() throws TicketValidationException, UnsupportedEncodingException { + final List list = new ArrayList(); + list.add(new String[] { "proxy1", "proxy2", "^proxy3/[a-z]*/" }); + this.ticketValidator.setAllowedProxyChains(new ProxyList(list)); + + final String USERNAME = "username"; + final String RESPONSE = "usernamePGTIOU-84678-8a9d...proxy1proxy2proxy3/abc/"; + server.content = RESPONSE.getBytes(server.encoding); + + final Assertion assertion = this.ticketValidator.validate("test", "test"); + assertEquals(USERNAME, assertion.getPrincipal().getName()); + } + + @Test + public void testRegexProxyChainWithInvalidProxy() throws TicketValidationException, UnsupportedEncodingException { + final List list = new ArrayList(); + list.add(new String[] { "proxy1", "proxy2", "^proxy3/[a-z]*/" }); + this.ticketValidator.setAllowedProxyChains(new ProxyList(list)); + + final String RESPONSE = "usernamePGTIOU-84678-8a9d...proxy1proxy2proxy3/ABC/"; + server.content = RESPONSE.getBytes(server.encoding); + + try { + this.ticketValidator.validate("test", "test"); + fail("Invalid proxy chain"); + } catch (InvalidProxyChainTicketValidationException e) { + // expected + } + } + @Test public void testConstructionFromSpringBean() throws TicketValidationException, UnsupportedEncodingException { - final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:cas20ProxyTicketValidator.xml"); + final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "classpath:cas20ProxyTicketValidator.xml"); final Cas20ProxyTicketValidator v = (Cas20ProxyTicketValidator) context.getBean("proxyTicketValidator"); - + final String USERNAME = "username"; final String RESPONSE = "usernamePGTIOU-84678-8a9d...proxy1proxy2proxy3"; server.content = RESPONSE.getBytes(server.encoding); - final Assertion assertion = v.validate("test","test"); + final Assertion assertion = v.validate("test", "test"); assertEquals(USERNAME, assertion.getPrincipal().getName()); } diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidatorTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidatorTests.java index c250969d2..14d3f82ae 100644 --- a/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidatorTests.java +++ b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidatorTests.java @@ -1,37 +1,33 @@ -/** +/* * 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: + * 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 + * 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 + * 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 static org.junit.Assert.*; +import java.io.UnsupportedEncodingException; +import java.util.List; import org.jasig.cas.client.PublicTestHttpServer; import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl; import org.jasig.cas.client.proxy.ProxyRetriever; -import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.*; - -import java.io.UnsupportedEncodingException; - /** * Test cases for the {@link Cas20ServiceTicketValidator}. * @@ -74,9 +70,9 @@ private ProxyRetriever getProxyRetriever() { return new ProxyRetriever() { /** Unique Id for serialization. */ - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - public String getProxyTicketIdFor(String proxyGrantingTicketId, String targetService) { + public String getProxyTicketIdFor(String proxyGrantingTicketId, String targetService) { return "test"; } }; @@ -95,23 +91,19 @@ public void testNoResponse() throws UnsupportedEncodingException { } @Test - public void testYesResponseButNoPgt() throws TicketValidationException, - UnsupportedEncodingException { + public void testYesResponseButNoPgt() throws TicketValidationException, UnsupportedEncodingException { final String USERNAME = "username"; final String RESPONSE = "" - + USERNAME - + ""; + + USERNAME + ""; server.content = RESPONSE.getBytes(server.encoding); - final Assertion assertion = this.ticketValidator.validate("test", - "test"); + final Assertion assertion = this.ticketValidator.validate("test", "test"); assertEquals(USERNAME, assertion.getPrincipal().getName()); } @Test - public void testYesResponseWithPgt() throws TicketValidationException, - UnsupportedEncodingException { + public void testYesResponseWithPgt() throws TicketValidationException, UnsupportedEncodingException { final String USERNAME = "username"; final String PGTIOU = "testPgtIou"; final String PGT = "test"; @@ -124,28 +116,33 @@ public void testYesResponseWithPgt() throws TicketValidationException, server.content = RESPONSE.getBytes(server.encoding); this.proxyGrantingTicketStorage.save(PGTIOU, PGT); - final Assertion assertion = this.ticketValidator.validate("test", - "test"); + final Assertion assertion = this.ticketValidator.validate("test", "test"); assertEquals(USERNAME, assertion.getPrincipal().getName()); -// assertEquals(PGT, assertion.getProxyGrantingTicketId()); + // assertEquals(PGT, assertion.getProxyGrantingTicketId()); } @Test - public void testGetAttributes() throws TicketValidationException, - UnsupportedEncodingException { + public void testGetAttributes() throws TicketValidationException, UnsupportedEncodingException { final String USERNAME = "username"; final String PGTIOU = "testPgtIou"; final String RESPONSE = "" - + USERNAME - + "" - + PGTIOU - + "testid"; - + + USERNAME + + "" + + PGTIOU + + "testidtest1\n\ntestvalue1value2"; + server.content = RESPONSE.getBytes(server.encoding); final Assertion assertion = this.ticketValidator.validate("test", "test"); assertEquals(USERNAME, assertion.getPrincipal().getName()); assertEquals("test", assertion.getPrincipal().getAttributes().get("password")); assertEquals("id", assertion.getPrincipal().getAttributes().get("eduPersonId")); + assertEquals("test1\n\ntest", assertion.getPrincipal().getAttributes().get("longAttribute")); + try { + List multivalued = (List) assertion.getPrincipal().getAttributes().get("multivaluedAttribute"); + assertArrayEquals(new String[] { "value1", "value2" }, multivalued.toArray()); + } catch (Exception e) { + fail("'multivaluedAttribute' attribute expected as List object."); + } //assertEquals(PGT, assertion.getProxyGrantingTicketId()); } diff --git a/cas-client-core/src/test/java/org/jasig/cas/client/validation/Saml11TicketValidatorTests.java b/cas-client-core/src/test/java/org/jasig/cas/client/validation/Saml11TicketValidatorTests.java deleted file mode 100644 index d55b548ae..000000000 --- a/cas-client-core/src/test/java/org/jasig/cas/client/validation/Saml11TicketValidatorTests.java +++ /dev/null @@ -1,91 +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: - * - * 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.PublicTestHttpServer; -import org.jasig.cas.client.util.CommonUtils; -import org.junit.*; - -import java.io.UnsupportedEncodingException; -import java.util.Date; - -import static org.junit.Assert.*; - -/** - * @author Scott Battaglia - * @version $Revision$ $Date$ - * @since 3.1.3 - */ -@Ignore -public final class Saml11TicketValidatorTests extends AbstractTicketValidatorTests { - - private Saml11TicketValidator validator; - - private static final PublicTestHttpServer server = PublicTestHttpServer.instance(9051); - - @Before - public void setUp() throws Exception { - this.validator = new Saml11TicketValidator(CONST_CAS_SERVER_URL_PREFIX + "9051"); - this.validator.setTolerance(1000L); - } - - /*@AfterClass - public static void cleanUp() throws Exception { - server.shutdown(); - }*/ - - @Test - public void testValidationFailedResponse() throws UnsupportedEncodingException { - final String RESPONSE = "Success"; - - server.content = RESPONSE.getBytes(server.encoding); - try { - this.validator.validate("test", "test"); - fail("ValidationException expected due to 'no' response"); - } catch (final TicketValidationException e) { - // expected - } - } - - @Test - public void testValidationSuccessWithNoAttributes() throws UnsupportedEncodingException { - final Date now = new Date(); - final Date before = new Date(now.getTime() - 5000); - final Date after = new Date(now.getTime() + 200000000); - final String RESPONSE = "testtestPrincipalurn:oasis:names:tc:SAML:1.0:cm:artifact"; - server.content = RESPONSE.getBytes(server.encoding); - try { - final Assertion a = this.validator.validate("test", "test"); - assertEquals("testPrincipal", a.getPrincipal().getName()); - } catch (final TicketValidationException e) { - fail(e.toString()); - } - } -} diff --git a/cas-client-core/src/test/resources/cas20ProxyTicketValidator.xml b/cas-client-core/src/test/resources/cas20ProxyTicketValidator.xml index 3f3838908..579ecba6e 100644 --- a/cas-client-core/src/test/resources/cas20ProxyTicketValidator.xml +++ b/cas-client-core/src/test/resources/cas20ProxyTicketValidator.xml @@ -5,42 +5,41 @@ 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: + 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 + 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 + 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. --> - - - - - - test test2 test3 test4 test5 - mytest mytest1 mytest2 mytest3 - proxy1 proxy2 proxy3 - - - - + + + + + test test2 test3 test4 test5 + mytest mytest1 mytest2 mytest3 + proxy1 proxy2 proxy3 + + + + + + + + - - - - \ No newline at end of file diff --git a/cas-client-core/src/test/resources/log4j.properties b/cas-client-core/src/test/resources/log4j.properties new file mode 100644 index 000000000..8ccad3bf1 --- /dev/null +++ b/cas-client-core/src/test/resources/log4j.properties @@ -0,0 +1,30 @@ +# +# log4j configuration to get clean console listing during Maven tests +# + +# +# 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. +# + +log4j.rootCategory=INFO, stdout +log4j.logger.org.apache.xml.security=OFF +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%-5p %d{ISO8601} %t::%c{1} - %m%n + +log4j.logger.org.jasig.cas=DEBUG diff --git a/cas-client-integration-atlassian/NOTICE b/cas-client-integration-atlassian/NOTICE new file mode 100644 index 000000000..264e83971 --- /dev/null +++ b/cas-client-integration-atlassian/NOTICE @@ -0,0 +1,39 @@ +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. + +This project includes: + AOP alliance under Public Domain + Atlassian Event under Atlassian End User License + Atlassian JIRA - Code - Core under Atlassian End User License + Atlassian Seraph under Atlassian End User License + atlassian-osuser under Atlassian End User License + Confluence Core under Atlassian End User License + Google Collections Library under The Apache Software License, Version 2.0 + 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 + JCL 1.1.1 implemented over SLF4J under MIT License + JUnit under Common Public License Version 1.0 + SLF4J API Module under MIT License + SLF4J Simple Binding under MIT License + spring-aop under The Apache Software License, Version 2.0 + spring-asm under The Apache Software License, Version 2.0 + spring-beans under The Apache Software License, Version 2.0 + spring-context under The Apache Software License, Version 2.0 + spring-core under The Apache Software License, Version 2.0 + spring-expression under The Apache Software License, Version 2.0 + diff --git a/cas-client-integration-atlassian/pom.xml b/cas-client-integration-atlassian/pom.xml index a8dc3203b..53b1f1540 100644 --- a/cas-client-integration-atlassian/pom.xml +++ b/cas-client-integration-atlassian/pom.xml @@ -1,556 +1,1378 @@ - - + + + org.jasig.cas.client + 3.4.1-SNAPSHOT + cas-client + + 4.0.0 org.jasig.cas.client - 3.3-SNAPSHOT - cas-client - - 4.0.0 - org.jasig.cas.client - cas-client-integration-atlassian - jar - Jasig CAS Client for Java - Atlassian Integration + cas-client-integration-atlassian + jar + Jasig CAS Client for Java - Atlassian Integration - - - atlassian-seraph - com.atlassian.seraph - 2.5.0 - provided - jar - - - opensymphony - propertyset - - - true - - + + + atlassian-seraph + com.atlassian.seraph + 2.5.1 + provided + jar + + + commons-lang + commons-lang + + + log4j + log4j + + + javax.servlet + servlet-api + + + opensymphony + oscore + + + com.atlassian.security + atlassian-secure-random + + + com.atlassian.security + atlassian-cookie-tools + + + true + + + + org.springframework + spring-context + + + + atlassian-osuser + com.atlassian.osuser + 1.1.2 + provided + jar + true + + + opensymphony + oscore + + + opensymphony + propertyset + + + commons-logging + commons-logging + - - atlassian-osuser - com.atlassian.osuser - 1.1.2 - provided - jar - true - + + ofbcore + ofbcore-jira-entity + + + ofbcore + ofbcore-jira-share + + + log4j + log4j + - - - com.atlassian.confluence - confluence - 3.4.4 - provided - + + dom4j + dom4j + + + + + + + com.atlassian.confluence + confluence + 3.5 + provided + + + opensymphony + webwork + - com.atlassian.mail - atlassian-mail + com.atlassian.crowd + embedded-crowd-api - com.atlassian.velocity - atlassian-velocity + com.atlassian.crowd + crowd-integration-api - com.atlassian.core - atlassian-core + com.atlassian.crowd + crowd-integration-seraph22 - com.atlassian.config - atlassian-config + com.atlassian.crowd + embedded-crowd-spi - com.atlassian.spring - atlassian-spring + com.atlassian.crowd + crowd-password-encoders - com.atlassian.event - atlassian-event + com.atlassian.crowd + atlassian-embedded-crowd-atlassian-user - com.atlassian.bucket - atlassian-bucket + com.atlassian.crowd + atlassian-embedded-crowd-hibernate2 - com.atlassian.profiling - atlassian-profiling + com.atlassian.crowd + embedded-crowd-core - atlassian-trackback - atlassian-trackback + com.atlassian.crowd + crowd-persistence - com.atlassian.extras - atlassian-extras + com.atlassian.crowd + crowd-ldap - com.atlassian.johnson - atlassian-johnson + org.springframework.ldap + spring-ldap-core - atlassian-cache-servlet - atlassian-cache-servlet + com.atlassian.confluence + confluence-upgrade - com.atlassian.plugins - atlassian-plugins + com.atlassian.gzipfilter + atlassian-gzipfilter - atlassian-bandana - atlassian-bandana + com.atlassian.applinks + applinks-api - atlassian-gregory - atlassian-gregory + com.atlassian.applinks + applinks-spi - com.atlassian.user - atlassian-user + com.atlassian.applinks + applinks-host - com.atlassian.renderer - atlassian-renderer + com.atlassian.util.concurrent + atlassian-util-concurrent - com.atlassian.bonnie - atlassian-bonnie + com.atlassian.modzdetector + modz-detector - joda-time - joda-time + com.atlassian.mail + atlassian-mail - backport-util-concurrent - backport-util-concurrent + com.atlassian.velocity + atlassian-velocity - com.atlassian.seraph - atlassian-seraph + com.atlassian.core + atlassian-core - javax.activation - activation + com.atlassian.config + atlassian-config - log4j - log4j + com.atlassian.spring + atlassian-spring - commons-lang - commons-lang + com.atlassian.confluence + confluence-bucket - commons-io - commons-io + com.atlassian.xwork + atlassian-xwork-10 - commons-beanutils - commons-beanutils + com.atlassian.xwork + atlassian-xwork-core - dwr - dwr + com.atlassian.profiling + atlassian-profiling - hibernate - hibernate + com.atlassian.trackback + atlassian-trackback - cglib - cglib + com.atlassian.extras + atlassian-extras - dom4j - dom4j + com.atlassian.johnson + atlassian-johnson - odmg - odmg + com.atlassian.plugins + atlassian-plugins-core - c3p0 - c3p0 + com.atlassian.plugins + atlassian-plugins-servlet - ant - ant + com.atlassian.plugins + atlassian-plugins-webfragment - jta - jta + com.atlassian.plugins + atlassian-plugins-webresource - opensymphony - webwork + com.atlassian.plugins + atlassian-plugins-osgi - opensymphony - xwork + com.atlassian.plugins + atlassian-plugins-spring - ognl - ognl + com.atlassian.bandana + atlassian-bandana - opensymphony - oscore + com.atlassian.user + atlassian-user - oscache - oscache + com.atlassian.renderer + atlassian-renderer - osuser - osuser + com.atlassian.bonnie + atlassian-bonnie - propertyset - propertyset + com.atlassian.jdk.utilities + atlassian-jdk-utilities - velocity - velocity + joda-time + joda-time - javax.servlet - servlet-api + com.atlassian.seraph + atlassian-seraph - aspectj - aspectjweaver + com.atlassian.security.auth.trustedapps + atlassian-trusted-apps-core + + + com.atlassian.security.auth.trustedapps + atlassian-trusted-apps-seraph-integration + + + javax.activation + activation - ldaptemplate - ldaptemplate + log4j + log4j - opensymphony - sitemesh + org.slf4j + slf4j-api + + + org.slf4j + slf4j-log4j12 + + + org.slf4j + jul-to-slf4j + + + commons-lang + commons-lang + + + commons-logging + commons-logging + + + commons-io + commons-io - - velocity-tools - velocity-tools + commons-beanutils + commons-beanutils - radeox - radeox + commons-fileupload + commons-fileupload - jython - jython + org.directwebremoting + dwr - lucene - lucene-core + hibernate + hibernate - lucene - lucene-analyzers + cglib + cglib - commons-digester - commons-digester + dom4j + dom4j - tm-extractors - tm-extractors + odmg + odmg - pdfbox - pdfbox + c3p0 + c3p0 - bouncycastle - bcprov-jdk14 + javax.transaction + jta + + + javax.media + jai-core - FontBox - FontBox + com.sun + jai_codec - poi - poi + opensymphony + webwork - fop - fop + opensymphony + xwork - xmlgraphics-commons - xmlgraphics-commons + ognl + ognl - batik - batik-all + opensymphony + oscore - avalon-framework - avalon-framework + oscache + oscache - com.sun - jai_core + osuser + osuser - com.sun - jai_codec + opensymphony + propertyset - css2parser - ss_css2 + org.apache.velocity + velocity - saxon - saxon + javax.servlet + servlet-api - org.hibernate - jtidy + javax.mail + mail - quartz - quartz + org.springframework + spring-core - hsqldb - hsqldb + org.springframework + spring-web - mockobjects - mockobjects-core + org.springframework + spring-support - mockobjects - mockobjects + org.springframework + spring-aop + + org.springframework + spring-hibernate2 + + + org.springframework + spring-beans + + + org.springframework + spring-jdbc + + + org.springframework + spring-dao + + + org.springframework + spring-jmx + + + com.atlassian.paddle + atlassian-paddle + - alt - alt-0.07 + org.aspectj + aspectjweaver - alt - alt + net.sf.ldaptemplate + ldaptemplate - exml - exml + opensymphony + sitemesh - rome - rome + velocity-tools + velocity-tools - jdom - jdom + radeox + radeox - commons-jrcs - commons-jrcs + org.apache.lucene + lucene-core - commons-httpclient - commons-httpclient + org.apache.lucene + lucene-analyzers - httpunit - httpunit + org.apache.lucene + lucene-misc - nekohtml - nekohtml + org.apache.lucene + lucene-highlighter - xerces - xercesImpl + commons-digester + commons-digester - xerces - xmlParserAPIs + batik + batik-all - xmlrpc - xmlrpc + net.sourceforge.cssparser + cssparser - glue - glue + org.hibernate + jtidy - axis - axis + org.quartz-scheduler + quartz - axis - axis-jaxrpc + org.hsqldb + hsqldb - axis - axis-saaj + mockobjects + mockobjects-core - commons-discovery - commons-discovery + org.mockito + mockito-all - commons-codec - commons-codec + org.springframework + spring-mock - oro - oro + rome + rome - slide - slide + jdom + jdom - xstream - xstream + commons-jrcs + commons-jrcs - xpp3 - xpp3 + commons-httpclient + commons-httpclient - concurrent - concurrent + xerces + xercesImpl - bsf - bsf + xmlrpc + xmlrpc - jfree - jfreechart + glue + glue - xalan - xalan + exml + exml - jcaptcha - jcaptcha + axis + axis - tangosol-coherence - tangosol + axis + axis-jaxrpc - tangosol-coherence - coherence + axis + axis-saaj - net.sourceforge.jtds - jtds + commons-discovery + commons-discovery - net.java.dev.urlrewrite - urlrewrite + commons-codec + commons-codec - commons-collections - commons-collections + oro + oro - net.sf.ehcache - ehcache + slide + slide - stax - stax-api + com.thoughtworks.xstream + xstream - woodstox - wstx-asl + jfree + jfreechart - org.codehaus.xfire - xfire-all + xalan + xalan - junit - junit + com.octo.captcha + jcaptcha-all - org.springframework - spring-aop + net.java.dev.urlrewrite + urlrewrite - org.springframework - spring-core + commons-collections + commons-collections - org.springframework - spring-web + javax.xml.stream + stax-api - org.springframework - spring-beans + org.codehaus.woodstox + wstx-asl + + + org.codehaus.xfire + xfire-core + + + org.codehaus.xfire + xfire-aegis + + + net.jcip + jcip-annotations + + + com.atlassian.cache + atlassian-cache-api + + + com.atlassian.sal + sal-spi - org.springframework - spring-context + com.atlassian.sal + sal-spring - org.springframework - spring-support + com.atlassian.sal + sal-api - aopalliance - aopalliance + com.atlassian.cache + atlassian-cache-memory - spring - spring-aopalliance + net.sourceforge.findbugs + jsr305 - pell - pell + opensymphony + pell-multipart - bouncycastle - bcprov-jdk14 + ch.qos.logback + logback-classic - - + + - + + com.atlassian.jira + jira-core + 4.4 + provided + jar + + com.atlassian.jira - jira-core - 4.4 - provided - jar - - - jta - jta - - - jndi - jndi - - - - - org.jasig.cas.client - cas-client-core - ${project.version} - compile - - - - - - - - atlassian - Atlassian Repository - http://repository.atlassian.com/maven2/ - - + jira-api + + + com.atlassian.multitenant + multitenant-core + + + com.atlassian.multitenant + multitenant-utils + + + com.atlassian.crowd + atlassian-embedded-crowd-ofbiz + + + com.atlassian.crowd + embedded-crowd-core + + + com.atlassian.crowd + embedded-crowd-spi + + + com.atlassian.crowd + crowd-api + + + com.atlassian.crowd + crowd-core + + + com.atlassian.crowd + crowd-integration-api + + + com.atlassian.crowd + crowd-integration-client-rest + + + com.atlassian.crowd + crowd-persistence + + + com.atlassian.crowd + crowd-events + + + com.atlassian.crowd + crowd-ldap + + + com.atlassian.crowd + crowd-remote + + + com.atlassian.crowd + crowd-server-common + + + com.atlassian.crowd + crowd-password-encoders + + + com.atlassian.security + atlassian-password-encoder + + + org.springframework.security + spring-security-core + + + org.springframework + spring-core + + + org.springframework + spring-beans + + + org.springframework.ldap + spring-ldap-core + + + org.springframework + spring-tx + + + com.atlassian.crowd + crowd-integration-client + + + wsdl4j + wsdl4j + + + org.codehaus.xfire + xfire-core + + + org.codehaus.xfire + xfire-aegis + + + sal-spi + com.atlassian.sal + + + com.atlassian.sal + sal-core + + + com.atlassian.gadgets + atlassian-gadgets-api + + + com.atlassian.gadgets + atlassian-gadgets-spi + + + com.atlassian.oauth + atlassian-oauth-api + + + com.atlassian.p4package + atlassian-p4package + + + com.atlassian.extras + atlassian-extras + + + com.atlassian.cache + atlassian-cache-api + + + com.atlassian.cache + atlassian-cache-memory + + + joda-time + joda-time + + + com.sun + jai_core + + + com.sun + jai_codec + + + commons-lang + commons-lang + + + commons-collections + commons-collections + + + commons-io + commons-io + + + com.atlassian.activeobjects + activeobjects-spi + + + log4j + log4j + + + dom4j + dom4j + + + oro + oro + + + com.atlassian.util.concurrent + atlassian-util-concurrent + + + com.atlassian.profiling + atlassian-profiling + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-log4j12 + + + org.slf4j + jul-to-slf4j + + + org.slf4j + jcl-over-slf4j + + + com.atlassian.jdk.utilities + atlassian-jdk-utilities + + + com.atlassian.scheduler + atlassian-scheduler + + + com.atlassian.johnson + atlassian-johnson + + + com.atlassian.plugins + atlassian-plugins-core + + + com.atlassian.plugins + atlassian-plugins-servlet + + + com.atlassian.plugins + atlassian-plugins-webfragment + + + com.atlassian.plugins + atlassian-plugins-webresource + + + com.atlassian.plugins + atlassian-plugins-osgi + + + com.atlassian.plugins + atlassian-plugins-osgi-events + + + com.atlassian.seraph + atlassian-seraph + + + com.atlassian.security + atlassian-secure-random + + + com.atlassian.security.auth.trustedapps + atlassian-trusted-apps-core + + + com.atlassian.security.auth.trustedapps + atlassian-trusted-apps-seraph-integration + + + bouncycastle + bcprov-jdk15 + + + com.atlassian.renderer + atlassian-renderer + + + com.atlassian.gzipfilter + atlassian-gzipfilter + + + com.atlassian.event + atlassian-event + + + opensymphony + oscore + + + opensymphony + sitemesh + + + commons-digester + commons-digester + + + commons-beanutils + commons-beanutils + + + commons-configuration + commons-configuration + + + org.apache.lucene + lucene-analyzers + + + velocity + velocity + + + org.apache.velocity + velocity-tools + + + javax.activation + activation + + + javax.mail + mail + + + glue + glue + + + bsf + bsf + + + bsh + bsh + + + com.atlassian.ofbiz + entityengine-share + + + com.atlassian.ofbiz + entityengine + + + csv + csv + + + quartz + quartz + + + picocontainer + picocontainer + + + jzlib + jzlib + + + jsch + jsch + + + com.octo.captcha + jcaptcha + + + com.octo.captcha + jcaptcha-api + + + com.jhlabs + filters + + + javacvs + javacvs + + + statcvs + statcvs + + + commons-dbcp + commons-dbcp + + + commons-pool + commons-pool + + + hsqldb + hsqldb + + + jndi + jndi + + + jta + jta + + + ots-jts + ots-jts + + + jotm + jotm + + + jotm + jotm-jrmp_stubs + + + jotm + jotm-iiop_stubs + + + jotm + jonas_timer + + + jotm + objectweb-datasource + + + carol + carol + + + carol + carol-properties + + + xapool + xapool + + + xml-apis + xml-apis + + + saxon + saxon-noaelfred + + + commons-jelly + commons-jelly + + + commons-jelly + commons-jelly-tags-junit + + + commons-jelly + commons-jelly-tags-util + + + commons-jelly + commons-jelly-tags-email + + + commons-jelly + commons-jelly-tags-log + + + commons-jelly + commons-jelly-tags-http + + + commons-jelly + commons-jelly-tags-soap + + + commons-jelly + commons-jelly-tags-sql + + + commons-jelly + commons-jelly-tags-regexp + + + commons-jexl + commons-jexl + + + commons-httpclient + commons-httpclient + + + commons-codec + commons-codec + + + xmlrpc + xmlrpc + + + axis + axis + + + axis + axis-jaxrpc + + + axis + axis-saaj + + + commons-discovery + commons-discovery + + + xerces + xercesImpl + + + xalan + xalan + + + xml-security + xmlsec + + + datafile + datafile + + + xpp3 + xpp3 + + + com.thoughtworks.xstream + xstream + + + jfree + jfreechart + + + jfree + jcommon + + + net.java.dev.urlrewrite + urlrewrite + + + radeox + radeox + + + jtidy + jtidy + + + jdom + jdom + + + net.sf.ehcache + ehcache + + + org.codehaus.woodstox + wstx-asl + + + javax.servlet + servlet-api + + + javax.servlet + jsp-api + + + com.atlassian.modzdetector + modz-detector + + + org.antlr + antlr-runtime + + + commons-jrcs + commons-jrcs + + + com.google.collections + google-collections + + + com.atlassian.jira + jira-lang-ca_ES + + + com.atlassian.jira + jira-lang-cs_CZ + + + com.atlassian.jira + jira-lang-da_DK + + + com.atlassian.jira + jira-lang-de_CH + + + com.atlassian.jira + jira-lang-de_DE + + + com.atlassian.jira + jira-lang-en_UK + + + com.atlassian.jira + jira-lang-en_US + + + com.atlassian.jira + jira-lang-es_ES + + + com.atlassian.jira + jira-lang-fr_FR + + + com.atlassian.jira + jira-lang-hu_HU + + + com.atlassian.jira + jira-lang-it_IT + + + com.atlassian.jira + jira-lang-ja_JP + + + com.atlassian.jira + jira-lang-nl_BE + + + com.atlassian.jira + jira-lang-no_NO + + + com.atlassian.jira + jira-lang-pl_PL + + + com.atlassian.jira + jira-lang-pt_BR + + + com.atlassian.jira + jira-lang-ru_RU + + + com.atlassian.jira + jira-lang-sk_SK + + + com.atlassian.jira + jira-lang-tr_TR + + + com.atlassian.jira + jira-lang-zh_CN + + + com.atlassian.jira + jira-lang-zh_TW + + + com.google.code.findbugs + jsr305 + + + com.google.code.findbugs + annotations + + + cglib + cglib-nodep + + + com.atlassian.sal + sal-api + + + com.atlassian.applinks + applinks-api + + + com.atlassian.applinks + applinks-spi + + + com.atlassian.applinks + applinks-host + + + rhino + js + + + + + org.jasig.cas.client + cas-client-core + ${project.version} + compile + + + + + atlassian + Atlassian Repository + https://maven.atlassian.com/repository/public + + diff --git a/cas-client-integration-atlassian/src/main/java/org/jasig/cas/client/integration/atlassian/Confluence35CasAuthenticator.java b/cas-client-integration-atlassian/src/main/java/org/jasig/cas/client/integration/atlassian/Confluence35CasAuthenticator.java new file mode 100644 index 000000000..0740121bd --- /dev/null +++ b/cas-client-integration-atlassian/src/main/java/org/jasig/cas/client/integration/atlassian/Confluence35CasAuthenticator.java @@ -0,0 +1,103 @@ +/* + * 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.integration.atlassian; + +import com.atlassian.confluence.event.events.security.LoginEvent; +import com.atlassian.confluence.event.events.security.LoginFailedEvent; +import com.atlassian.confluence.user.ConfluenceAuthenticator; +import com.atlassian.seraph.auth.AuthenticatorException; +import com.atlassian.seraph.auth.LoginReason; +import java.security.Principal; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.jasig.cas.client.util.AbstractCasFilter; +import org.jasig.cas.client.validation.Assertion; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Extension of ConfluenceAuthenticator to allow people to configure Confluence 3.5+ to authenticate + * via CAS. + * + * Based on https://bitbucket.org/jaysee00/example-confluence-sso-authenticator + * + * @author Scott Battaglia + * @author John Watson + * @author Jozef Kotlar + * @version $Revision$ $Date$ + * @since 3.3.0 + */ +public final class Confluence35CasAuthenticator extends ConfluenceAuthenticator { + private static final long serialVersionUID = -6097438206488390678L; + + private static final Logger LOGGER = LoggerFactory.getLogger(Confluence35CasAuthenticator.class); + + public Principal getUser(final HttpServletRequest request, final HttpServletResponse response) { + Principal existingUser = getUserFromSession(request); + if (existingUser != null) { + LOGGER.debug("Session found; user already logged in."); + LoginReason.OK.stampRequestResponse(request, response); + return existingUser; + } + + final HttpSession session = request.getSession(); + final Assertion assertion = (Assertion) session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION); + + if (assertion != null) { + final String username = assertion.getPrincipal().getName(); + final Principal user = getUser(username); + final String remoteIP = request.getRemoteAddr(); + final String remoteHost = request.getRemoteHost(); + + if (user != null) { + putPrincipalInSessionContext(request, user); + getElevatedSecurityGuard().onSuccessfulLoginAttempt(request, username); + // Firing this event is necessary to ensure the user's personal information is initialised correctly. + getEventPublisher().publish( + new LoginEvent(this, username, request.getSession().getId(), remoteHost, remoteIP)); + LoginReason.OK.stampRequestResponse(request, response); + LOGGER.debug("Logging in [{}] from CAS.", username); + } else { + LOGGER.debug("Failed logging [{}] from CAS.", username); + getElevatedSecurityGuard().onFailedLoginAttempt(request, username); + getEventPublisher().publish( + new LoginFailedEvent(this, username, request.getSession().getId(), remoteHost, remoteIP)); + } + return user; + } + + return super.getUser(request, response); + } + + public boolean logout(final HttpServletRequest request, final HttpServletResponse response) + throws AuthenticatorException { + final HttpSession session = request.getSession(); + + final Principal principal = (Principal) session.getAttribute(LOGGED_IN_KEY); + + if (principal != null) { + LOGGER.debug("Logging out [{}] from CAS.", principal.getName()); + } + + removePrincipalFromSessionContext(request); + session.setAttribute(AbstractCasFilter.CONST_CAS_ASSERTION, null); + return true; + } +} diff --git a/cas-client-integration-atlassian/src/main/java/org/jasig/cas/client/integration/atlassian/ConfluenceCasAuthenticator.java b/cas-client-integration-atlassian/src/main/java/org/jasig/cas/client/integration/atlassian/ConfluenceCasAuthenticator.java index 3f88bea79..a705ede56 100644 --- a/cas-client-integration-atlassian/src/main/java/org/jasig/cas/client/integration/atlassian/ConfluenceCasAuthenticator.java +++ b/cas-client-integration-atlassian/src/main/java/org/jasig/cas/client/integration/atlassian/ConfluenceCasAuthenticator.java @@ -1,35 +1,33 @@ -/** +/* * 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: + * 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 + * 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 + * 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.integration.atlassian; import com.atlassian.confluence.user.ConfluenceAuthenticator; import com.atlassian.seraph.auth.AuthenticatorException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.jasig.cas.client.util.AbstractCasFilter; -import org.jasig.cas.client.validation.Assertion; - +import java.security.Principal; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import java.security.Principal; +import org.jasig.cas.client.util.AbstractCasFilter; +import org.jasig.cas.client.validation.Assertion; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Extension of ConfluenceAuthenticator to allow people to configure Confluence to authenticate @@ -45,16 +43,14 @@ public final class ConfluenceCasAuthenticator extends ConfluenceAuthenticator { /** ConfluenceCasAuthenticator.java */ private static final long serialVersionUID = -6097438206488390677L; - private static final Log LOG = LogFactory.getLog(ConfluenceCasAuthenticator.class); + private static final Logger LOGGER = LoggerFactory.getLogger(ConfluenceCasAuthenticator.class); public Principal getUser(final HttpServletRequest request, final HttpServletResponse response) { final HttpSession session = request.getSession(); // user already exists if (session.getAttribute(LOGGED_IN_KEY) != null) { - if (LOG.isDebugEnabled()) { - LOG.debug("Session found; user already logged in."); - } + LOGGER.debug("Session found; user already logged in."); return (Principal) session.getAttribute(LOGGED_IN_KEY); } @@ -63,9 +59,7 @@ public Principal getUser(final HttpServletRequest request, final HttpServletResp if (assertion != null) { final Principal p = getUser(assertion.getPrincipal().getName()); - if (LOG.isDebugEnabled()) { - LOG.debug("Logging in [" + p.getName() + "] from CAS."); - } + LOGGER.debug("Logging in [{}] from CAS.", p.getName()); session.setAttribute(LOGGED_IN_KEY, p); session.setAttribute(LOGGED_OUT_KEY, null); @@ -75,18 +69,17 @@ public Principal getUser(final HttpServletRequest request, final HttpServletResp return super.getUser(request, response); } - public boolean logout(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticatorException { + public boolean logout(final HttpServletRequest request, final HttpServletResponse response) + throws AuthenticatorException { final HttpSession session = request.getSession(); final Principal principal = (Principal) session.getAttribute(LOGGED_IN_KEY); - if (LOG.isDebugEnabled()) { - LOG.debug("Logging out [" + principal.getName() + "] from CAS."); - } + LOGGER.debug("Logging out [{}] from CAS.", principal.getName()); session.setAttribute(LOGGED_OUT_KEY, principal); session.setAttribute(LOGGED_IN_KEY, null); session.setAttribute(AbstractCasFilter.CONST_CAS_ASSERTION, null); return true; } -} \ No newline at end of file +} diff --git a/cas-client-integration-atlassian/src/main/java/org/jasig/cas/client/integration/atlassian/Jira44CasAuthenticator.java b/cas-client-integration-atlassian/src/main/java/org/jasig/cas/client/integration/atlassian/Jira44CasAuthenticator.java index 493aa617d..999dc8f5f 100644 --- a/cas-client-integration-atlassian/src/main/java/org/jasig/cas/client/integration/atlassian/Jira44CasAuthenticator.java +++ b/cas-client-integration-atlassian/src/main/java/org/jasig/cas/client/integration/atlassian/Jira44CasAuthenticator.java @@ -1,42 +1,42 @@ -/** +/* * 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: + * 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 + * 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 + * 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.integration.atlassian; import com.atlassian.jira.security.login.JiraSeraphAuthenticator; import com.atlassian.seraph.auth.AuthenticatorException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.jasig.cas.client.util.AbstractCasFilter; -import org.jasig.cas.client.validation.Assertion; - +import com.atlassian.seraph.auth.LoginReason; +import java.security.Principal; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import java.security.Principal; +import org.jasig.cas.client.util.AbstractCasFilter; +import org.jasig.cas.client.validation.Assertion; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Extension of JiraSeraphAuthenticator to allow people to configure * JIRA 4.4 and above to authenticate via Jasig CAS * * @author Scott Battaglia - * @author Martin Stiborsk� + * @author Martin Stiborsky + * @author Jozef Kotlar * @version $Revision$ $Date$ * @since 3.3.0 */ @@ -45,47 +45,48 @@ public final class Jira44CasAuthenticator extends JiraSeraphAuthenticator { /** Jira43CasAuthenticator.java */ private static final long serialVersionUID = 3852011252741183166L; - private static final Log LOG = LogFactory.getLog(Jira44CasAuthenticator.class); + private static final Logger LOGGER = LoggerFactory.getLogger(Jira44CasAuthenticator.class); public Principal getUser(final HttpServletRequest request, final HttpServletResponse response) { - final HttpSession session = request.getSession(); - - // user already exists - if (session.getAttribute(LOGGED_IN_KEY) != null) { - if (LOG.isDebugEnabled()) { - LOG.debug("Session found; user already logged in."); - } - return (Principal) session.getAttribute(LOGGED_IN_KEY); + // First, check to see if this session has already been authenticated during a previous request. + Principal existingUser = getUserFromSession(request); + if (existingUser != null) { + LOGGER.debug("Session found; user already logged in."); } + final HttpSession session = request.getSession(); final Assertion assertion = (Assertion) session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION); if (assertion != null) { - final Principal p = getUser(assertion.getPrincipal().getName()); - - if (LOG.isDebugEnabled()) { - LOG.debug("Logging in [" + p.getName() + "] from CAS."); + final String username = assertion.getPrincipal().getName(); + final Principal user = getUser(username); + + if (user != null) { + putPrincipalInSessionContext(request, user); + getElevatedSecurityGuard().onSuccessfulLoginAttempt(request, username); + LoginReason.OK.stampRequestResponse(request, response); + LOGGER.debug("Logging in [{}] from CAS.", username); + } else { + LOGGER.debug("Failed logging [{}] from CAS.", username); + getElevatedSecurityGuard().onFailedLoginAttempt(request, username); } - - session.setAttribute(LOGGED_IN_KEY, p); - session.setAttribute(LOGGED_OUT_KEY, null); - return p; + return user; } return super.getUser(request, response); } - public boolean logout(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticatorException { + public boolean logout(final HttpServletRequest request, final HttpServletResponse response) + throws AuthenticatorException { final HttpSession session = request.getSession(); final Principal p = (Principal) session.getAttribute(LOGGED_IN_KEY); - if (LOG.isDebugEnabled()) { - LOG.debug("Logging out [" + p.getName() + "] from CAS."); + if (p != null) { + LOGGER.debug("Logging out [{}] from CAS.", p.getName()); } - session.setAttribute(LOGGED_OUT_KEY, p); - session.setAttribute(LOGGED_IN_KEY, null); + removePrincipalFromSessionContext(request); session.setAttribute(AbstractCasFilter.CONST_CAS_ASSERTION, null); return true; } -} \ No newline at end of file +} diff --git a/cas-client-integration-atlassian/src/main/java/org/jasig/cas/client/integration/atlassian/JiraCasAuthenticator.java b/cas-client-integration-atlassian/src/main/java/org/jasig/cas/client/integration/atlassian/JiraCasAuthenticator.java index aeb48f11a..a34c49e16 100644 --- a/cas-client-integration-atlassian/src/main/java/org/jasig/cas/client/integration/atlassian/JiraCasAuthenticator.java +++ b/cas-client-integration-atlassian/src/main/java/org/jasig/cas/client/integration/atlassian/JiraCasAuthenticator.java @@ -1,38 +1,35 @@ -/** +/* * 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: + * 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 + * 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 + * 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.integration.atlassian; -import com.atlassian.seraph.auth.DefaultAuthenticator; import com.atlassian.seraph.auth.AuthenticatorException; +import com.atlassian.seraph.auth.DefaultAuthenticator; import com.opensymphony.user.EntityNotFoundException; -import com.opensymphony.user.User; import com.opensymphony.user.UserManager; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.jasig.cas.client.util.AbstractCasFilter; -import org.jasig.cas.client.validation.Assertion; - +import java.security.Principal; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import java.security.Principal; +import org.jasig.cas.client.util.AbstractCasFilter; +import org.jasig.cas.client.validation.Assertion; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Extension of ConfluenceAuthenticator to allow people to configure Confluence to authenticate @@ -48,7 +45,7 @@ public final class JiraCasAuthenticator extends DefaultAuthenticator { /** JiraCasAuthenticator.java */ private static final long serialVersionUID = 3452011252741183166L; - private static final Log LOG = LogFactory.getLog(JiraCasAuthenticator.class); + private static final Logger LOGGER = LoggerFactory.getLogger(JiraCasAuthenticator.class); @Override protected boolean authenticate(final Principal principal, final String password) throws AuthenticatorException { @@ -60,7 +57,7 @@ protected Principal getUser(final String username) { try { return UserManager.getInstance().getUser(username); } catch (final EntityNotFoundException e) { - LOG.warn("Could not find user '" + username + "' in UserManager : " + e); + LOGGER.warn("Could not find user '{}' in UserManager : {}", username, e); } return null; } @@ -70,9 +67,7 @@ public Principal getUser(final HttpServletRequest request, final HttpServletResp // user already exists if (session.getAttribute(LOGGED_IN_KEY) != null) { - if (LOG.isDebugEnabled()) { - LOG.debug("Session found; user already logged in."); - } + LOGGER.debug("Session found; user already logged in."); return (Principal) session.getAttribute(LOGGED_IN_KEY); } @@ -81,9 +76,7 @@ public Principal getUser(final HttpServletRequest request, final HttpServletResp if (assertion != null) { final Principal p = getUser(assertion.getPrincipal().getName()); - if (LOG.isDebugEnabled()) { - LOG.debug("Logging in [" + p.getName() + "] from CAS."); - } + LOGGER.debug("Logging in [{}] from CAS.", p.getName()); session.setAttribute(LOGGED_IN_KEY, p); session.setAttribute(LOGGED_OUT_KEY, null); @@ -93,17 +86,16 @@ public Principal getUser(final HttpServletRequest request, final HttpServletResp return super.getUser(request, response); } - public boolean logout(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticatorException { + public boolean logout(final HttpServletRequest request, final HttpServletResponse response) + throws AuthenticatorException { final HttpSession session = request.getSession(); final Principal p = (Principal) session.getAttribute(LOGGED_IN_KEY); - if (LOG.isDebugEnabled()) { - LOG.debug("Logging out [" + p.getName() + "] from CAS."); - } + LOGGER.debug("Logging out [{}] from CAS.", p.getName()); session.setAttribute(LOGGED_OUT_KEY, p); session.setAttribute(LOGGED_IN_KEY, null); session.setAttribute(AbstractCasFilter.CONST_CAS_ASSERTION, null); return true; } -} \ No newline at end of file +} diff --git a/cas-client-integration-jboss/NOTICE b/cas-client-integration-jboss/NOTICE new file mode 100644 index 000000000..93718dd71 --- /dev/null +++ b/cas-client-integration-jboss/NOTICE @@ -0,0 +1,27 @@ +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. + +This project includes: + Jasig CAS Client for Java - Core under Apache License Version 2.0 + 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 + JCL 1.1.1 implemented over SLF4J under MIT License + JUnit under Common Public License Version 1.0 + SLF4J API Module under MIT License + SLF4J Simple Binding under MIT License + diff --git a/cas-client-integration-jboss/pom.xml b/cas-client-integration-jboss/pom.xml index b631df1be..c0113c27b 100644 --- a/cas-client-integration-jboss/pom.xml +++ b/cas-client-integration-jboss/pom.xml @@ -1,67 +1,244 @@ - - + + + org.jasig.cas.client + 3.4.1-SNAPSHOT + cas-client + + 4.0.0 org.jasig.cas.client - 3.3-SNAPSHOT - cas-client - - 4.0.0 - org.jasig.cas.client - cas-client-integration-jboss - jar - Jasig CAS Client for Java - JBoss Integration + cas-client-integration-jboss + jar + Jasig CAS Client for Java - JBoss Integration - - - org.jasig.cas.client - cas-client-core - ${project.version} - compile - + + + org.jasig.cas.client + cas-client-core + ${project.version} + compile + - - + + + org.jboss.jbossas + jboss-as-tomcat + ${jboss.version} + provided + + + xml-apis + xml-apis + + + org.jboss.spec.javax.servlet.jstl + jboss-jstl-api_1.2_spec + + + jacorb + jacorb + + + org.jboss.logging + jboss-logging + + + org.picketbox + jboss-security-spi + + + jboss + jboss-serialization + + + org.jboss.integration + jboss-deployment-spi + + + org.picketbox + jbosssx-client + + + org.jboss.web + jbossweb + + + org.jboss.web + jasper-jdt + + + junit + junit + + + log4j + log4j + + + org.jboss + jboss-common-core + + + org.jboss.metadata + jboss-metadata-common + + + org.jboss.metadata + jboss-metadata-war + + + org.jboss + jboss-vfs + + + org.jboss.ws + jbossws-spi + + + org.jboss + jbossxb + + + org.jboss.aop + jboss-aop + + + org.jboss.spec.javax.security.auth.message + jboss-jaspi-api_1.0_spec + + org.jboss.jbossas - jboss-as-tomcat - ${jboss.version} - provided - - - trove - trove - - - apache-log4j - log4j - - - jpl-pattern - jpl-pattern - - - jpl-util - jpl-util - - - javax.security - jacc - - - - - - - - - jboss - JBoss Repository - https://repository.jboss.org/nexus/content/groups/public// - - + jboss-as-connector + + + org.jboss.jbossas + jboss-as-ejb3 + + + org.jboss.ejb3 + jboss-ejb3-core + + + org.hibernate.javax.persistence + hibernate-jpa-2.0-api + + + org.jboss.cluster + jboss-ha-server-cache-spi + + + org.jboss.jbossas + jboss-as-iiop + + + org.jboss.jbossas + jboss-as-security + + + org.jboss.spec.javax.security.jacc + jboss-jacc-api_1.4_spec + + + org.jboss.spec.javax.resource + jboss-connector-api_1.5_spec + + + org.jboss.spec.javax.ejb + jboss-ejb-api_3.1_spec + + + org.jboss.spec.javax.servlet + jboss-servlet-api_3.0_spec + + + org.jboss.spec.javax.servlet.jsp + jboss-jsp-api_2.2_spec + + + org.jboss.spec.javax.el + jboss-el-api_2.2_spec + + + org.jboss.spec.javax.xml.ws + jboss-jaxws-api_2.2_spec + + + org.jboss.mx + jboss-j2se + + + org.jboss.jbossas + jboss-as-server + + + org.jboss.jbossas + jboss-as-system-jmx + + + org.jboss.cl + jboss-classloader + + + org.jboss.deployers + jboss-deployers-core-spi + + + org.jboss.deployers + jboss-deployers-impl + + + org.jboss.deployers + jboss-deployers-spi + + + org.jboss.deployers + jboss-deployers-structure-spi + + + org.jboss.deployers + jboss-deployers-vfs + + + org.jboss.deployers + jboss-deployers-vfs-spi + + + oswego-concurrent + concurrent + + + org.slf4j + jcl-over-slf4j + + + net.jcip + jcip-annotations + + + javax.validation + validation-api + + + org.jboss.mc-int + jboss-mc-int-servlet + + + org.jboss.mod_cluster + mod_cluster + + + + - - 6.1.0.Final - + + + + jboss + JBoss Repository + https://repository.jboss.org/nexus/content/groups/public// + + + + 6.1.0.Final + diff --git a/cas-client-integration-jboss/src/main/java/org/jasig/cas/client/configuration/JBossCompatibleJndiConfigurationStrategyImpl.java b/cas-client-integration-jboss/src/main/java/org/jasig/cas/client/configuration/JBossCompatibleJndiConfigurationStrategyImpl.java new file mode 100644 index 000000000..1675f48c0 --- /dev/null +++ b/cas-client-integration-jboss/src/main/java/org/jasig/cas/client/configuration/JBossCompatibleJndiConfigurationStrategyImpl.java @@ -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); + } +} diff --git a/cas-client-integration-jboss/src/main/java/org/jasig/cas/client/jboss/authentication/WebAuthenticationFilter.java b/cas-client-integration-jboss/src/main/java/org/jasig/cas/client/jboss/authentication/WebAuthenticationFilter.java index 9eefd67ae..597d4950d 100644 --- a/cas-client-integration-jboss/src/main/java/org/jasig/cas/client/jboss/authentication/WebAuthenticationFilter.java +++ b/cas-client-integration-jboss/src/main/java/org/jasig/cas/client/jboss/authentication/WebAuthenticationFilter.java @@ -1,27 +1,25 @@ -/** +/* * 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: + * 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 + * 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 + * 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.jboss.authentication; import java.io.IOException; import java.security.GeneralSecurityException; - import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; @@ -30,10 +28,10 @@ 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; - import org.jboss.web.tomcat.security.login.WebAuthentication; /** @@ -54,27 +52,33 @@ */ public final class WebAuthenticationFilter extends AbstractCasFilter { - public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain chain) throws IOException, ServletException { + 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 { final String service = constructServiceUrl(request, response); - log.debug("Attempting CAS ticket validation with service=" + service + " and ticket=" + ticket); + logger.debug("Attempting CAS ticket validation with service={} and ticket={}", service, ticket); if (!new WebAuthentication().login(service, ticket)) { - log.debug("JBoss Web authentication failed."); + logger.debug("JBoss Web authentication failed."); throw new GeneralSecurityException("JBoss Web authentication failed."); } if (request.getUserPrincipal() instanceof AssertionPrincipal) { final AssertionPrincipal principal = (AssertionPrincipal) request.getUserPrincipal(); - log.debug("Installing CAS assertion into session."); + logger.debug("Installing CAS assertion into session."); request.getSession().setAttribute(CONST_CAS_ASSERTION, principal.getAssertion()); } else { - log.debug("Aborting -- principal is not of type AssertionPrincipal"); - throw new GeneralSecurityException("JBoss Web authentication did not produce CAS AssertionPrincipal."); + logger.debug("Aborting -- principal is not of type AssertionPrincipal"); + throw new GeneralSecurityException( + "JBoss Web authentication did not produce CAS AssertionPrincipal."); } } catch (final GeneralSecurityException e) { response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); @@ -83,7 +87,7 @@ public void doFilter(final ServletRequest servletRequest, final ServletResponse // There is evidence that in some cases the principal can disappear // in JBoss despite a valid session. // This block forces consistency between principal and assertion. - log.info("User principal not found. Removing CAS assertion from session to force re-authentication."); + logger.info("User principal not found. Removing CAS assertion from session to force re-authentication."); session.removeAttribute(CONST_CAS_ASSERTION); } chain.doFilter(request, response); diff --git a/cas-client-integration-tomcat-common/NOTICE b/cas-client-integration-tomcat-common/NOTICE new file mode 100644 index 000000000..aa5bf99c7 --- /dev/null +++ b/cas-client-integration-tomcat-common/NOTICE @@ -0,0 +1,26 @@ +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. + +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 + JCL 1.1.1 implemented over SLF4J under MIT License + JUnit under Common Public License Version 1.0 + SLF4J API Module under MIT License + SLF4J Simple Binding under MIT License + diff --git a/cas-client-integration-tomcat-common/pom.xml b/cas-client-integration-tomcat-common/pom.xml index 1e2aad134..4a688a8f7 100644 --- a/cas-client-integration-tomcat-common/pom.xml +++ b/cas-client-integration-tomcat-common/pom.xml @@ -1,27 +1,25 @@ - - - cas-client - org.jasig.cas.client - 3.3-SNAPSHOT - - 4.0.0 + + + cas-client + org.jasig.cas.client + 3.4.1-SNAPSHOT + + 4.0.0 - org.jasig.cas.client - cas-client-integration-tomcat-common - jar - Jasig CAS Client for Java - Common Tomcat Integration Support + org.jasig.cas.client + cas-client-integration-tomcat-common + jar + Jasig CAS Client for Java - Common Tomcat Integration Support - - - org.jasig.cas.client - cas-client-core - ${project.version} - jar - compile - - + + + org.jasig.cas.client + cas-client-core + ${project.version} + jar + compile + + diff --git a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/AbstractLogoutHandler.java b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/AbstractLogoutHandler.java index bc8660600..37af2182a 100644 --- a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/AbstractLogoutHandler.java +++ b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/AbstractLogoutHandler.java @@ -1,33 +1,31 @@ -/** +/* * 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: + * 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 + * 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 + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ - package org.jasig.cas.client.tomcat; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.jasig.cas.client.util.AbstractCasFilter; import org.jasig.cas.client.util.CommonUtils; import org.jasig.cas.client.validation.Assertion; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Base class for all logout handlers. @@ -38,7 +36,7 @@ */ public abstract class AbstractLogoutHandler implements LogoutHandler { - protected final Log log = LogFactory.getLog(getClass()); + protected final Logger logger = LoggerFactory.getLogger(getClass()); protected String redirectUrl; @@ -48,20 +46,21 @@ public void setRedirectUrl(final String redirectUrl) { /** {@inheritDoc} */ public void logout(final HttpServletRequest request, final HttpServletResponse response) { - log.debug("Processing logout request from CAS server."); + logger.debug("Processing logout request from CAS server."); final Assertion assertion; final HttpSession httpSession = request.getSession(false); - if (httpSession != null && (assertion = (Assertion) httpSession.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION)) != null) { + if (httpSession != null + && (assertion = (Assertion) httpSession.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION)) != null) { httpSession.removeAttribute(AbstractCasFilter.CONST_CAS_ASSERTION); - log.info("Successfully logged out " + assertion.getPrincipal()); + logger.info("Successfully logged out {}", assertion.getPrincipal()); } else { - log.info("Session already ended."); + logger.info("Session already ended."); } final String redirectUrl = constructRedirectUrl(request); if (redirectUrl != null) { - log.debug("Redirecting to " + redirectUrl); + logger.debug("Redirecting to {}", redirectUrl); CommonUtils.sendRedirect(response, redirectUrl); } } diff --git a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/AssertionCasRealmDelegate.java b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/AssertionCasRealmDelegate.java index bdd1d5636..f2cce310f 100644 --- a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/AssertionCasRealmDelegate.java +++ b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/AssertionCasRealmDelegate.java @@ -1,29 +1,27 @@ -/** +/* * 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: + * 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 + * 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 + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ - package org.jasig.cas.client.tomcat; import java.security.Principal; import java.util.Arrays; import java.util.Collection; import java.util.Collections; - import org.jasig.cas.client.authentication.AttributePrincipal; import org.jasig.cas.client.util.CommonUtils; @@ -48,7 +46,6 @@ public class AssertionCasRealmDelegate implements CasRealm { /** Name of the role attribute in the principal's attributes */ private String roleAttributeName = DEFAULT_ROLE_NAME; - /** * @param name Name of the attribute in the principal that contains role data. */ @@ -63,7 +60,8 @@ public Principal authenticate(final Principal p) { /** {@inheritDoc} */ public String[] getRoles(final Principal p) { - CommonUtils.assertTrue(p instanceof AttributePrincipal, "Expected instance of AttributePrincipal but got " + p.getClass()); + CommonUtils.assertTrue(p instanceof AttributePrincipal, + "Expected instance of AttributePrincipal but got " + p.getClass()); final Collection roles = getRoleCollection(p); final String[] array = new String[roles.size()]; @@ -73,6 +71,9 @@ public String[] getRoles(final Principal p) { /** {@inheritDoc} */ public boolean hasRole(final Principal principal, final String role) { + if ("*".equals(role)) { + return true; + } return getRoleCollection(principal).contains(role); } diff --git a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/AuthenticatorDelegate.java b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/AuthenticatorDelegate.java index 3b2660144..9fae1a685 100644 --- a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/AuthenticatorDelegate.java +++ b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/AuthenticatorDelegate.java @@ -1,38 +1,35 @@ -/** +/* * 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: + * 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 + * 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 + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ - package org.jasig.cas.client.tomcat; import java.io.IOException; import java.security.Principal; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.jasig.cas.client.util.AbstractCasFilter; import org.jasig.cas.client.util.CommonUtils; import org.jasig.cas.client.validation.Assertion; import org.jasig.cas.client.validation.TicketValidationException; import org.jasig.cas.client.validation.TicketValidator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Version-agnostic authenticator which encapsulates the core CAS workflow of @@ -49,20 +46,20 @@ public final class AuthenticatorDelegate { /** Log instance */ - private final Log log = LogFactory.getLog(getClass()); + private final Logger logger = LoggerFactory.getLogger(getClass()); private String serviceUrl; - + private String serverName; - + private String casServerLoginUrl; private String artifactParameterName; - + private String serviceParameterName; - + private TicketValidator ticketValidator; - + private CasRealm realm; /** @@ -86,19 +83,21 @@ public final Principal authenticate(final HttpServletRequest request, final Http assertion = (Assertion) session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION); } if (assertion == null) { - log.debug("CAS assertion not found in session -- authentication required."); + logger.debug("CAS assertion not found in session -- authentication required."); final String token = request.getParameter(this.artifactParameterName); - final String service = CommonUtils.constructServiceUrl(request, response, this.serviceUrl, this.serverName, this.artifactParameterName, true); + final String service = CommonUtils.constructServiceUrl(request, response, this.serviceUrl, this.serverName, + this.serviceParameterName, this.artifactParameterName, true); if (CommonUtils.isBlank(token)) { - final String redirectUrl = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, this.serviceParameterName, service, false, false); - log.debug("Redirecting to " + redirectUrl); + final String redirectUrl = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, + this.serviceParameterName, service, false, false); + logger.debug("Redirecting to {}", redirectUrl); CommonUtils.sendRedirect(response, redirectUrl); return null; } try { - log.debug("Attempting to validate " + token + " for " + service); + logger.debug("Attempting to validate {} for {}", token, service); assertion = this.ticketValidator.validate(token, service); - log.debug("CAS authentication succeeded."); + logger.debug("CAS authentication succeeded."); if (session == null) { session = request.getSession(true); } @@ -110,7 +109,7 @@ public final Principal authenticate(final HttpServletRequest request, final Http } Principal p = realm.authenticate(assertion.getPrincipal()); if (p == null) { - log.debug(assertion.getPrincipal().getName() + " failed to authenticate to " + realm); + logger.debug("{} failed to authenticate to {}", assertion.getPrincipal().getName(), realm); setUnauthorized(response, null); } return p; @@ -189,9 +188,9 @@ public void setRealm(final CasRealm realm) { private void setUnauthorized(final HttpServletResponse response, final String message) { try { if (message != null) { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED, message); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, message); } else { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); } } catch (IOException e) { throw new IllegalStateException("Error setting 403 status.", e); diff --git a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/CasRealm.java b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/CasRealm.java index 3e1a58b26..7aff937ea 100644 --- a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/CasRealm.java +++ b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/CasRealm.java @@ -1,22 +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: + * 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 + * 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 + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ - package org.jasig.cas.client.tomcat; import java.security.Principal; @@ -47,7 +46,7 @@ public interface CasRealm { * @return Roles for given principal or empty array if none exist. */ String[] getRoles(Principal p); - + /** * Determines whether the given principal possesses the given role. * diff --git a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/LogoutHandler.java b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/LogoutHandler.java index 0bd45b805..a53502b5f 100644 --- a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/LogoutHandler.java +++ b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/LogoutHandler.java @@ -1,22 +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: + * 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 + * 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 + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ - package org.jasig.cas.client.tomcat; import javax.servlet.http.HttpServletRequest; diff --git a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/PropertiesCasRealmDelegate.java b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/PropertiesCasRealmDelegate.java index 3b135d41a..dab417dc7 100644 --- a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/PropertiesCasRealmDelegate.java +++ b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/PropertiesCasRealmDelegate.java @@ -1,22 +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: + * 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 + * 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 + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ - package org.jasig.cas.client.tomcat; import java.io.BufferedInputStream; @@ -25,10 +24,9 @@ import java.io.IOException; import java.security.Principal; import java.util.*; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.jasig.cas.client.util.CommonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * {@link CasRealm} implementation with users and roles defined by a properties @@ -46,17 +44,17 @@ * @since 3.1.12 * */ -public class PropertiesCasRealmDelegate implements CasRealm { +public class PropertiesCasRealmDelegate implements CasRealm { /** Log instance */ - private final Log log = LogFactory.getLog(getClass()); - + private final Logger logger = LoggerFactory.getLogger(getClass()); + /** Path to backing properties file */ private String propertiesFilePath; - + /** Map of usernames to roles */ private Map> roleMap; - + /** * @param path Path to properties file container username/role data. */ @@ -71,16 +69,16 @@ public void readProperties() { if (!file.isAbsolute()) { file = new File(System.getProperty("catalina.base"), propertiesFilePath); } - CommonUtils.assertTrue(file.exists(), "File not found " + file); + CommonUtils.assertTrue(file.exists(), "File not found " + file); CommonUtils.assertTrue(file.canRead(), "Cannot read " + file); - log.debug("Loading users/roles from " + file); + logger.debug("Loading users/roles from {}", file); final Properties properties = new Properties(); try { properties.load(new BufferedInputStream(new FileInputStream(file))); } catch (final IOException e) { throw new IllegalStateException("Error loading users/roles from " + file, e); } - this.roleMap = new HashMap>(properties.size()); + this.roleMap = new HashMap>(properties.size()); for (final Object key : properties.keySet()) { final String user = (String) key; @@ -109,6 +107,10 @@ public String[] getRoles(final Principal p) { /** {@inheritDoc} */ public boolean hasRole(final Principal principal, final String role) { + if ("*".equals(role)) { + return true; + } + final Set roles = this.roleMap.get(principal.getName()); return roles != null && roles.contains(role); diff --git a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/RegexUriLogoutHandler.java b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/RegexUriLogoutHandler.java index e50f75d82..f4a33b181 100644 --- a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/RegexUriLogoutHandler.java +++ b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/RegexUriLogoutHandler.java @@ -1,28 +1,25 @@ -/** +/* * 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: + * 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 + * 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 + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ - package org.jasig.cas.client.tomcat; import java.util.regex.Pattern; - import javax.servlet.http.HttpServletRequest; - import org.jasig.cas.client.util.CommonUtils; /** @@ -53,7 +50,7 @@ public void init() { CommonUtils.assertNotNull(this.logoutUriRegex, "A logout URI regular expression is required."); this.logoutUriPattern = Pattern.compile(this.logoutUriRegex); } - + /** {@inheritDoc} */ public boolean isLogoutRequest(final HttpServletRequest request) { return this.logoutUriPattern.matcher(request.getRequestURI()).matches(); diff --git a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/StaticUriLogoutHandler.java b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/StaticUriLogoutHandler.java index d4a4fdcb5..8f2a0f323 100644 --- a/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/StaticUriLogoutHandler.java +++ b/cas-client-integration-tomcat-common/src/main/java/org/jasig/cas/client/tomcat/StaticUriLogoutHandler.java @@ -1,26 +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: + * 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 + * 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 + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ - package org.jasig.cas.client.tomcat; import javax.servlet.http.HttpServletRequest; - import org.jasig.cas.client.util.CommonUtils; /** diff --git a/cas-client-integration-tomcat-common/src/test/java/org/jasig/cas/client/tomcat/PropertiesCasRealmDelegateTests.java b/cas-client-integration-tomcat-common/src/test/java/org/jasig/cas/client/tomcat/PropertiesCasRealmDelegateTests.java index 317190643..9f0ae2dc2 100644 --- a/cas-client-integration-tomcat-common/src/test/java/org/jasig/cas/client/tomcat/PropertiesCasRealmDelegateTests.java +++ b/cas-client-integration-tomcat-common/src/test/java/org/jasig/cas/client/tomcat/PropertiesCasRealmDelegateTests.java @@ -1,30 +1,27 @@ -/** +/* * 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: + * 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 + * 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 + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ - package org.jasig.cas.client.tomcat; import java.security.Principal; import java.util.Arrays; import java.util.List; - import junit.framework.TestCase; - import org.jasig.cas.client.authentication.AttributePrincipalImpl; /** @@ -48,10 +45,10 @@ public void testAuthenticate() { final Principal p = new AttributePrincipalImpl("rosencrantz"); assertTrue(p == realm.authenticate(p)); } - + public void testGetRoles() { final Principal p = new AttributePrincipalImpl("rosencrantz"); - final List expected = Arrays.asList(new String[] {"admins", "users"}); + final List expected = Arrays.asList(new String[] { "admins", "users" }); final List actual = Arrays.asList(realm.getRoles(p)); assertEquals(expected.size(), actual.size()); @@ -59,7 +56,7 @@ public void testGetRoles() { assertTrue(actual.contains(item)); } } - + public void testHasRole() { assertTrue(realm.hasRole(new AttributePrincipalImpl("rosencrantz"), "admins")); assertTrue(realm.hasRole(new AttributePrincipalImpl("rosencrantz"), "users")); diff --git a/cas-client-integration-tomcat-common/src/test/resources/org/jasig/cas/client/tomcat/user-roles.properties b/cas-client-integration-tomcat-common/src/test/resources/org/jasig/cas/client/tomcat/user-roles.properties index 81302fbbd..a72413d0f 100644 --- a/cas-client-integration-tomcat-common/src/test/resources/org/jasig/cas/client/tomcat/user-roles.properties +++ b/cas-client-integration-tomcat-common/src/test/resources/org/jasig/cas/client/tomcat/user-roles.properties @@ -4,15 +4,15 @@ # 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: +# 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 +# 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 +# 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. # diff --git a/cas-client-integration-tomcat-v6/NOTICE b/cas-client-integration-tomcat-v6/NOTICE new file mode 100644 index 000000000..9532d55a9 --- /dev/null +++ b/cas-client-integration-tomcat-v6/NOTICE @@ -0,0 +1,30 @@ +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. + +This project includes: + catalina under Apache License, Version 2.0 + 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 + Jasig CAS Client for Java - SAML Protocol Support 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 + JCL 1.1.1 implemented over SLF4J under MIT License + Joda-Time under Apache 2 + JUnit under Common Public License Version 1.0 + SLF4J API Module under MIT License + SLF4J Simple Binding under MIT License + diff --git a/cas-client-integration-tomcat-v6/pom.xml b/cas-client-integration-tomcat-v6/pom.xml index d70cd2cf1..ce874f264 100644 --- a/cas-client-integration-tomcat-v6/pom.xml +++ b/cas-client-integration-tomcat-v6/pom.xml @@ -1,42 +1,62 @@ - - - cas-client - org.jasig.cas.client - 3.3-SNAPSHOT - - 4.0.0 + + + cas-client + org.jasig.cas.client + 3.4.1-SNAPSHOT + + 4.0.0 - org.jasig.cas.client - cas-client-integration-tomcat-v6 - jar - Jasig CAS Client for Java - Tomcat 6.x Integration + org.jasig.cas.client + cas-client-integration-tomcat-v6 + jar + Jasig CAS Client for Java - Tomcat 6.x Integration - - - org.jasig.cas.client - cas-client-integration-tomcat-common - ${project.version} - jar - compile - - + + + org.jasig.cas.client + cas-client-integration-tomcat-common + ${project.version} + jar + compile + + + org.jasig.cas.client + cas-client-support-saml + ${project.version} + jar + compile + true + + + org.apache.tomcat + catalina + 6.0.29 + jar + provided + + + org.apache.tomcat + servlet-api + + + org.apache.tomcat + juli + + org.apache.tomcat - catalina - 6.0.29 - jar - compile - + annotations-api + + + - - org.jasig.cas.client - cas-client-core - ${project.version} - jar - compile - - + + org.jasig.cas.client + cas-client-core + ${project.version} + jar + compile + + diff --git a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractAuthenticator.java b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractAuthenticator.java index 4e1c7cc72..313de03d0 100644 --- a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractAuthenticator.java +++ b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractAuthenticator.java @@ -1,24 +1,25 @@ -/** +/* * 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: + * 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 + * 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 + * 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.v6; +import java.io.IOException; +import java.security.Principal; import org.apache.catalina.LifecycleEvent; import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleListener; @@ -27,15 +28,12 @@ import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.deploy.LoginConfig; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.jasig.cas.client.tomcat.AuthenticatorDelegate; import org.jasig.cas.client.tomcat.CasRealm; import org.jasig.cas.client.util.CommonUtils; import org.jasig.cas.client.validation.TicketValidator; - -import java.io.IOException; -import java.security.Principal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Base authenticator for all authentication protocols supported by CAS. @@ -46,8 +44,8 @@ */ public abstract class AbstractAuthenticator extends AuthenticatorBase implements LifecycleListener { - protected final Log log = LogFactory.getLog(getClass()); - + protected final Logger logger = LoggerFactory.getLogger(getClass()); + private final AuthenticatorDelegate delegate = new AuthenticatorDelegate(); private String casServerUrlPrefix; @@ -84,16 +82,15 @@ public abstract class AbstractAuthenticator extends AuthenticatorBase implements public void start() throws LifecycleException { super.start(); - this.log.debug(getName() + " starting."); + logger.debug("{} starting.", getName()); final Realm realm = this.context.getRealm(); try { CommonUtils.assertTrue(realm instanceof CasRealm, "Expected CasRealm but got " + realm.getInfo()); CommonUtils.assertNotNull(this.casServerUrlPrefix, "casServerUrlPrefix cannot be null."); CommonUtils.assertNotNull(this.delegate.getCasServerLoginUrl(), "casServerLoginUrl cannot be null."); - CommonUtils.assertTrue( - this.delegate.getServerName() != null || this.delegate.getServiceUrl() != null, + CommonUtils.assertTrue(this.delegate.getServerName() != null || this.delegate.getServiceUrl() != null, "either serverName or serviceUrl must be set."); - this.delegate.setRealm((CasRealm) realm); + this.delegate.setRealm((CasRealm) realm); } catch (final Exception e) { throw new LifecycleException(e); } @@ -147,12 +144,13 @@ public final void setEncoding(final String encoding) { } /** {@inheritDoc} */ - public final boolean authenticate(final Request request, final Response response, final LoginConfig loginConfig) throws IOException { + public final boolean authenticate(final Request request, final Response response, final LoginConfig loginConfig) + throws IOException { Principal principal = request.getUserPrincipal(); boolean result = false; if (principal == null) { // Authentication sets the response headers for status and redirect if needed - principal = this.delegate.authenticate(request.getRequest(), response); + principal = this.delegate.authenticate(request.getRequest(), response); if (principal != null) { register(request, response, principal, getAuthenticationMethod(), null, null); result = true; @@ -166,7 +164,7 @@ public final boolean authenticate(final Request request, final Response response /** {@inheritDoc} */ public void lifecycleEvent(final LifecycleEvent event) { if (AFTER_START_EVENT.equals(event.getType())) { - this.log.debug(getName() + " processing lifecycle event " + AFTER_START_EVENT); + logger.debug("{} processing lifecycle event {}", getName(), AFTER_START_EVENT); this.delegate.setTicketValidator(getTicketValidator()); this.delegate.setArtifactParameterName(getArtifactParameterName()); this.delegate.setServiceParameterName(getServiceParameterName()); diff --git a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractCasAuthenticator.java b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractCasAuthenticator.java index e4c84acf8..b0fccc8aa 100644 --- a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractCasAuthenticator.java +++ b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractCasAuthenticator.java @@ -1,22 +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: + * 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 + * 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 + * 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.v6; /** @@ -30,7 +29,6 @@ public abstract class AbstractCasAuthenticator extends AbstractAuthenticator { private String proxyCallbackUrl; - protected final String getProxyCallbackUrl() { return this.proxyCallbackUrl; } diff --git a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractCasRealm.java b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractCasRealm.java index a08cebca9..083db69b2 100644 --- a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractCasRealm.java +++ b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractCasRealm.java @@ -1,30 +1,28 @@ -/** +/* * 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: + * 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 + * 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 + * 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.v6; import java.security.Principal; - import org.apache.catalina.realm.RealmBase; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.jasig.cas.client.tomcat.CasRealm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Base Realm implementation for all CAS realms. @@ -36,8 +34,7 @@ public abstract class AbstractCasRealm extends RealmBase implements CasRealm { /** Logger instance */ - protected final Log log = LogFactory.getLog(getClass()); - + protected final Logger logger = LoggerFactory.getLogger(getClass()); /** {@inheritDoc} */ public Principal authenticate(final Principal p) { @@ -58,7 +55,7 @@ public boolean hasRole(final Principal principal, final String role) { public String toString() { return getName(); } - + /** {@inheritDoc} */ public String getInfo() { return getClass().getName() + "/1.0"; diff --git a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractLifecycleValve.java b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractLifecycleValve.java index 50a0984f7..3f85114e4 100644 --- a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractLifecycleValve.java +++ b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractLifecycleValve.java @@ -1,22 +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: + * 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 + * 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 + * 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.v6; import org.apache.catalina.Lifecycle; @@ -24,8 +23,8 @@ import org.apache.catalina.LifecycleListener; import org.apache.catalina.util.LifecycleSupport; import org.apache.catalina.valves.ValveBase; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Base Valve implementation for valves that need Catalina lifecycle @@ -38,12 +37,11 @@ public abstract class AbstractLifecycleValve extends ValveBase implements Lifecycle { /** Logger instance */ - protected final Log log = LogFactory.getLog(getClass()); - + protected final Logger logger = LoggerFactory.getLogger(getClass()); + /** Lifecycle listeners */ private LifecycleSupport lifecycle = new LifecycleSupport(this); - - + /** {@inheritDoc} */ public void addLifecycleListener(final LifecycleListener listener) { lifecycle.addLifecycleListener(listener); @@ -61,12 +59,12 @@ public void removeLifecycleListener(final LifecycleListener listener) { /** {@inheritDoc} */ public void start() throws LifecycleException { - log.debug(getName() + " starting."); + logger.debug("{} starting.", getName()); } /** {@inheritDoc} */ public void stop() throws LifecycleException { - log.debug(getName() + " stopping."); + logger.debug("{} stopping.", getName()); } /** diff --git a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractLogoutValve.java b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractLogoutValve.java index 8a9d8eeca..f893a2186 100644 --- a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractLogoutValve.java +++ b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AbstractLogoutValve.java @@ -1,31 +1,29 @@ -/** +/* * 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: + * 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 + * 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 + * 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.v6; +import java.io.IOException; +import javax.servlet.ServletException; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.jasig.cas.client.tomcat.LogoutHandler; -import javax.servlet.ServletException; -import java.io.IOException; - /** * Abstract base class for Container-managed log out. Removes the attributes * from the session. @@ -43,9 +41,9 @@ public final void invoke(final Request request, final Response response) throws return; } - this.log.debug("URI is not a logout request: " + request.getRequestURI()); + 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-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AssertionCasRealm.java b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AssertionCasRealm.java index 9dcbc15a8..eee4cf9b8 100644 --- a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AssertionCasRealm.java +++ b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/AssertionCasRealm.java @@ -1,22 +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: + * 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 + * 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 + * 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.v6; import org.apache.catalina.LifecycleException; @@ -36,11 +35,11 @@ public class AssertionCasRealm extends AbstractCasRealm { private final AssertionCasRealmDelegate delegate = new AssertionCasRealmDelegate(); - + /** {@inheritDoc} */ public void start() throws LifecycleException { super.start(); - log.info("Startup completed."); + logger.info("Startup completed."); } /** diff --git a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Cas10CasAuthenticator.java b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Cas10CasAuthenticator.java index b30bdb4b0..0c6a1276a 100644 --- a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Cas10CasAuthenticator.java +++ b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Cas10CasAuthenticator.java @@ -1,22 +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: + * 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 + * 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 + * 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.v6; import org.apache.catalina.LifecycleException; @@ -41,7 +40,7 @@ public class Cas10CasAuthenticator extends AbstractCasAuthenticator { protected TicketValidator getTicketValidator() { return this.ticketValidator; } - + protected String getAuthenticationMethod() { return AUTH_METHOD; } @@ -54,6 +53,6 @@ public void start() throws LifecycleException { super.start(); this.ticketValidator = new Cas10TicketValidator(getCasServerUrlPrefix()); lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); - log.info("Startup completed."); + logger.info("Startup completed."); } } diff --git a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Cas20CasAuthenticator.java b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Cas20CasAuthenticator.java index 462094d71..03ceb2961 100644 --- a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Cas20CasAuthenticator.java +++ b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Cas20CasAuthenticator.java @@ -1,22 +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: + * 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 + * 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 + * 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.v6; import org.apache.catalina.LifecycleException; @@ -35,13 +34,13 @@ public final class Cas20CasAuthenticator extends AbstractCasAuthenticator { public static final String AUTH_METHOD = "CAS20"; private static final String NAME = Cas20CasAuthenticator.class.getName(); - + private Cas20ServiceTicketValidator ticketValidator; protected TicketValidator getTicketValidator() { return this.ticketValidator; } - + protected String getAuthenticationMethod() { return AUTH_METHOD; } @@ -60,6 +59,6 @@ public void start() throws LifecycleException { this.ticketValidator.setProxyGrantingTicketStorage(ProxyCallbackValve.getProxyGrantingTicketStorage()); this.ticketValidator.setRenew(isRenew()); lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); - this.log.info("Startup completed."); + logger.info("Startup completed."); } } diff --git a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Cas20ProxyCasAuthenticator.java b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Cas20ProxyCasAuthenticator.java index cc577f0ca..fe64d729a 100644 --- a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Cas20ProxyCasAuthenticator.java +++ b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Cas20ProxyCasAuthenticator.java @@ -1,22 +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: + * 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 + * 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 + * 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.v6; import org.apache.catalina.LifecycleException; @@ -38,7 +37,7 @@ public final class Cas20ProxyCasAuthenticator extends AbstractCasAuthenticator { private static final String NAME = Cas20ProxyCasAuthenticator.class.getName(); private Cas20ProxyTicketValidator ticketValidator; - + private boolean acceptAnyProxy; private String allowedProxyChains; @@ -54,7 +53,7 @@ public void setAllowedProxyChains(final String allowedProxyChains) { protected TicketValidator getTicketValidator() { return this.ticketValidator; } - + protected String getAuthenticationMethod() { return AUTH_METHOD; } @@ -75,6 +74,6 @@ public void start() throws LifecycleException { this.ticketValidator.setEncoding(getEncoding()); } lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); - this.log.info("Startup completed."); + logger.info("Startup completed."); } } diff --git a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/PropertiesCasRealm.java b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/PropertiesCasRealm.java index 080d1acfc..88dcbb369 100644 --- a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/PropertiesCasRealm.java +++ b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/PropertiesCasRealm.java @@ -1,22 +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: + * 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 + * 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 + * 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.v6; import org.apache.catalina.LifecycleException; @@ -49,12 +48,12 @@ public class PropertiesCasRealm extends AbstractCasRealm { public void setPropertiesFilePath(final String path) { this.delegate.setPropertiesFilePath(path); } - + /** {@inheritDoc} */ public void start() throws LifecycleException { super.start(); this.delegate.readProperties(); - this.log.info("Startup completed."); + logger.info("Startup completed."); } /** {@inheritDoc} */ diff --git a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/ProxyCallbackValve.java b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/ProxyCallbackValve.java index 3d1dc161e..d695fd86e 100644 --- a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/ProxyCallbackValve.java +++ b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/ProxyCallbackValve.java @@ -1,24 +1,25 @@ -/** +/* * 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: + * 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 + * 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 + * 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.v6; +import java.io.IOException; +import javax.servlet.ServletException; import org.apache.catalina.LifecycleException; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; @@ -26,9 +27,6 @@ import org.jasig.cas.client.util.CommonUtils; import org.jasig.cas.client.util.ReflectUtils; -import javax.servlet.ServletException; -import java.io.IOException; - /** * Handles watching a url for the proxy callback. *

    @@ -73,12 +71,12 @@ public void start() throws LifecycleException { } catch (final Exception e) { throw new LifecycleException(e); } - this.log.info("Startup completed."); + logger.info("Startup completed."); } public void invoke(final Request request, final Response response) throws IOException, ServletException { if (this.proxyCallbackUrl.equals(request.getRequestURI())) { - this.log.debug("Processing proxy callback request."); + logger.debug("Processing proxy callback request."); CommonUtils.readAndRespondToProxyReceptorRequest(request, response, PROXY_GRANTING_TICKET_STORAGE); return; } diff --git a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/RegexUriLogoutValve.java b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/RegexUriLogoutValve.java index 150e377b7..bcdea8041 100644 --- a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/RegexUriLogoutValve.java +++ b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/RegexUriLogoutValve.java @@ -1,22 +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: + * 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 + * 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 + * 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.v6; import org.apache.catalina.LifecycleException; @@ -34,7 +33,7 @@ public final class RegexUriLogoutValve extends AbstractLogoutValve { private static final String NAME = RegexUriLogoutValve.class.getName(); - + private RegexUriLogoutHandler logoutHandler = new RegexUriLogoutHandler(); public void setRedirectUrl(final String redirectUrl) { @@ -48,7 +47,7 @@ public void setLogoutUriRegex(final String regex) { public void start() throws LifecycleException { super.start(); this.logoutHandler.init(); - this.log.info("Startup completed."); + logger.info("Startup completed."); } /** {@inheritDoc} */ diff --git a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Saml11Authenticator.java b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Saml11Authenticator.java index 924798594..c24bc0d88 100644 --- a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Saml11Authenticator.java +++ b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/Saml11Authenticator.java @@ -1,22 +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: + * 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 + * 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 + * 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.v6; import org.apache.catalina.LifecycleException; @@ -38,7 +37,7 @@ public class Saml11Authenticator extends AbstractAuthenticator { private static final String NAME = Saml11Authenticator.class.getName(); private Saml11TicketValidator ticketValidator; - + /** SAML protocol clock drift tolerance in ms */ private int tolerance = -1; @@ -53,24 +52,24 @@ public void start() throws LifecycleException { super.start(); this.ticketValidator = new Saml11TicketValidator(getCasServerUrlPrefix()); if (this.tolerance > -1) { - this.ticketValidator.setTolerance(this.tolerance); + this.ticketValidator.setTolerance(this.tolerance); } if (getEncoding() != null) { this.ticketValidator.setEncoding(getEncoding()); } this.ticketValidator.setRenew(isRenew()); lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); - this.log.info("Startup completed."); + logger.info("Startup completed."); } protected TicketValidator getTicketValidator() { return this.ticketValidator; } - + protected String getAuthenticationMethod() { return AUTH_METHOD; } - + /** {@inheritDoc} */ protected String getArtifactParameterName() { return "SAMLart"; diff --git a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/SingleSignOutValve.java b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/SingleSignOutValve.java index d7c5d1238..9941651a8 100644 --- a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/SingleSignOutValve.java +++ b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/SingleSignOutValve.java @@ -1,35 +1,31 @@ -/** +/* * 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: + * 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 + * 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 + * 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.v6; import java.io.IOException; - import javax.servlet.ServletException; - 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.jasig.cas.client.session.SessionMappingStorage; import org.jasig.cas.client.session.SingleSignOutHandler; @@ -45,50 +41,52 @@ public class SingleSignOutValve extends AbstractLifecycleValve implements SessionListener { private static final String NAME = SingleSignOutValve.class.getName(); - + private final SingleSignOutHandler handler = new SingleSignOutHandler(); public void setArtifactParameterName(final String name) { - handler.setArtifactParameterName(name); + this.handler.setArtifactParameterName(name); } - + public void setLogoutParameterName(final String name) { - handler.setLogoutParameterName(name); + this.handler.setLogoutParameterName(name); + } + + public void setFrontLogoutParameterName(final String name) { + this.handler.setFrontLogoutParameterName(name); + } + + public void setRelayStateParameterName(final String name) { + this.handler.setRelayStateParameterName(name); + } + + public void setCasServerUrlPrefix(final String casServerUrlPrefix) { + this.handler.setCasServerUrlPrefix(casServerUrlPrefix); } public void setSessionMappingStorage(final SessionMappingStorage storage) { - handler.setSessionMappingStorage(storage); + this.handler.setSessionMappingStorage(storage); } /** {@inheritDoc} */ public void start() throws LifecycleException { super.start(); - handler.init(); - log.info("Startup completed."); + this.handler.init(); + logger.info("Startup completed."); } /** {@inheritDoc} */ public void invoke(final Request request, final Response response) throws IOException, ServletException { - if (this.handler.isTokenRequest(request)) { - this.handler.recordSession(request); - request.getSessionInternal(true).addSessionListener(this); - } - else if (this.handler.isLogoutRequest(request)) { - this.handler.destroySession(request); - // Do not proceed up valve chain - return; - } else { - log.debug("Ignoring URI " + request.getRequestURI()); + if (this.handler.process(request, response)) { + getNext().invoke(request, response); } - getNext().invoke(request, response); } - /** {@inheritDoc} */ public void sessionEvent(final SessionEvent event) { if (Session.SESSION_DESTROYED_EVENT.equals(event.getType())) { - this.log.debug("Cleaning up SessionMappingStorage on destroySession event"); - this.handler.getSessionMappingStorage().removeBySessionById(event.getSession().getId()); + logger.debug("Cleaning up SessionMappingStorage on destroySession event"); + this.handler.getSessionMappingStorage().removeBySessionById(event.getSession().getId()); } } @@ -96,5 +94,4 @@ public void sessionEvent(final SessionEvent event) { protected String getName() { return NAME; } - } diff --git a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/StaticUriLogoutValve.java b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/StaticUriLogoutValve.java index 0ddbd0990..d1167c4ba 100644 --- a/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/StaticUriLogoutValve.java +++ b/cas-client-integration-tomcat-v6/src/main/java/org/jasig/cas/client/tomcat/v6/StaticUriLogoutValve.java @@ -1,22 +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: + * 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 + * 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 + * 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.v6; import org.apache.catalina.LifecycleException; @@ -40,7 +39,7 @@ public final class StaticUriLogoutValve extends AbstractLogoutValve { public void setRedirectUrl(final String redirectUrl) { this.logoutHandler.setRedirectUrl(redirectUrl); } - + public void setLogoutUri(final String logoutUri) { this.logoutHandler.setLogoutUri(logoutUri); } @@ -48,7 +47,7 @@ public void setLogoutUri(final String logoutUri) { public void start() throws LifecycleException { super.start(); this.logoutHandler.init(); - this.log.info("Startup completed."); + logger.info("Startup completed."); } /** {@inheritDoc} */ diff --git a/cas-client-integration-tomcat-v7/NOTICE b/cas-client-integration-tomcat-v7/NOTICE new file mode 100644 index 000000000..f5ce3c060 --- /dev/null +++ b/cas-client-integration-tomcat-v7/NOTICE @@ -0,0 +1,30 @@ +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. + +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 + Jasig CAS Client for Java - SAML Protocol Support 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 + JCL 1.1.1 implemented over SLF4J under MIT License + Joda-Time under Apache 2 + JUnit under Common Public License Version 1.0 + SLF4J API Module under MIT License + SLF4J Simple Binding under MIT License + tomcat-catalina under Apache License, Version 2.0 + diff --git a/cas-client-integration-tomcat-v7/pom.xml b/cas-client-integration-tomcat-v7/pom.xml index 47abd3c10..7399f8c6a 100644 --- a/cas-client-integration-tomcat-v7/pom.xml +++ b/cas-client-integration-tomcat-v7/pom.xml @@ -1,42 +1,70 @@ - - - cas-client - org.jasig.cas.client - 3.3-SNAPSHOT - - 4.0.0 + + + cas-client + org.jasig.cas.client + 3.4.1-SNAPSHOT + + 4.0.0 - org.jasig.cas.client - cas-client-integration-tomcat-v7 - jar - Jasig CAS Client for Java - Tomcat 7.x Integration + org.jasig.cas.client + cas-client-integration-tomcat-v7 + jar + Jasig CAS Client for Java - Tomcat 7.x Integration - - - org.jasig.cas.client - cas-client-integration-tomcat-common - ${project.version} - jar - compile - - - org.apache.tomcat - tomcat-catalina - 7.0.8 - jar - compile - + + + org.jasig.cas.client + cas-client-integration-tomcat-common + ${project.version} + jar + compile + + + org.jasig.cas.client + cas-client-support-saml + ${project.version} + jar + compile + true + + + org.apache.tomcat + tomcat-catalina + 7.0.8 + jar + provided + + + org.apache.tomcat + tomcat-servlet-api + + + org.apache.tomcat + tomcat-juli + + + org.apache.tomcat + tomcat-annotations-api + + + org.apache.tomcat + tomcat-api + + + org.apache.tomcat + tomcat-util + + + - - org.jasig.cas.client - cas-client-core - ${project.version} - jar - compile - - + + org.jasig.cas.client + cas-client-core + ${project.version} + jar + compile + + diff --git a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractAuthenticator.java b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractAuthenticator.java index 137367659..96a56c888 100644 --- a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractAuthenticator.java +++ b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractAuthenticator.java @@ -1,42 +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: + * 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 + * 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 + * 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.v7; -import org.apache.catalina.LifecycleEvent; -import org.apache.catalina.LifecycleException; -import org.apache.catalina.LifecycleListener; -import org.apache.catalina.LifecycleState; -import org.apache.catalina.Realm; +import java.io.IOException; +import java.security.Principal; +import javax.servlet.http.HttpServletResponse; +import org.apache.catalina.*; import org.apache.catalina.authenticator.AuthenticatorBase; import org.apache.catalina.connector.Request; import org.apache.catalina.deploy.LoginConfig; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.jasig.cas.client.tomcat.AuthenticatorDelegate; import org.jasig.cas.client.tomcat.CasRealm; import org.jasig.cas.client.util.CommonUtils; import org.jasig.cas.client.validation.TicketValidator; - -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.security.Principal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Base authenticator for all authentication protocols supported by CAS. @@ -47,8 +41,8 @@ */ public abstract class AbstractAuthenticator extends AuthenticatorBase implements LifecycleListener { - protected final Log log = LogFactory.getLog(getClass()); - + protected final Logger logger = LoggerFactory.getLogger(getClass()); + private final AuthenticatorDelegate delegate = new AuthenticatorDelegate(); private String casServerUrlPrefix; @@ -67,7 +61,7 @@ public abstract class AbstractAuthenticator extends AuthenticatorBase implements * @return the authentication method. */ protected String getAuthMethod() { - return getAuthenticationMethod(); + return getAuthenticationMethod(); } /** @@ -94,7 +88,7 @@ protected String getAuthMethod() { protected void startInternal() throws LifecycleException { super.startInternal(); - this.log.debug(getName() + " starting."); + logger.debug("{} starting.", getName()); final Realm realm = this.context.getRealm(); try { CommonUtils.assertTrue(realm instanceof CasRealm, "Expected CasRealm but got " + realm.getInfo()); @@ -102,7 +96,7 @@ protected void startInternal() throws LifecycleException { CommonUtils.assertNotNull(this.delegate.getCasServerLoginUrl(), "casServerLoginUrl cannot be null."); CommonUtils.assertTrue(this.delegate.getServerName() != null || this.delegate.getServiceUrl() != null, "either serverName or serviceUrl must be set."); - this.delegate.setRealm((CasRealm) realm); + this.delegate.setRealm((CasRealm) realm); } catch (final Exception e) { throw new LifecycleException(e); } @@ -156,12 +150,13 @@ public final void setEncoding(final String encoding) { } /** {@inheritDoc} */ - public final boolean authenticate(final Request request, final HttpServletResponse response, final LoginConfig loginConfig) throws IOException { + public final boolean authenticate(final Request request, final HttpServletResponse response, + final LoginConfig loginConfig) throws IOException { Principal principal = request.getUserPrincipal(); boolean result = false; if (principal == null) { // Authentication sets the response headers for status and redirect if needed - principal = this.delegate.authenticate(request.getRequest(), response); + principal = this.delegate.authenticate(request.getRequest(), response); if (principal != null) { register(request, response, principal, getAuthenticationMethod(), null, null); result = true; @@ -175,7 +170,7 @@ public final boolean authenticate(final Request request, final HttpServletRespon /** {@inheritDoc} */ public void lifecycleEvent(final LifecycleEvent event) { if (AFTER_START_EVENT.equals(event.getType())) { - this.log.debug(getName() + " processing lifecycle event " + AFTER_START_EVENT); + logger.debug("{} processing lifecycle event {}", getName(), AFTER_START_EVENT); this.delegate.setTicketValidator(getTicketValidator()); this.delegate.setArtifactParameterName(getArtifactParameterName()); this.delegate.setServiceParameterName(getServiceParameterName()); @@ -186,12 +181,12 @@ public void lifecycleEvent(final LifecycleEvent event) { public String getInfo() { return getName() + "/1.0"; } - + /** {@inheritDoc} */ protected synchronized void setState(LifecycleState state, Object data) { super.setState(state, data); if (LifecycleState.STARTED.equals(state)) { - this.log.info(getName() + " started."); + logger.info("{} started.", getName()); } } diff --git a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractCasAuthenticator.java b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractCasAuthenticator.java index 1dfa263bc..7f451e801 100644 --- a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractCasAuthenticator.java +++ b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractCasAuthenticator.java @@ -1,22 +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: + * 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 + * 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 + * 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.v7; /** diff --git a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractCasRealm.java b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractCasRealm.java index 26091a39c..3ffd729be 100644 --- a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractCasRealm.java +++ b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractCasRealm.java @@ -1,26 +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: + * 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 + * 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 + * 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.v7; import java.security.Principal; - import org.apache.catalina.Wrapper; import org.apache.catalina.realm.RealmBase; import org.jasig.cas.client.tomcat.CasRealm; @@ -59,7 +57,7 @@ public boolean hasRole(final Wrapper wrapper, final Principal principal, final S public String toString() { return getName(); } - + /** {@inheritDoc} */ public String getInfo() { return getClass().getName() + "/1.0"; diff --git a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractLogoutValve.java b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractLogoutValve.java index dd24e28a2..550bf2acd 100644 --- a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractLogoutValve.java +++ b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AbstractLogoutValve.java @@ -1,33 +1,31 @@ -/** +/* * 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: + * 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 + * 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 + * 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.v7; +import java.io.IOException; +import javax.servlet.ServletException; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.valves.ValveBase; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.jasig.cas.client.tomcat.LogoutHandler; - -import javax.servlet.ServletException; -import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Abstract base class for Container-managed log out. Removes the attributes @@ -40,7 +38,7 @@ */ public abstract class AbstractLogoutValve extends ValveBase { - protected final Log log = LogFactory.getLog(getClass()); + protected final Logger logger = LoggerFactory.getLogger(getClass()); public final void invoke(final Request request, final Response response) throws IOException, ServletException { if (getLogoutHandler().isLogoutRequest(request)) { @@ -49,9 +47,9 @@ public final void invoke(final Request request, final Response response) throws return; } - this.log.debug("URI is not a logout request: " + request.getRequestURI()); + 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-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AssertionCasRealm.java b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AssertionCasRealm.java index edfddba00..43bd27ca5 100644 --- a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AssertionCasRealm.java +++ b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/AssertionCasRealm.java @@ -1,22 +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: + * 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 + * 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 + * 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.v7; import org.jasig.cas.client.tomcat.AssertionCasRealmDelegate; diff --git a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Cas10CasAuthenticator.java b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Cas10CasAuthenticator.java index 3d2d59329..a6211ab0e 100644 --- a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Cas10CasAuthenticator.java +++ b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Cas10CasAuthenticator.java @@ -1,22 +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: + * 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 + * 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 + * 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.v7; import org.apache.catalina.LifecycleException; @@ -33,7 +32,7 @@ public final class Cas10CasAuthenticator extends AbstractCasAuthenticator { public static final String AUTH_METHOD = "CAS10"; - + private static final String NAME = Cas10CasAuthenticator.class.getName(); private Cas10TicketValidator ticketValidator; @@ -41,7 +40,7 @@ public final class Cas10CasAuthenticator extends AbstractCasAuthenticator { protected TicketValidator getTicketValidator() { return this.ticketValidator; } - + protected String getAuthenticationMethod() { return AUTH_METHOD; } diff --git a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Cas20CasAuthenticator.java b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Cas20CasAuthenticator.java index c17d99239..5b69c43d4 100644 --- a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Cas20CasAuthenticator.java +++ b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Cas20CasAuthenticator.java @@ -1,22 +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: + * 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 + * 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 + * 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.v7; import org.apache.catalina.LifecycleException; @@ -35,13 +34,13 @@ public final class Cas20CasAuthenticator extends AbstractCasAuthenticator { public static final String AUTH_METHOD = "CAS20"; private static final String NAME = Cas20CasAuthenticator.class.getName(); - + private Cas20ServiceTicketValidator ticketValidator; protected TicketValidator getTicketValidator() { return this.ticketValidator; } - + protected String getAuthenticationMethod() { return AUTH_METHOD; } diff --git a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Cas20ProxyCasAuthenticator.java b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Cas20ProxyCasAuthenticator.java index cc3d2756c..11fc5367a 100644 --- a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Cas20ProxyCasAuthenticator.java +++ b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Cas20ProxyCasAuthenticator.java @@ -1,22 +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: + * 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 + * 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 + * 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.v7; import org.apache.catalina.LifecycleException; @@ -38,7 +37,7 @@ public final class Cas20ProxyCasAuthenticator extends AbstractCasAuthenticator { private static final String NAME = Cas20ProxyCasAuthenticator.class.getName(); private Cas20ProxyTicketValidator ticketValidator; - + private boolean acceptAnyProxy; private String allowedProxyChains; @@ -54,7 +53,7 @@ public void setAllowedProxyChains(final String allowedProxyChains) { protected TicketValidator getTicketValidator() { return this.ticketValidator; } - + protected String getAuthenticationMethod() { return AUTH_METHOD; } diff --git a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/PropertiesCasRealm.java b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/PropertiesCasRealm.java index ce1dee1f0..e3f4b122f 100644 --- a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/PropertiesCasRealm.java +++ b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/PropertiesCasRealm.java @@ -1,22 +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: + * 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 + * 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 + * 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.v7; import org.apache.catalina.LifecycleException; @@ -49,7 +48,7 @@ public class PropertiesCasRealm extends AbstractCasRealm { public void setPropertiesFilePath(final String path) { this.delegate.setPropertiesFilePath(path); } - + /** {@inheritDoc} */ protected void startInternal() throws LifecycleException { super.startInternal(); diff --git a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/ProxyCallbackValve.java b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/ProxyCallbackValve.java index 5966ba1c0..f0c2afeaa 100644 --- a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/ProxyCallbackValve.java +++ b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/ProxyCallbackValve.java @@ -1,36 +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: + * 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 + * 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 + * 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.v7; +import java.io.IOException; +import javax.servlet.ServletException; 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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage; import org.jasig.cas.client.util.CommonUtils; import org.jasig.cas.client.util.ReflectUtils; - -import javax.servlet.ServletException; -import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Handles watching a url for the proxy callback. @@ -46,9 +44,9 @@ public final class ProxyCallbackValve extends ValveBase { private static ProxyGrantingTicketStorage PROXY_GRANTING_TICKET_STORAGE; - + /** Logger instance */ - private final Log log = LogFactory.getLog(getClass()); + private final Logger logger = LoggerFactory.getLogger(getClass()); private String proxyGrantingTicketStorageClass; @@ -77,12 +75,12 @@ protected void startInternal() throws LifecycleException { } catch (final Exception e) { throw new LifecycleException(e); } - this.log.info("Startup completed."); + logger.info("Startup completed."); } public void invoke(final Request request, final Response response) throws IOException, ServletException { if (this.proxyCallbackUrl.equals(request.getRequestURI())) { - this.log.debug("Processing proxy callback request."); + logger.debug("Processing proxy callback request."); CommonUtils.readAndRespondToProxyReceptorRequest(request, response, PROXY_GRANTING_TICKET_STORAGE); return; } diff --git a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/RegexUriLogoutValve.java b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/RegexUriLogoutValve.java index c87ad7f83..f97426c80 100644 --- a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/RegexUriLogoutValve.java +++ b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/RegexUriLogoutValve.java @@ -1,22 +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: + * 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 + * 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 + * 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.v7; import org.apache.catalina.LifecycleException; @@ -32,7 +31,7 @@ * @since 3.1.12 */ public final class RegexUriLogoutValve extends AbstractLogoutValve { - + private RegexUriLogoutHandler logoutHandler = new RegexUriLogoutHandler(); public void setRedirectUrl(final String redirectUrl) { @@ -46,7 +45,7 @@ public void setLogoutUriRegex(final String regex) { protected void startInternal() throws LifecycleException { super.startInternal(); this.logoutHandler.init(); - this.log.info("Startup completed."); + logger.info("Startup completed."); } /** {@inheritDoc} */ diff --git a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Saml11Authenticator.java b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Saml11Authenticator.java index 84877951b..b3b5b8829 100644 --- a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Saml11Authenticator.java +++ b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/Saml11Authenticator.java @@ -1,22 +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: + * 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 + * 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 + * 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.v7; import org.apache.catalina.LifecycleException; @@ -34,15 +33,14 @@ 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. */ @@ -54,7 +52,7 @@ protected void startInternal() throws LifecycleException { super.startInternal(); this.ticketValidator = new Saml11TicketValidator(getCasServerUrlPrefix()); if (this.tolerance > -1) { - this.ticketValidator.setTolerance(this.tolerance); + this.ticketValidator.setTolerance(this.tolerance); } if (getEncoding() != null) { this.ticketValidator.setEncoding(getEncoding()); @@ -65,11 +63,11 @@ protected void startInternal() throws LifecycleException { protected TicketValidator getTicketValidator() { return this.ticketValidator; } - + protected String getAuthenticationMethod() { return AUTH_METHOD; } - + /** {@inheritDoc} */ protected String getArtifactParameterName() { return "SAMLart"; diff --git a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/SingleSignOutValve.java b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/SingleSignOutValve.java index f1b024957..62ac2149b 100644 --- a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/SingleSignOutValve.java +++ b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/SingleSignOutValve.java @@ -1,28 +1,25 @@ -/** +/* * 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: + * 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 + * 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 + * 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.v7; import java.io.IOException; - import javax.servlet.ServletException; - import org.apache.catalina.LifecycleException; import org.apache.catalina.Session; import org.apache.catalina.SessionEvent; @@ -30,11 +27,10 @@ import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.valves.ValveBase; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - import org.jasig.cas.client.session.SessionMappingStorage; import org.jasig.cas.client.session.SingleSignOutHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Handles logout request messages sent from the CAS server by ending the current @@ -48,53 +44,54 @@ public class SingleSignOutValve extends ValveBase implements SessionListener { /** Logger instance */ - private final Log log = LogFactory.getLog(getClass()); - + private final Logger logger = LoggerFactory.getLogger(getClass()); + private final SingleSignOutHandler handler = new SingleSignOutHandler(); public void setArtifactParameterName(final String name) { - handler.setArtifactParameterName(name); + this.handler.setArtifactParameterName(name); } - + public void setLogoutParameterName(final String name) { - handler.setLogoutParameterName(name); + this.handler.setLogoutParameterName(name); } - public void setSessionMappingStorage(final SessionMappingStorage storage) { - handler.setSessionMappingStorage(storage); + public void setFrontLogoutParameterName(final String name) { + this.handler.setFrontLogoutParameterName(name); } + public void setRelayStateParameterName(final String name) { + this.handler.setRelayStateParameterName(name); + } + + public void setCasServerUrlPrefix(final String casServerUrlPrefix) { + this.handler.setCasServerUrlPrefix(casServerUrlPrefix); + } + + public void setSessionMappingStorage(final SessionMappingStorage storage) { + this.handler.setSessionMappingStorage(storage); + } /** {@inheritDoc} */ public void invoke(final Request request, final Response response) throws IOException, ServletException { - if (this.handler.isTokenRequest(request)) { - this.handler.recordSession(request); - request.getSessionInternal(true).addSessionListener(this); - } - else if (this.handler.isLogoutRequest(request)) { - this.handler.destroySession(request); - // Do not proceed up valve chain - return; - } else { - this.log.debug("Ignoring URI " + request.getRequestURI()); + if (this.handler.process(request, response)) { + getNext().invoke(request, response); } - getNext().invoke(request, response); } - /** {@inheritDoc} */ public void sessionEvent(final SessionEvent event) { if (Session.SESSION_DESTROYED_EVENT.equals(event.getType())) { - this.log.debug("Cleaning up SessionMappingStorage on destroySession event"); - this.handler.getSessionMappingStorage().removeBySessionById(event.getSession().getId()); + logger.debug("Cleaning up SessionMappingStorage on destroySession event"); + this.handler.getSessionMappingStorage().removeBySessionById(event.getSession().getId()); } } /** {@inheritDoc} */ protected void startInternal() throws LifecycleException { super.startInternal(); - this.log.info("Starting..."); - handler.init(); - this.log.info("Startup completed."); + logger.info("Starting..."); + this.handler.init(); + logger.info("Startup completed."); } } diff --git a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/StaticUriLogoutValve.java b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/StaticUriLogoutValve.java index aaf550292..cc7dd0d00 100644 --- a/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/StaticUriLogoutValve.java +++ b/cas-client-integration-tomcat-v7/src/main/java/org/jasig/cas/client/tomcat/v7/StaticUriLogoutValve.java @@ -1,22 +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: + * 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 + * 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 + * 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.v7; import org.apache.catalina.LifecycleException; @@ -38,7 +37,7 @@ public final class StaticUriLogoutValve extends AbstractLogoutValve { public void setRedirectUrl(final String redirectUrl) { this.logoutHandler.setRedirectUrl(redirectUrl); } - + public void setLogoutUri(final String logoutUri) { this.logoutHandler.setLogoutUri(logoutUri); } @@ -46,7 +45,7 @@ public void setLogoutUri(final String logoutUri) { protected void startInternal() throws LifecycleException { super.startInternal(); this.logoutHandler.init(); - this.log.info("Startup completed."); + logger.info("Startup completed."); } /** {@inheritDoc} */ diff --git a/cas-client-support-distributed-ehcache/NOTICE b/cas-client-support-distributed-ehcache/NOTICE new file mode 100644 index 000000000..1ccc84be8 --- /dev/null +++ b/cas-client-support-distributed-ehcache/NOTICE @@ -0,0 +1,27 @@ +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. + +This project includes: + Ehcache Core under The Apache Software License, Version 2.0 + 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 + JCL 1.1.1 implemented over SLF4J under MIT License + JUnit under Common Public License Version 1.0 + SLF4J API Module under MIT License + SLF4J Simple Binding under MIT License + diff --git a/cas-client-support-distributed-ehcache/pom.xml b/cas-client-support-distributed-ehcache/pom.xml index 43c22fa50..b8e6db191 100644 --- a/cas-client-support-distributed-ehcache/pom.xml +++ b/cas-client-support-distributed-ehcache/pom.xml @@ -1,44 +1,32 @@ - - - cas-client - org.jasig.cas.client - 3.3-SNAPSHOT - - 4.0.0 - Jasig CAS Client for Java - Distributed Proxy Storage Support: EhCache - + + + cas-client + org.jasig.cas.client + 3.4.1-SNAPSHOT + + 4.0.0 + Jasig CAS Client for Java - Distributed Proxy Storage Support: EhCache + - cas-client-support-distributed-ehcache - jar + cas-client-support-distributed-ehcache + jar - - - org.jasig.cas.client - cas-client-core - ${project.version} - jar - compile - - - - - net.sf.ehcache - ehcache-core - ${ehcache.version} - compile - jar - - - - org.slf4j - slf4j-api - 1.6.1 - test - - - + + + org.jasig.cas.client + cas-client-core + ${project.version} + jar + compile + + + net.sf.ehcache + ehcache-core + ${ehcache.version} + compile + jar + + \ No newline at end of file diff --git a/cas-client-support-distributed-ehcache/src/main/java/org/jasig/cas/client/proxy/EhcacheBackedProxyGrantingTicketStorageImpl.java b/cas-client-support-distributed-ehcache/src/main/java/org/jasig/cas/client/proxy/EhcacheBackedProxyGrantingTicketStorageImpl.java index 573129202..362a8289d 100644 --- a/cas-client-support-distributed-ehcache/src/main/java/org/jasig/cas/client/proxy/EhcacheBackedProxyGrantingTicketStorageImpl.java +++ b/cas-client-support-distributed-ehcache/src/main/java/org/jasig/cas/client/proxy/EhcacheBackedProxyGrantingTicketStorageImpl.java @@ -1,34 +1,29 @@ -/** +/* * 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: + * 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 + * 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 + * 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.proxy; import net.sf.ehcache.Cache; -import net.sf.ehcache.Element; import net.sf.ehcache.CacheManager; +import net.sf.ehcache.Element; import net.sf.ehcache.distribution.RemoteCacheException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author Scott Battaglia @@ -39,13 +34,13 @@ public final class EhcacheBackedProxyGrantingTicketStorageImpl extends AbstractE public static final String EHCACHE_CACHE_NAME = "org.jasig.cas.client.proxy.EhcacheBackedProxyGrantingTicketStorageImpl.cache"; - private static final Log log = LogFactory.getLog(EhcacheBackedProxyGrantingTicketStorageImpl.class); + private static final Logger logger = LoggerFactory.getLogger(EhcacheBackedProxyGrantingTicketStorageImpl.class); final Cache cache; public EhcacheBackedProxyGrantingTicketStorageImpl() { this(CacheManager.getInstance().getCache(EHCACHE_CACHE_NAME)); - log.info("Created cache with name: " + this.cache.getName()); + logger.info("Created cache with name: {}", this.cache.getName()); } public EhcacheBackedProxyGrantingTicketStorageImpl(final Cache cache) { @@ -58,7 +53,7 @@ public void saveInternal(final String proxyGrantingTicketIou, final String proxy try { this.cache.put(element); } catch (final RemoteCacheException e) { - log.warn("Exception accessing one of the remote servers: " + e.getMessage(), e); + logger.warn("Exception accessing one of the remote servers: {}", e.getMessage(), e); } } diff --git a/cas-client-support-distributed-ehcache/src/test/java/EhCacheBackedProxyGrantingTicketStorageImplTests.java b/cas-client-support-distributed-ehcache/src/test/java/EhCacheBackedProxyGrantingTicketStorageImplTests.java index ea5600377..045a313fe 100644 --- a/cas-client-support-distributed-ehcache/src/test/java/EhCacheBackedProxyGrantingTicketStorageImplTests.java +++ b/cas-client-support-distributed-ehcache/src/test/java/EhCacheBackedProxyGrantingTicketStorageImplTests.java @@ -1,7 +1,25 @@ +/* + * 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. + */ + import junit.framework.TestCase; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; -import net.sf.ehcache.Ehcache; import org.jasig.cas.client.proxy.EhcacheBackedProxyGrantingTicketStorageImpl; /** @@ -12,10 +30,11 @@ public class EhCacheBackedProxyGrantingTicketStorageImplTests extends TestCase { public void testEncryptionMechanisms() throws Exception { - final Cache ehcache = new Cache("name", 100,false, false, 500, 500); + final Cache ehcache = new Cache("name", 100, false, false, 500, 500); CacheManager.getInstance().addCache(ehcache); - final EhcacheBackedProxyGrantingTicketStorageImpl cache = new EhcacheBackedProxyGrantingTicketStorageImpl(ehcache); -// cache.setSecretKey("thismustbeatleast24charactersandcannotbelessthanthat1234"); + final EhcacheBackedProxyGrantingTicketStorageImpl cache = new EhcacheBackedProxyGrantingTicketStorageImpl( + ehcache); + // cache.setSecretKey("thismustbeatleast24charactersandcannotbelessthanthat1234"); assertNull(cache.retrieve(null)); assertNull(cache.retrieve("foobar")); diff --git a/cas-client-support-distributed-memcached/NOTICE b/cas-client-support-distributed-memcached/NOTICE new file mode 100644 index 000000000..ac2d7c0d7 --- /dev/null +++ b/cas-client-support-distributed-memcached/NOTICE @@ -0,0 +1,28 @@ +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. + +This project includes: + Jasig CAS Client for Java - Core under Apache License Version 2.0 + 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 + JCL 1.1.1 implemented over SLF4J under MIT License + JUnit under Common Public License Version 1.0 + SLF4J API Module under MIT License + SLF4J Simple Binding under MIT License + Spymemcached under The Apache Software License, Version 2.0 + diff --git a/cas-client-support-distributed-memcached/pom.xml b/cas-client-support-distributed-memcached/pom.xml index 833abc379..fcabc69c6 100644 --- a/cas-client-support-distributed-memcached/pom.xml +++ b/cas-client-support-distributed-memcached/pom.xml @@ -1,49 +1,32 @@ - - - cas-client - org.jasig.cas.client - 3.3-SNAPSHOT - - 4.0.0 - - org.jasig.cas.client - jar - cas-client-support-distributed-memcached - Jasig CAS Client for Java - Distributed Proxy Storage Support: - Memcached - + + + cas-client + org.jasig.cas.client + 3.4.1-SNAPSHOT + + 4.0.0 - - - org.jasig.cas.client - cas-client-core - ${project.version} - compile - jar - - - - - spy - spymemcached - 2.6 - jar - provided - - + org.jasig.cas.client + jar + cas-client-support-distributed-memcached + Jasig CAS Client for Java - Distributed Proxy Storage Support: + Memcached + - - - spy - Spy Repository - default - http://files.couchbase.com/maven2/ - - false - - - - \ No newline at end of file + + + org.jasig.cas.client + cas-client-core + ${project.version} + compile + jar + + + net.spy + spymemcached + 2.9.1 + jar + + + diff --git a/cas-client-support-distributed-memcached/src/main/java/org/jasig/cas/client/proxy/MemcachedBackedProxyGrantingTicketStorageImpl.java b/cas-client-support-distributed-memcached/src/main/java/org/jasig/cas/client/proxy/MemcachedBackedProxyGrantingTicketStorageImpl.java index 910eb8ec4..42cd8664b 100644 --- a/cas-client-support-distributed-memcached/src/main/java/org/jasig/cas/client/proxy/MemcachedBackedProxyGrantingTicketStorageImpl.java +++ b/cas-client-support-distributed-memcached/src/main/java/org/jasig/cas/client/proxy/MemcachedBackedProxyGrantingTicketStorageImpl.java @@ -1,31 +1,33 @@ -/** +/* * 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: + * 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 + * 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 + * 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.proxy; -import net.spy.memcached.MemcachedClient; - -import java.io.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Future; +import net.spy.memcached.MemcachedClient; +import net.spy.memcached.MemcachedClientIF; /** * Implementation of the {@link org.jasig.cas.client.proxy.ProxyGrantingTicketStorage} interface that is backed by @@ -35,9 +37,10 @@ * @version $Revision$ $Date$ * @since 3.1.9 */ -public final class MemcachedBackedProxyGrantingTicketStorageImpl extends AbstractEncryptedProxyGrantingTicketStorageImpl { +public final class MemcachedBackedProxyGrantingTicketStorageImpl extends + AbstractEncryptedProxyGrantingTicketStorageImpl { - private final MemcachedClient client; + protected final MemcachedClientIF client; /** * Default constructor reads from the /casclient_memcached_hosts.txt in the classpath. Each line should be a host:port @@ -48,7 +51,8 @@ public MemcachedBackedProxyGrantingTicketStorageImpl() { } protected static String[] getHostsFromClassPath() { - final InputStream inputStream = MemcachedBackedProxyGrantingTicketStorageImpl.class.getResourceAsStream("/cas/casclient_memcached_hosts.txt"); + final InputStream inputStream = MemcachedBackedProxyGrantingTicketStorageImpl.class + .getResourceAsStream("/cas/casclient_memcached_hosts.txt"); final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); final List hosts = new ArrayList(); @@ -91,7 +95,6 @@ public MemcachedBackedProxyGrantingTicketStorageImpl(final String[] hostnamesAnd } } - public void saveInternal(final String proxyGrantingTicketIou, final String proxyGrantingTicket) { handleSynchronousRequest(this.client.add(proxyGrantingTicketIou, 120, proxyGrantingTicket)); } diff --git a/cas-client-support-saml/NOTICE b/cas-client-support-saml/NOTICE new file mode 100644 index 000000000..bab9bec52 --- /dev/null +++ b/cas-client-support-saml/NOTICE @@ -0,0 +1,30 @@ +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. + +This project includes: + Jasig CAS Client for Java - Core under Apache License Version 2.0 + Jasig CAS Client for Java - SAML Protocol Support under Apache License Version 2.0 + Java Servlet API under CDDL + GPLv2 with classpath exception + JCL 1.1.1 implemented over SLF4J under MIT License + Joda-Time under Apache 2 + JUnit under Common Public License Version 1.0 + SLF4J API Module under MIT License + SLF4J Simple Binding under MIT License + spring-asm under The Apache Software License, Version 2.0 + spring-core under The Apache Software License, Version 2.0 + spring-test under The Apache Software License, Version 2.0 + diff --git a/cas-client-support-saml/pom.xml b/cas-client-support-saml/pom.xml new file mode 100644 index 000000000..26aef5b6c --- /dev/null +++ b/cas-client-support-saml/pom.xml @@ -0,0 +1,44 @@ + + + org.jasig.cas.client + 3.4.1-SNAPSHOT + cas-client + + 4.0.0 + org.jasig.cas.client + cas-client-support-saml + jar + Jasig CAS Client for Java - SAML Protocol Support + + + + org.jasig.cas.client + cas-client-core + ${project.version} + + + joda-time + joda-time + 2.7 + + + + + org.jasig.cas.client + cas-client-core + ${project.version} + test-jar + test + + + org.springframework + spring-test + test + + + org.springframework + spring-core + test + + + diff --git a/cas-client-support-saml/src/main/java/org/jasig/cas/client/authentication/Saml11AuthenticationFilter.java b/cas-client-support-saml/src/main/java/org/jasig/cas/client/authentication/Saml11AuthenticationFilter.java new file mode 100644 index 000000000..14cbd1cdc --- /dev/null +++ b/cas-client-support-saml/src/main/java/org/jasig/cas/client/authentication/Saml11AuthenticationFilter.java @@ -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.authentication; + +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. + *

    + * 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 + * @version $Revision$ $Date$ + */ +public class Saml11AuthenticationFilter extends AuthenticationFilter { + + public Saml11AuthenticationFilter() { + super(Protocol.SAML11); + } +} diff --git a/cas-client-support-saml/src/main/java/org/jasig/cas/client/util/SamlUtils.java b/cas-client-support-saml/src/main/java/org/jasig/cas/client/util/SamlUtils.java new file mode 100644 index 000000000..55b1bfe62 --- /dev/null +++ b/cas-client-support-saml/src/main/java/org/jasig/cas/client/util/SamlUtils.java @@ -0,0 +1,52 @@ +/* + * 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(); + } +} diff --git a/cas-client-support-saml/src/main/java/org/jasig/cas/client/validation/Saml11TicketValidationFilter.java b/cas-client-support-saml/src/main/java/org/jasig/cas/client/validation/Saml11TicketValidationFilter.java new file mode 100644 index 000000000..2d2e18f2c --- /dev/null +++ b/cas-client-support-saml/src/main/java/org/jasig/cas/client/validation/Saml11TicketValidationFilter.java @@ -0,0 +1,57 @@ +/* + * 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 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 TicketValidationFilter that can instanciate a SAML 1.1 Ticket Validator. + *

    + * 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() { + super(Protocol.SAML11); + } + + protected final TicketValidator getTicketValidator(final FilterConfig filterConfig) { + 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(), getSSLConfig()); + validator.setURLConnectionFactory(factory); + + validator.setEncoding(getString(ConfigurationKeys.ENCODING)); + return validator; + } +} diff --git a/cas-client-support-saml/src/main/java/org/jasig/cas/client/validation/Saml11TicketValidator.java b/cas-client-support-saml/src/main/java/org/jasig/cas/client/validation/Saml11TicketValidator.java new file mode 100644 index 000000000..4a5908198 --- /dev/null +++ b/cas-client-support-saml/src/main/java/org/jasig/cas/client/validation/Saml11TicketValidator.java @@ -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 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 principalAttributes = new HashMap(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 items = new ArrayList(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(); + } +} diff --git a/cas-client-support-saml/src/test/java/org/jasig/cas/client/util/SamlUtilsTest.java b/cas-client-support-saml/src/test/java/org/jasig/cas/client/util/SamlUtilsTest.java new file mode 100644 index 000000000..3958e24ac --- /dev/null +++ b/cas-client-support-saml/src/test/java/org/jasig/cas/client/util/SamlUtilsTest.java @@ -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.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")); + } +} \ No newline at end of file diff --git a/cas-client-support-saml/src/test/java/org/jasig/cas/client/validation/Saml11TicketValidationFilterTests.java b/cas-client-support-saml/src/test/java/org/jasig/cas/client/validation/Saml11TicketValidationFilterTests.java new file mode 100644 index 000000000..e4db12858 --- /dev/null +++ b/cas-client-support-saml/src/test/java/org/jasig/cas/client/validation/Saml11TicketValidationFilterTests.java @@ -0,0 +1,61 @@ +/* + * 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 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; + +/** + * Unit test for {@link Saml11TicketValidationFilter}. + * + * @author Marvin S. Addison + */ +public class Saml11TicketValidationFilterTests { + @Test + public void testRenewInitParamThrows() throws Exception { + final Saml11TicketValidationFilter f = new Saml11TicketValidationFilter(); + final MockFilterConfig config = new MockFilterConfig(); + config.addInitParameter("casServerUrlPrefix", "https://cas.example.com"); + config.addInitParameter("renew", "true"); + try { + f.init(config); + fail("Should have thrown IllegalArgumentException."); + } catch (final IllegalArgumentException e) { + assertTrue(e.getMessage().contains("Renew MUST")); + } + } + + @Test + public void testAllowsRenewContextParam() throws Exception { + final Saml11TicketValidationFilter f = new Saml11TicketValidationFilter(); + final MockServletContext context = new MockServletContext(); + context.addInitParameter("casServerUrlPrefix", "https://cas.example.com"); + context.addInitParameter("renew", "true"); + 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()); + } +} \ No newline at end of file diff --git a/cas-client-support-saml/src/test/java/org/jasig/cas/client/validation/Saml11TicketValidatorTests.java b/cas-client-support-saml/src/test/java/org/jasig/cas/client/validation/Saml11TicketValidatorTests.java new file mode 100644 index 000000000..8d9080023 --- /dev/null +++ b/cas-client-support-saml/src/test/java/org/jasig/cas/client/validation/Saml11TicketValidatorTests.java @@ -0,0 +1,157 @@ +/* + * 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 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.SamlUtils; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.Interval; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +/** + * @author Scott Battaglia + * @version $Revision$ $Date$ + * @since 3.1.3 + */ +@Ignore +public final class Saml11TicketValidatorTests extends AbstractTicketValidatorTests { + + private Saml11TicketValidator validator; + + private static final PublicTestHttpServer server = PublicTestHttpServer.instance(9051); + + @Before + public void setUp() throws Exception { + this.validator = new Saml11TicketValidator(AbstractTicketValidatorTests.CONST_CAS_SERVER_URL_PREFIX + "9051"); + this.validator.setTolerance(1000L); + } + + /*@AfterClass + public static void cleanUp() throws Exception { + server.shutdown(); + }*/ + + @Test + public void testCompatibilityValidationFailedResponse() throws UnsupportedEncodingException { + final String RESPONSE = "Success"; + + server.content = RESPONSE.getBytes(server.encoding); + try { + this.validator.validate("test", "test"); + fail("ValidationException expected due to 'no' response"); + } catch (final TicketValidationException e) { + // expected + } + } + + @Test + public void testCompatibilityValidationSuccessWithNoAttributes() throws UnsupportedEncodingException { + final Interval range = currentTimeRangeInterval(); + final Date now = new Date(); + final String RESPONSE = "testtestPrincipalurn:oasis:names:tc:SAML:1.0:cm:artifact"; + server.content = RESPONSE.getBytes(server.encoding); + try { + final Assertion a = this.validator.validate("test", "test"); + assertEquals("testPrincipal", a.getPrincipal().getName()); + } catch (final TicketValidationException e) { + fail(e.toString()); + } + } + + @Test + public void openSaml2GeneratedResponse() throws UnsupportedEncodingException { + final Interval range = currentTimeRangeInterval(); + final Date now = new Date(); + + final String response = "" + + "" + + "" + + "" + + "" + + "https://example.com/test-client/secure/" + + "" + + "" + + "testPrincipal" + + "urn:oasis:names:tc:SAML:1.0:cm:artifact" + + "" + + "testPrincipal" + + "urn:oasis:names:tc:SAML:1.0:cm:artifact" + + "12345" + + "" + + "ACTIVE" + + "" + + "employee" + + "staff" + + "student"; + + 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()); + } + } + + private Interval currentTimeRangeInterval() { + return new Interval(new DateTime(DateTimeZone.UTC).minus(5000), new DateTime(DateTimeZone.UTC).plus(200000000)); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8cb137f0e..ac95973c2 100644 --- a/pom.xml +++ b/pom.xml @@ -1,207 +1,263 @@ - - - org.jasig.parent - jasig-parent - 31 - - 4.0.0 - org.jasig.cas.client - 3.3-SNAPSHOT - cas-client - pom + + + org.jasig.parent + jasig-parent + 39 + + 4.0.0 + org.jasig.cas.client + 3.4.1-SNAPSHOT + cas-client + pom - Jasig CAS Client for Java - Jasig CAS Client for Java is the integration point for - applications that want to speak with a CAS - server, either via the CAS 1.0 or CAS 2.0 protocol. - - http://www.jasig.org/cas + Jasig CAS Client for Java + + Jasig CAS Client for Java is the integration point for applications that want to speak with a CAS + server, either via the CAS 1.0 or CAS 2.0 protocol. + + http://www.jasig.org/cas - - JIRA - https://issues.jasig.org/browse/CASC - + + JIRA + https://issues.jasig.org/browse/CASC + - - scm:git:git@github.com:Jasig/java-cas-client.git - scm:git:git@github.com:Jasig/java-cas-client.git - https://github.com/Jasig/java-cas-client + + scm:git:git@github.com:Jasig/java-cas-client.git + scm:git:git@github.com:Jasig/java-cas-client.git + https://github.com/Jasig/java-cas-client + HEAD - 2006 + 2006 - - - battags - Scott Battaglia - scott.battaglia@gmail.com - http://www.scottbattaglia.com - - Project Admin - Developer - - -5 - - - serac - Marvin S. Addison - marvin.addison@gmail.com - - Developer - - -5 - - + + + battags + Scott Battaglia + scott.battaglia@gmail.com + http://www.scottbattaglia.com + + Project Admin + Developer + + -5 + + + serac + Marvin S. Addison + marvin.addison@gmail.com + + Developer + + -5 + + - - Jasig - http://www.jasig.org - + + Jasig + http://www.jasig.org + - - - - org.apache.maven.plugins - maven-assembly-plugin - 2.2-beta-1 - - - ${basedir}/assembly.xml - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.5 - 1.5 - - + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.2-beta-1 + + + ${basedir}/assembly.xml + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.0 + + 1.5 + 1.5 + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.6 + + + **/*Tests* + + false + 1 + + + + maven-source-plugin + 2.2.1 + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 1.0 + + + enforce-banned-dependencies + + enforce + + + + + + commons-logging + + + + true + + + + + + com.mycila.maven-license-plugin + maven-license-plugin + +
    src/licensing/header.txt
    + true + + src/licensing/header-definitions.xml + + true + + **/.idea/** + LICENSE + **/INSTALL* + **/NOTICE* + **/README* + **/readme* + **/*.log + **/*.license + **/*.txt + **/*.crt + **/*.crl + **/*.key + **/.gitignore + **/overlays/** + src/licensing/** + +
    +
    +
    +
    - - org.apache.maven.plugins - maven-surefire-plugin - 2.6 - - - **/*Tests* - - false - 1 - - - - maven-source-plugin - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-release-plugin - 2.2.1 - - forked-path - v@{project.version} - - - -
    -
    + + org.springframework + spring-test + ${spring.version} + test + - - - junit - junit - 4.8.2 - test - - - commons-logging - commons-logging - 1.1 - compile - - - log4j - log4j - - - logkit - logkit - - - avalon-framework - avalon-framework - - - - - javax.servlet - servlet-api - 2.4 - provided - - + + log4j + log4j + test + 1.2.15 + + + jmxri + com.sun.jmx + + + com.sun.jdmk + jmxtools + + + javax.jms + jms + + + + + - - cas-client-core - cas-client-integration-atlassian - cas-client-integration-jboss - cas-client-support-distributed-ehcache - cas-client-support-distributed-memcached - cas-client-integration-tomcat-common - cas-client-integration-tomcat-v6 - cas-client-integration-tomcat-v7 - + + + junit + junit + 4.8.2 + test + + + org.slf4j + slf4j-api + ${slf4j.version} + compile + + + javax.servlet + javax.servlet-api + 3.0.1 + provided + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + test + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + - - 2.2.0 - 3.0.2 - + + cas-client-core + cas-client-integration-atlassian + cas-client-integration-jboss + cas-client-support-distributed-ehcache + cas-client-support-distributed-memcached + cas-client-support-saml + cas-client-integration-tomcat-common + cas-client-integration-tomcat-v6 + cas-client-integration-tomcat-v7 + + + + 3.1.3.RELEASE + 2.2.0 + 3.0.2 + 1.7.1 +
    diff --git a/src/licensing/header-definitions.xml b/src/licensing/header-definitions.xml new file mode 100644 index 000000000..39fb559bc --- /dev/null +++ b/src/licensing/header-definitions.xml @@ -0,0 +1,12 @@ + + + + /* + * + */ + ( |\t)*/\*( |\t)*$ + ( |\t)*\*/( |\t)*$ + true + true + + \ No newline at end of file diff --git a/src/licensing/header.txt b/src/licensing/header.txt new file mode 100644 index 000000000..81fb4278b --- /dev/null +++ b/src/licensing/header.txt @@ -0,0 +1,16 @@ +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. \ No newline at end of file diff --git a/travis/deploy-to-sonatype.sh b/travis/deploy-to-sonatype.sh new file mode 100644 index 000000000..80f2154e3 --- /dev/null +++ b/travis/deploy-to-sonatype.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Only invoke the deployment to Sonatype when it's not a PR and only for master +if [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_BRANCH" == "master" ]; then + mvn deploy --settings travis/settings.xml + echo -e "Successfully deployed SNAPSHOT artifacts to Sonatype under Travis job ${TRAVIS_JOB_NUMBER}" +fi diff --git a/travis/settings.xml b/travis/settings.xml new file mode 100644 index 000000000..de99f7713 --- /dev/null +++ b/travis/settings.xml @@ -0,0 +1,21 @@ + + + + + + + + + sonatype-nexus-snapshots + ${env.SONATYPE_USER} + ${env.SONATYPE_PWD} + + + + + + +