Compare commits

..

7 Commits

Author SHA1 Message Date
Scott Battaglia 54a977a98d [maven-release-plugin] prepare release cas-client-3.2.2 2014-09-05 23:54:44 -04:00
Scott Battaglia ec5bbb02b8 Corrected scm information 2014-09-05 23:53:22 -04:00
Scott Battaglia 4a9998102f Set version for 3.2.2-SNAPSHOT 2014-09-05 23:44:49 -04:00
Scott Battaglia 32772f70c3 Prepare for a 3.2.2 release 2014-09-05 23:36:19 -04:00
Scott a1a9630f5d Merge pull request #82 from serac/CASC-228-3.2.x
CAS-228 URL Encoding Fix for 3.2.x branch
2014-09-01 22:59:50 -04:00
Scott Battaglia 8afb55e2b2 CASC-228 URL Encode Paramaters Passed to Server via Validate
Problem: We currently don't pass encoded values to the server, possibly resolving in parsing/extraction errors.
Solution: URL Encode all values instead of just the service url.

QA Notes: Added unit test.

Conflicts:
	cas-client-core/src/main/java/org/jasig/cas/client/validation/Cas20ServiceTicketValidator.java
	cas-client-core/src/test/java/org/jasig/cas/client/validation/Cas10TicketValidatorTests.java
2014-08-29 09:18:54 -04:00
Scott Battaglia db5963cf56 [maven-release-plugin] copy for tag cas-client-3.2.1 2011-07-10 23:44:02 +00:00
285 changed files with 4099 additions and 20429 deletions

20
.github/FUNDING.yml vendored
View File

@ -1,20 +0,0 @@
#
# 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.
#
custom: ['https://www.apereo.org/content/apereo-membership']

11
.github/renovate.json vendored
View File

@ -1,11 +0,0 @@
{
"extends": [
"config:base",
":preserveSemverRanges",
":rebaseStalePrs",
":disableRateLimiting",
":semanticCommits",
":semanticCommitTypeAll(renovatebot)"
],
"labels": ["dependencies", "bot"]
}

73
.github/stale.yml vendored
View File

@ -1,73 +0,0 @@
#
# 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.
#
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 7
# Number of days of inactivity before a stale Issue or Pull Request is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 7
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: false
# Label to use when marking as stale
staleLabel: Pending
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This patch has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when removing the stale label.
# unmarkComment: >
# Your comment here.
# Comment to post when closing a stale Issue or Pull Request.
closeComment: >
This patch has been automatically closed because it has not had
recent activity. If you wish to resume work, please re-open the pull request
and continue as usual. Thank you for your contributions.
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30
# Limit to only `issues` or `pulls`
# only: pulls
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
# pulls:
# daysUntilStale: 30
# markComment: >
# This pull request has been automatically marked as stale because it has not had
# recent activity. It will be closed if no further activity occurs. Thank you
# for your contributions.
# issues:
# exemptLabels:
# - confirmed

13
.gitignore vendored
View File

@ -1,12 +1,3 @@
.classpath
!/.project
.project
.settings
bin
target
*.ipr
*.iml
*.iws
**/target
.idea/
.DS_Store
.idea
*.iml

View File

@ -1,44 +0,0 @@
#
# 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.
#
pull_request_rules:
- name: automatic merge by dependabot
conditions:
- status-success=continuous-integration/travis-ci/pr
- status-success=WIP
- "#changes-requested-reviews-by=0"
- base=master
- label=dependencies
actions:
merge:
method: squash
strict: false
delete_head_branch:
- name: automatic merge by renovate
conditions:
- status-success=continuous-integration/travis-ci/pr
- status-success=WIP
- "#changes-requested-reviews-by=0"
- base=master
- label=dependencies
actions:
merge:
method: squash
strict: false
delete_head_branch:

View File

@ -1,38 +0,0 @@
#
# Licensed to Jasig under one or more contributor license
# agreements. See the NOTICE file distributed with this work
# for additional information regarding copyright ownership.
# Jasig licenses this file to you under the Apache License,
# Version 2.0 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a
# copy of the License at the following location:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
language: java
sudo: required
branches:
only:
- master
cache:
directories:
- "$HOME/.m2/repository"
script: "mvn install --settings travis/settings.xml"
jdk:
- openjdk8
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

101
NOTICE
View File

@ -1,101 +0,0 @@
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:
Apache Commons Codec under Apache License, Version 2.0
Apache Log4j under The Apache Software License, Version 2.0
Apache Log4j API under Apache License, Version 2.0
Apache Log4j to SLF4J Adapter under Apache License, Version 2.0
Apache XML Security under The Apache Software License, Version 2.0
Apereo CAS Client for Java under Apache License Version 2.0
asm under BSD
asm-analysis under BSD
asm-commons under BSD
asm-tree under BSD
Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs under Bouncy Castle Licence
Bouncy Castle Provider under Bouncy Castle Licence
catalina under Apache License, Version 2.0
coyote under Apache License, Version 2.0
Eclipse Compiler for Java(TM) under Eclipse Public License - v 2.0
Ehcache Core under The Apache Software License, Version 2.0
Hamcrest Core under New BSD License
Jackson-annotations under The Apache Software License, Version 2.0
Jackson-core under The Apache Software License, Version 2.0
jackson-databind under The Apache Software 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: EhCache 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 - JBoss Integration under Apache License Version 2.0
Jasig CAS Client for Java - Jetty Container 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 - Spring Boot 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
Jasig CAS Client for Java - Tomcat 8.5.x Integration under Apache License Version 2.0
Jasig CAS Client for Java - Tomcat 8.x Integration under Apache License Version 2.0
Jasig CAS Client for Java - Tomcat 9.0.x Integration under Apache License Version 2.0
Java Servlet API under CDDL + GPLv2 with classpath exception
javax.annotation API under CDDL + GPLv2 with classpath exception
JBoss Application Server Tomcat under lgpl
JCL 1.2 implemented over SLF4J under MIT License
Jetty :: Apache JSP Implementation under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
Jetty :: Http Utility under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
Jetty :: IO Utility under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
Jetty :: JNDI Naming under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
Jetty :: Plus under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
Jetty :: Schemas under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
Jetty :: Security under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
Jetty :: Server Core under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
Jetty :: Servlet Annotations under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
Jetty :: Servlet Handling under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
Jetty :: Utilities under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
Jetty :: Webapp Application Support under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
Jetty :: XML utilities under Apache Software License - Version 2.0 or Eclipse Public License - Version 1.0
Joda-Time under Apache License, Version 2.0
JUL to SLF4J bridge under MIT License
JUnit under Eclipse Public License 1.0
Logback Classic Module under Eclipse Public License - v 1.0 or GNU Lesser General Public License
Logback Core Module under Eclipse Public License - v 1.0 or GNU Lesser General Public License
MortBay :: Apache EL :: API and Implementation under Apache License Version 2.0
MortBay :: Apache Jasper :: JSP Implementation under Apache License Version 2.0
SLF4J API Module under MIT License
SLF4J Simple Binding under MIT License
SnakeYAML under Apache License, Version 2.0
Spring AOP under Apache License, Version 2.0
Spring Beans under Apache License, Version 2.0
Spring Boot under Apache License, Version 2.0
Spring Boot AutoConfigure under Apache License, Version 2.0
Spring Boot Logging Starter under Apache License, Version 2.0
Spring Boot Starter under Apache License, Version 2.0
Spring Commons Logging Bridge under Apache License, Version 2.0
Spring Context under Apache License, Version 2.0
Spring Core under Apache License, Version 2.0
Spring Expression Language (SpEL) under Apache License, Version 2.0
Spring TestContext Framework under Apache License, Version 2.0
Spring Web under Apache License, Version 2.0
Spymemcached under The Apache Software License, Version 2.0
tomcat-annotations-api under Apache License, Version 2.0
tomcat-catalina under Apache License, Version 2.0
tomcat-coyote under Apache License, Version 2.0
tomcat-el-api under Apache License, Version 2.0
tomcat-embed-core under Apache License, Version 2.0
tomcat-jaspic-api under Apache License, Version 2.0
tomcat-jni under Apache License, Version 2.0
tomcat-jsp-api under Apache License, Version 2.0
tomcat-util-scan under Apache License, Version 2.0

1504
README.md

File diff suppressed because it is too large Load Diff

View File

@ -1,84 +1,85 @@
<!--
Licensed to Apereo under one or more contributor license
Licensed to Jasig 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,
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:
except in compliance with the License. You may obtain a
copy of the License at:
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.
-->
<assembly>
<id>release</id>
<formats>
<format>zip</format>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<lineEnding>unix</lineEnding>
<useDefaultExcludes>true</useDefaultExcludes>
<useStrictFiltering>false</useStrictFiltering>
<directory>${basedir}</directory>
<outputDirectory></outputDirectory>
<includes>
<include>*.xml</include>
<include>*.txt</include>
</includes>
</fileSet>
</fileSets>
<moduleSets>
<moduleSet>
<includes></includes>
<sources>
<fileSets>
<fileSet>
<directory>src</directory>
<outputDirectory>src</outputDirectory>
<lineEnding>unix</lineEnding>
<useDefaultExcludes>true</useDefaultExcludes>
</fileSet>
<id>release</id>
<formats>
<format>zip</format>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<lineEnding>unix</lineEnding>
<useDefaultExcludes>true</useDefaultExcludes>
<useStrictFiltering>false</useStrictFiltering>
<directory>${basedir}</directory>
<outputDirectory></outputDirectory>
<includes>
<include>*.xml</include>
<include>*.txt</include>
</includes>
</fileSet>
</fileSets>
<moduleSets>
<moduleSet>
<includes></includes>
<sources>
<fileSets>
<fileSet>
<directory>src</directory>
<outputDirectory>src</outputDirectory>
<lineEnding>unix</lineEnding>
<useDefaultExcludes>true</useDefaultExcludes>
</fileSet>
<fileSet>
<lineEnding>unix</lineEnding>
<useDefaultExcludes>true</useDefaultExcludes>
<includes>
<include>*.xml</include>
</includes>
</fileSet>
<fileSet>
<lineEnding>unix</lineEnding>
<useDefaultExcludes>true</useDefaultExcludes>
<includes>
<include>*.xml</include>
</includes>
</fileSet>
<fileSet>
<lineEnding>unix</lineEnding>
<directory>target/site/apidocs/</directory>
<useDefaultExcludes>true</useDefaultExcludes>
<outputDirectory>docs</outputDirectory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
</fileSets>
<fileSet>
<lineEnding>unix</lineEnding>
<directory>target/site/apidocs/</directory>
<useDefaultExcludes>true</useDefaultExcludes>
<outputDirectory>docs</outputDirectory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
</fileSets>
<includeModuleDirectory>true</includeModuleDirectory>
<useDefaultExcludes>true</useDefaultExcludes>
</sources>
<binaries>
<outputDirectory>modules</outputDirectory>
<includeDependencies>true</includeDependencies>
<unpack>false</unpack>
<useDefaultExcludes>true</useDefaultExcludes>
<includes/>
</binaries>
</moduleSet>
</moduleSets>
<includeModuleDirectory>true</includeModuleDirectory>
<useDefaultExcludes>true</useDefaultExcludes>
</sources>
<binaries>
<outputDirectory>modules</outputDirectory>
<includeDependencies>true</includeDependencies>
<unpack>false</unpack>
<useDefaultExcludes>true</useDefaultExcludes>
<includes />
</binaries>
</moduleSet>
</moduleSets>
</assembly>

View File

@ -1,18 +1,18 @@
====
Licensed to Apereo under one or more contributor license
Licensed to Jasig 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,
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:
except in compliance with the License. You may obtain a
copy of the License at:
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.
====

View File

@ -1,42 +0,0 @@
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:
Apache Commons Codec under Apache License, Version 2.0
Apache Log4j under The Apache Software License, Version 2.0
Apache XML Security under The Apache Software License, Version 2.0
Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs under Bouncy Castle Licence
Bouncy Castle Provider under Bouncy Castle Licence
Hamcrest Core under New BSD License
Jackson-annotations under The Apache Software License, Version 2.0
Jackson-core under The Apache Software License, Version 2.0
jackson-databind 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
JCL 1.2 implemented over SLF4J under MIT License
JUnit under Eclipse Public License 1.0
SLF4J API Module under MIT License
SLF4J Simple Binding under MIT License
Spring AOP under Apache License, Version 2.0
Spring Beans under Apache License, Version 2.0
Spring Commons Logging Bridge under Apache License, Version 2.0
Spring Context under Apache License, Version 2.0
Spring Core under Apache License, Version 2.0
Spring Expression Language (SpEL) under Apache License, Version 2.0
Spring TestContext Framework under Apache License, Version 2.0
Spring Web under Apache License, Version 2.0

View File

@ -1,101 +1,74 @@
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.jasig.cas.client</groupId>
<version>3.6.2-SNAPSHOT</version>
<version>3.2.2</version>
<artifactId>cas-client</artifactId>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<packaging>jar</packaging>
<name>Jasig CAS Client for Java - Core</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>xml-security</groupId>
<artifactId>xmlsec</artifactId>
<version>1.3.0</version>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml</artifactId>
<version>1.1</version>
<type>jar</type>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>xml-security</groupId>
<artifactId>xmlsec</artifactId>
<version>1.3.0</version>
<scope>runtime</scope>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.4</version>
<type>jar</type>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>test</scope>
<version>1.2.17</version>
<version>1.2.15</version>
<exclusions>
<exclusion>
<artifactId>jmxri</artifactId>
@ -113,4 +86,7 @@
</dependency>
</dependencies>
<properties>
<spring.version>2.5.6.SEC01</spring.version>
</properties>
</project>

View File

@ -1,51 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client;
/**
* Simple enumeration to hold/capture some of the standard request parameters used by the various protocols.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public enum Protocol {
CAS1("ticket", "service"), CAS2(CAS1), CAS3(CAS2), SAML11("SAMLart", "TARGET");
private final String artifactParameterName;
private final String serviceParameterName;
private Protocol(final String artifactParameterName, final String serviceParameterName) {
this.artifactParameterName = artifactParameterName;
this.serviceParameterName = serviceParameterName;
}
private Protocol(final Protocol protocol) {
this(protocol.getArtifactParameterName(), protocol.getServiceParameterName());
}
public String getArtifactParameterName() {
return this.artifactParameterName;
}
public String getServiceParameterName() {
return this.serviceParameterName;
}
}

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
@ -47,6 +48,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<String, Object> getAttributes();
Map<String,Object> getAttributes();
}

View File

@ -1,45 +1,48 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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 Logger LOGGER = LoggerFactory.getLogger(AttributePrincipalImpl.class);
private static final Log LOG = LogFactory.getLog(AttributePrincipalImpl.class);
/** Unique Id for Serialization */
private static final long serialVersionUID = -1443182634624927187L;
/** Map of key/value pairs about this principal. */
private final Map<String, Object> attributes;
private final Map<String,Object> attributes;
/** The CAS 2 ticket used to retrieve a proxy ticket. */
private final String proxyGrantingTicket;
@ -53,7 +56,7 @@ public class AttributePrincipalImpl extends SimplePrincipal implements Attribute
* @param name the unique identifier for the principal.
*/
public AttributePrincipalImpl(final String name) {
this(name, Collections.<String, Object> emptyMap());
this(name, Collections.<String, Object>emptyMap());
}
/**
@ -62,8 +65,8 @@ public class AttributePrincipalImpl extends SimplePrincipal implements Attribute
* @param name the unique identifier for the principal.
* @param attributes the key/value pairs for this principal.
*/
public AttributePrincipalImpl(final String name, final Map<String, Object> attributes) {
this(name, attributes, null, null);
public AttributePrincipalImpl(final String name, final Map<String,Object> attributes) {
this(name, attributes, null, null);
}
/**
@ -73,21 +76,19 @@ public class AttributePrincipalImpl extends SimplePrincipal implements Attribute
* @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.<String, Object> emptyMap(), proxyGrantingTicket, proxyRetriever);
public AttributePrincipalImpl(final String name, final String proxyGrantingTicket, final ProxyRetriever proxyRetriever) {
this(name, Collections.<String, Object>emptyMap(), proxyGrantingTicket, proxyRetriever);
}
/**
* Constructs a new principal with the supplied name, attributes, and proxying capabilities.
* Constructs a new principal witht he 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<String, Object> attributes,
final String proxyGrantingTicket, final ProxyRetriever proxyRetriever) {
public AttributePrincipalImpl(final String name, final Map<String,Object> attributes, final String proxyGrantingTicket, final ProxyRetriever proxyRetriever) {
super(name);
this.attributes = attributes;
this.proxyGrantingTicket = proxyGrantingTicket;
@ -96,18 +97,16 @@ public class AttributePrincipalImpl extends SimplePrincipal implements Attribute
CommonUtils.assertNotNull(this.attributes, "attributes cannot be null.");
}
@Override
public Map<String, Object> getAttributes() {
public Map<String,Object> getAttributes() {
return this.attributes;
}
@Override
public String getProxyTicketFor(final String service) {
public String getProxyTicketFor(String service) {
if (proxyGrantingTicket != null) {
return this.proxyRetriever.getProxyTicketIdFor(this.proxyGrantingTicket, service);
}
LOGGER.debug("No ProxyGrantingTicket was supplied, so no Proxy Ticket can be retrieved.");
LOG.debug("No ProxyGrantingTicket was supplied, so no Proxy Ticket can be retrieved.");
return null;
}
}

View File

@ -1,28 +1,26 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.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;
import javax.servlet.FilterChain;
@ -34,8 +32,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* Filter implementation to intercept all requests and attempt to authenticate
@ -46,16 +42,16 @@ import java.util.Map;
* <li><code>casServerLoginUrl</code> - the url to log into CAS, i.e. https://cas.rutgers.edu/login</li>
* <li><code>renew</code> - true/false on whether to use renew or not.</li>
* <li><code>gateway</code> - true/false on whether to use gateway or not.</li>
* <li><code>method</code> - the method used by the CAS server to send the user back to the application (redirect or post).</li>
* </ul>
*
* <p>Please see AbstractCasFilter for additional properties.</p>
*
* @author Scott Battaglia
* @author Misagh Moayyed
* @version $Revision: 11768 $ $Date: 2007-02-07 15:44:16 -0500 (Wed, 07 Feb 2007) $
* @since 3.0
*/
public class AuthenticationFilter extends AbstractCasFilter {
/**
* The URL to the CAS Server login.
*/
@ -70,122 +66,40 @@ public class AuthenticationFilter extends AbstractCasFilter {
* Whether to send the gateway request or not.
*/
private boolean gateway = false;
/**
* The method used by the CAS server to send the user back to the application.
*/
private String method;
private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();
private AuthenticationRedirectStrategy authenticationRedirectStrategy = new DefaultAuthenticationRedirectStrategy();
private UrlPatternMatcherStrategy ignoreUrlPatternMatcherStrategyClass = null;
private String internalIp = null;
private static final String X_REAL_IP = "x-real-ip";
private static final Map<String, Class<? extends UrlPatternMatcherStrategy>> PATTERN_MATCHER_TYPES =
new HashMap<String, Class<? extends UrlPatternMatcherStrategy>>();
static {
PATTERN_MATCHER_TYPES.put("CONTAINS", ContainsPatternUrlPatternMatcherStrategy.class);
PATTERN_MATCHER_TYPES.put("REGEX", RegexUrlPatternMatcherStrategy.class);
PATTERN_MATCHER_TYPES.put("FULL_REGEX", EntireRegionRegexUrlPatternMatcherStrategy.class);
PATTERN_MATCHER_TYPES.put("EXACT", ExactUrlPatternMatcherStrategy.class);
}
public AuthenticationFilter() {
this(Protocol.CAS2);
}
protected AuthenticationFilter(final Protocol protocol) {
super(protocol);
}
@Override
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 loginUrl = getString(ConfigurationKeys.CAS_SERVER_LOGIN_URL);
if (loginUrl != null) {
setCasServerLoginUrl(loginUrl);
} else {
setCasServerUrlPrefix(getString(ConfigurationKeys.CAS_SERVER_URL_PREFIX));
}
setRenew(getBoolean(ConfigurationKeys.RENEW));
setGateway(getBoolean(ConfigurationKeys.GATEWAY));
setMethod(getString(ConfigurationKeys.METHOD));
setInternalIp(getString(ConfigurationKeys.INTERNAL_IP));
final String ignorePattern = getString(ConfigurationKeys.IGNORE_PATTERN);
final String ignoreUrlPatternType = getString(ConfigurationKeys.IGNORE_URL_PATTERN_TYPE);
if (ignorePattern != null) {
final Class<? extends UrlPatternMatcherStrategy> ignoreUrlMatcherClass = PATTERN_MATCHER_TYPES.get(ignoreUrlPatternType);
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<? extends GatewayResolver> gatewayStorageClass = getClass(ConfigurationKeys.GATEWAY_STORAGE_CLASS);
final String gatewayStorageClass = getPropertyFromInitParams(filterConfig, "gatewayStorageClass", null);
if (gatewayStorageClass != null) {
setGatewayStorage(ReflectUtils.newInstance(gatewayStorageClass));
}
final Class<? extends AuthenticationRedirectStrategy> authenticationRedirectStrategyClass = getClass(ConfigurationKeys.AUTHENTICATION_REDIRECT_STRATEGY_CLASS);
if (authenticationRedirectStrategyClass != null) {
this.authenticationRedirectStrategy = ReflectUtils.newInstance(authenticationRedirectStrategyClass);
try {
this.gatewayStorage = (GatewayResolver) Class.forName(gatewayStorageClass).newInstance();
} catch (final Exception e) {
log.error(e,e);
throw new ServletException(e);
}
}
}
}
@Override
public void init() {
super.init();
final String message = String.format(
"one of %s and %s must not be null.",
ConfigurationKeys.CAS_SERVER_LOGIN_URL.getName(),
ConfigurationKeys.CAS_SERVER_URL_PREFIX.getName());
CommonUtils.assertNotNull(this.casServerLoginUrl, message);
CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
}
@Override
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 (isInternalRequest(request)) {
logger.debug("Request is ignored [internal].");
filterChain.doFilter(request, response);
return;
}
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;
@ -195,8 +109,8 @@ public class AuthenticationFilter extends AbstractCasFilter {
}
final String serviceUrl = constructServiceUrl(request, response);
final String ticket = retrieveTicketFromRequest(request);
final boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
final String ticket = CommonUtils.safeGetParameter(request,getArtifactParameterName());
final boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
filterChain.doFilter(request, response);
@ -205,21 +119,25 @@ public class AuthenticationFilter extends AbstractCasFilter {
final String modifiedServiceUrl;
logger.debug("no ticket and no assertion found");
log.debug("no ticket and no assertion found");
if (this.gateway) {
logger.debug("setting gateway attribute in session");
log.debug("setting gateway attribute in session");
modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
} else {
modifiedServiceUrl = serviceUrl;
}
logger.debug("Constructed service url: {}", modifiedServiceUrl);
if (log.isDebugEnabled()) {
log.debug("Constructed service url: " + modifiedServiceUrl);
}
final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl,
getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway, this.method);
final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
logger.debug("redirecting to \"{}\"", urlToRedirectTo);
this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);
if (log.isDebugEnabled()) {
log.debug("redirecting to \"" + urlToRedirectTo + "\"");
}
response.sendRedirect(urlToRedirectTo);
}
public final void setRenew(final boolean renew) {
@ -230,52 +148,11 @@ public class AuthenticationFilter extends AbstractCasFilter {
this.gateway = gateway;
}
public void setMethod(final String method) {
this.method = method;
}
public final void setCasServerUrlPrefix(final String casServerUrlPrefix) {
setCasServerLoginUrl(CommonUtils.addTrailingSlash(casServerUrlPrefix) + "login");
}
public final void setCasServerLoginUrl(final String casServerLoginUrl) {
this.casServerLoginUrl = casServerLoginUrl;
}
public void setInternalIp(String internalIp) {
this.internalIp = internalIp;
}
public final void setGatewayStorage(final GatewayResolver gatewayStorage) {
this.gatewayStorage = gatewayStorage;
this.gatewayStorage = gatewayStorage;
}
private boolean isInternalRequest(final HttpServletRequest request) {
if (this.internalIp == null) {
return false;
}
String realIp = request.getHeader(X_REAL_IP);
return this.internalIp.equals(realIp);
}
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);
}
public final void setIgnoreUrlPatternMatcherStrategyClass(
final UrlPatternMatcherStrategy ignoreUrlPatternMatcherStrategyClass) {
this.ignoreUrlPatternMatcherStrategyClass = ignoreUrlPatternMatcherStrategyClass;
}
}

View File

@ -1,46 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.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;
}

View File

@ -1,40 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.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;
@Override
public boolean matches(final String url) {
return url.contains(this.pattern);
}
@Override
public void setPattern(final String pattern) {
this.pattern = pattern;
}
}

View File

@ -1,38 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.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 {
@Override
public void redirect(final HttpServletRequest request, final HttpServletResponse response,
final String potentialRedirectUrl) throws IOException {
response.sendRedirect(potentialRedirectUrl);
}
}

View File

@ -1,45 +1,47 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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_";
@Override
public boolean hasGatewayedAlready(final HttpServletRequest request, final String serviceUrl) {
final HttpSession session = request.getSession(false);
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;
}
if (session == null) {
return false;
}
final boolean result = session.getAttribute(CONST_CAS_GATEWAY) != null;
return result;
}
@Override
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;
}
}

View File

@ -1,53 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.authentication;
import java.util.regex.Matcher;
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.
* The match behavior is based on {@link Matcher#matches()}:
* Attempts to match the entire region against the pattern.
*
* @author Misagh Moayyed
* @since 3.5
*/
public final class EntireRegionRegexUrlPatternMatcherStrategy implements UrlPatternMatcherStrategy {
private Pattern pattern;
public EntireRegionRegexUrlPatternMatcherStrategy() {
}
public EntireRegionRegexUrlPatternMatcherStrategy(final String pattern) {
this.setPattern(pattern);
}
@Override
public boolean matches(final String url) {
return this.pattern.matcher(url).matches();
}
@Override
public void setPattern(final String pattern) {
this.pattern = Pattern.compile(pattern);
}
}

View File

@ -1,48 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.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);
}
@Override
public boolean matches(final String url) {
return url.equals(this.pattern);
}
@Override
public void setPattern(final String pattern) {
this.pattern = pattern;
}
}

View File

@ -1,54 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.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";
@Override
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("<?xml version='1.0' encoding='UTF-8'?>");
writer.write(String.format("<partial-response><redirect url=\"%s\"></redirect></partial-response>",
potentialRedirectUrl));
} else {
response.sendRedirect(potentialRedirectUrl);
}
}
}

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
@ -31,21 +32,21 @@ import javax.servlet.http.HttpServletRequest;
*/
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);
}

View File

@ -1,57 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.authentication;
import java.util.regex.Matcher;
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.
* The match behavior is based on {@link Matcher#find()}:
* Attempts to find the next subsequence of the input sequence that matches
* the pattern. This method starts at the beginning of this matcher's region, or, if
* a previous invocation of the method was successful and the matcher has
* not since been reset, at the first character not matched by the previous
* match.
*
* @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);
}
@Override
public boolean matches(final String url) {
return this.pattern.matcher(url).find();
}
@Override
public void setPattern(final String pattern) {
this.pattern = Pattern.compile(pattern);
}
}

View File

@ -0,0 +1,45 @@
/**
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.
* <p>
* Note, the "final" on this class helps ensure the compliance required in the initInternal method.
*
* @author Scott Battaglia
* @since 3.1.12
* @version $Revision$ $Date$
*/
public final class Saml11AuthenticationFilter extends AuthenticationFilter {
protected 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");
}
}

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
@ -49,26 +50,22 @@ public final class SimpleGroup extends SimplePrincipal implements Group {
super(name);
}
@Override
public boolean addMember(final Principal user) {
return this.members.add(user);
}
@Override
public boolean isMember(final Principal member) {
return this.members.contains(member);
}
@Override
public Enumeration<? extends Principal> members() {
return Collections.enumeration(this.members);
}
@Override
public boolean removeMember(final Principal user) {
return this.members.remove(user);
}
public String toString() {
return super.toString() + ": " + members.toString();
}

View File

@ -1,25 +1,27 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
/**
@ -47,7 +49,6 @@ public class SimplePrincipal implements Principal, Serializable {
CommonUtils.assertNotNull(this.name, "name cannot be null.");
}
@Override
public final String getName() {
return this.name;
}
@ -62,7 +63,7 @@ public class SimplePrincipal implements Principal, Serializable {
} else if (!(o instanceof SimplePrincipal)) {
return false;
} else {
return getName().equals(((SimplePrincipal) o).getName());
return getName().equals(((SimplePrincipal)o).getName());
}
}

View File

@ -1,42 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.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);
}

View File

@ -1,121 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.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());
@Override
public final boolean getBoolean(final ConfigurationKey<Boolean> configurationKey) {
return getValue(configurationKey, new Parser<Boolean>() {
@Override
public Boolean parse(final String value) {
return CommonUtils.toBoolean(value);
}
});
}
@Override
public final long getLong(final ConfigurationKey<Long> configurationKey) {
return getValue(configurationKey, new Parser<Long>() {
@Override
public Long parse(final String value) {
return CommonUtils.toLong(value, configurationKey.getDefaultValue());
}
});
}
@Override
public final int getInt(final ConfigurationKey<Integer> configurationKey) {
return getValue(configurationKey, new Parser<Integer>() {
@Override
public Integer parse(final String value) {
return CommonUtils.toInt(value, configurationKey.getDefaultValue());
}
});
}
@Override
public final String getString(final ConfigurationKey<String> configurationKey) {
return getValue(configurationKey, new Parser<String>() {
@Override
public String parse(final String value) {
return value;
}
});
}
@Override
public <T> Class<? extends T> getClass(final ConfigurationKey<Class<? extends T>> configurationKey) {
return getValue(configurationKey, new Parser<Class<? extends T>>() {
@Override
public Class<? extends T> parse(final String value) {
try {
return ReflectUtils.loadClass(value);
} catch (final IllegalArgumentException e) {
return configurationKey.getDefaultValue();
}
}
});
}
private <T> T getValue(final ConfigurationKey<T> configurationKey, final Parser<T> parser) {
final String value = getWithCheck(configurationKey);
if (CommonUtils.isBlank(value)) {
logger.trace("No value found for property {}, returning default {}", configurationKey.getName(), configurationKey.getDefaultValue());
return configurationKey.getDefaultValue();
} else {
logger.trace("Loaded property {} with value {}", configurationKey.getName(), configurationKey.getDefaultValue());
}
return parser.parse(value);
}
private String getWithCheck(final ConfigurationKey configurationKey) {
CommonUtils.assertNotNull(configurationKey, "configurationKey cannot be null");
return get(configurationKey);
}
/**
* Retrieve the String value for this key. Returns null if there is no value.
*
* @param configurationKey the key to retrieve. MUST NOT BE NULL.
* @return the String if its found, null otherwise.
*/
protected abstract String get(ConfigurationKey configurationKey);
private interface Parser<T> {
T parse(String value);
}
}

View File

@ -1,68 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.configuration;
import org.jasig.cas.client.util.CommonUtils;
/**
* Holder class to represent a particular configuration key and its optional default value.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public final class ConfigurationKey<E> {
private final String name;
private final E defaultValue;
public ConfigurationKey(final String name) {
this(name, null);
}
public ConfigurationKey(final String name, final E defaultValue) {
CommonUtils.assertNotNull(name, "name must not be null.");
this.name = name;
this.defaultValue = defaultValue;
}
/**
* The referencing name of the configuration key (i.e. what you would use to look it up in your configuration strategy)
*
* @return the name. MUST NOT BE NULL.
*/
public String getName() {
return this.name;
}
/**
* The (optional) default value to use when this configuration key is not set. If a value is provided it should be used. A <code>null</code> value indicates that there is no default.
*
* @return the default value or null.
*/
public E getDefaultValue() {
return this.defaultValue;
}
@Override
public String toString() {
return getName();
}
}

View File

@ -1,86 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.configuration;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.authentication.AuthenticationRedirectStrategy;
import org.jasig.cas.client.authentication.DefaultGatewayResolverImpl;
import org.jasig.cas.client.authentication.GatewayResolver;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
import javax.net.ssl.HostnameVerifier;
/**
* Holder interface for all known configuration keys.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public interface ConfigurationKeys {
ConfigurationKey<String> ARTIFACT_PARAMETER_NAME = new ConfigurationKey<String>("artifactParameterName", Protocol.CAS2.getArtifactParameterName());
ConfigurationKey<String> SERVER_NAME = new ConfigurationKey<String>("serverName", null);
ConfigurationKey<String> SERVICE = new ConfigurationKey<String>("service");
ConfigurationKey<Boolean> RENEW = new ConfigurationKey<Boolean>("renew", Boolean.FALSE);
ConfigurationKey<String> LOGOUT_PARAMETER_NAME = new ConfigurationKey<String>("logoutParameterName", "logoutRequest");
ConfigurationKey<Boolean> ARTIFACT_PARAMETER_OVER_POST = new ConfigurationKey<Boolean>("artifactParameterOverPost", Boolean.FALSE);
ConfigurationKey<Boolean> EAGERLY_CREATE_SESSIONS = new ConfigurationKey<Boolean>("eagerlyCreateSessions", Boolean.TRUE);
ConfigurationKey<Boolean> ENCODE_SERVICE_URL = new ConfigurationKey<Boolean>("encodeServiceUrl", Boolean.TRUE);
ConfigurationKey<String> SSL_CONFIG_FILE = new ConfigurationKey<String>("sslConfigFile", null);
ConfigurationKey<String> ROLE_ATTRIBUTE = new ConfigurationKey<String>("roleAttribute", null);
ConfigurationKey<Boolean> IGNORE_CASE = new ConfigurationKey<Boolean>("ignoreCase", Boolean.FALSE);
ConfigurationKey<String> CAS_SERVER_LOGIN_URL = new ConfigurationKey<String>("casServerLoginUrl", null);
ConfigurationKey<Boolean> GATEWAY = new ConfigurationKey<Boolean>("gateway", Boolean.FALSE);
ConfigurationKey<String> METHOD = new ConfigurationKey<String>("method", null);
ConfigurationKey<Class<? extends AuthenticationRedirectStrategy>> AUTHENTICATION_REDIRECT_STRATEGY_CLASS = new ConfigurationKey<Class<? extends AuthenticationRedirectStrategy>>("authenticationRedirectStrategyClass", null);
ConfigurationKey<Class<? extends GatewayResolver>> GATEWAY_STORAGE_CLASS = new ConfigurationKey<Class<? extends GatewayResolver>>("gatewayStorageClass", DefaultGatewayResolverImpl.class);
ConfigurationKey<String> CAS_SERVER_URL_PREFIX = new ConfigurationKey<String>("casServerUrlPrefix", null);
ConfigurationKey<String> ENCODING = new ConfigurationKey<String>("encoding", null);
ConfigurationKey<Long> TOLERANCE = new ConfigurationKey<Long>("tolerance", 1000L);
ConfigurationKey<String> PRIVATE_KEY_PATH = new ConfigurationKey<String>("privateKeyPath", null);
ConfigurationKey<String> PRIVATE_KEY_ALGORITHM = new ConfigurationKey<String>("privateKeyAlgorithm", "RSA");
/**
* @deprecated As of 3.4. This constant is not used by the client and will
* be removed in future versions.
*/
@Deprecated
ConfigurationKey<Boolean> DISABLE_XML_SCHEMA_VALIDATION = new ConfigurationKey<Boolean>("disableXmlSchemaValidation", Boolean.FALSE);
ConfigurationKey<String> INTERNAL_IP = new ConfigurationKey<String>("internalIp", null);
ConfigurationKey<String> IGNORE_PATTERN = new ConfigurationKey<String>("ignorePattern", null);
ConfigurationKey<String> IGNORE_URL_PATTERN_TYPE = new ConfigurationKey<String>("ignoreUrlPatternType", "REGEX");
ConfigurationKey<Class<? extends HostnameVerifier>> HOSTNAME_VERIFIER = new ConfigurationKey<Class<? extends HostnameVerifier>>("hostnameVerifier", null);
ConfigurationKey<String> HOSTNAME_VERIFIER_CONFIG = new ConfigurationKey<String>("hostnameVerifierConfig", null);
ConfigurationKey<Boolean> EXCEPTION_ON_VALIDATION_FAILURE = new ConfigurationKey<Boolean>("exceptionOnValidationFailure", Boolean.TRUE);
ConfigurationKey<Boolean> REDIRECT_AFTER_VALIDATION = new ConfigurationKey<Boolean>("redirectAfterValidation", Boolean.TRUE);
ConfigurationKey<Boolean> USE_SESSION = new ConfigurationKey<Boolean>("useSession", Boolean.TRUE);
ConfigurationKey<String> SECRET_KEY = new ConfigurationKey<String>("secretKey", null);
ConfigurationKey<String> CIPHER_ALGORITHM = new ConfigurationKey<String>("cipherAlgorithm", "DESede");
ConfigurationKey<String> PROXY_RECEPTOR_URL = new ConfigurationKey<String>("proxyReceptorUrl", null);
ConfigurationKey<Class<? extends ProxyGrantingTicketStorage>> PROXY_GRANTING_TICKET_STORAGE_CLASS = new ConfigurationKey<Class<? extends ProxyGrantingTicketStorage>>("proxyGrantingTicketStorageClass", ProxyGrantingTicketStorageImpl.class);
ConfigurationKey<Integer> MILLIS_BETWEEN_CLEAN_UPS = new ConfigurationKey<Integer>("millisBetweenCleanUps", 60000);
ConfigurationKey<Boolean> ACCEPT_ANY_PROXY = new ConfigurationKey<Boolean>("acceptAnyProxy", Boolean.FALSE);
ConfigurationKey<String> ALLOWED_PROXY_CHAINS = new ConfigurationKey<String>("allowedProxyChains", null);
ConfigurationKey<Class<? extends Cas20ServiceTicketValidator>> TICKET_VALIDATOR_CLASS = new ConfigurationKey<Class<? extends Cas20ServiceTicketValidator>>("ticketValidatorClass", null);
ConfigurationKey<String> PROXY_CALLBACK_URL = new ConfigurationKey<String>("proxyCallbackUrl", null);
ConfigurationKey<String> RELAY_STATE_PARAMETER_NAME = new ConfigurationKey<String>("relayStateParameterName", "RelayState");
ConfigurationKey<String> LOGOUT_CALLBACK_PATH = new ConfigurationKey<String>("logoutCallbackPath", null);
}

View File

@ -1,79 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.configuration;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
/**
* Abstraction to allow for pluggable methods for retrieving filter configuration.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public interface ConfigurationStrategy {
/**
* Retrieves the value for the provided {@param configurationKey}, falling back to the {@param configurationKey}'s {@link ConfigurationKey#getDefaultValue()} if nothing can be found.
*
* @param configurationKey the configuration key. MUST NOT BE NULL.
* @return the configured value, or the default value.
*/
boolean getBoolean(ConfigurationKey<Boolean> configurationKey);
/**
* Retrieves the value for the provided {@param configurationKey}, falling back to the {@param configurationKey}'s {@link ConfigurationKey#getDefaultValue()} if nothing can be found.
*
* @param configurationKey the configuration key. MUST NOT BE NULL.
* @return the configured value, or the default value.
*/
String getString(ConfigurationKey<String> configurationKey);
/**
* Retrieves the value for the provided {@param configurationKey}, falling back to the {@param configurationKey}'s {@link ConfigurationKey#getDefaultValue()} if nothing can be found.
*
* @param configurationKey the configuration key. MUST NOT BE NULL.
* @return the configured value, or the default value.
*/
long getLong(ConfigurationKey<Long> configurationKey);
/**
* Retrieves the value for the provided {@param configurationKey}, falling back to the {@param configurationKey}'s {@link ConfigurationKey#getDefaultValue()} if nothing can be found.
*
* @param configurationKey the configuration key. MUST NOT BE NULL.
* @return the configured value, or the default value.
*/
int getInt(ConfigurationKey<Integer> configurationKey);
/**
* Retrieves the value for the provided {@param configurationKey}, falling back to the {@param configurationKey}'s {@link ConfigurationKey#getDefaultValue()} if nothing can be found.
*
* @param configurationKey the configuration key. MUST NOT BE NULL.
* @return the configured value, or the default value.
*/
<T> Class<? extends T> getClass(ConfigurationKey<Class<? extends T>> configurationKey);
/**
* Initializes the strategy. This must be called before calling any of the "get" methods.
*
* @param filterConfig the filter configuration object.
* @param filterClazz the filter
*/
void init(FilterConfig filterConfig, Class<? extends Filter> filterClazz);
}

View File

@ -1,74 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.configuration;
import org.jasig.cas.client.util.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Enumeration to map simple names to the underlying classes so that deployers can reference the simple name in the
* <code>web.xml</code> instead of the fully qualified class name.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public enum ConfigurationStrategyName {
DEFAULT(LegacyConfigurationStrategyImpl.class), JNDI(JndiConfigurationStrategyImpl.class), WEB_XML(WebXmlConfigurationStrategyImpl.class),
PROPERTY_FILE(PropertiesConfigurationStrategyImpl.class), SYSTEM_PROPERTIES(SystemPropertiesConfigurationStrategyImpl.class);
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationStrategyName.class);
private final Class<? extends ConfigurationStrategy> configurationStrategyClass;
private ConfigurationStrategyName(final Class<? extends ConfigurationStrategy> configurationStrategyClass) {
this.configurationStrategyClass = configurationStrategyClass;
}
/**
* Static helper method that will resolve a simple string to either an enum value or a {@link org.jasig.cas.client.configuration.ConfigurationStrategy} class.
*
* @param value the value to attempt to resolve.
* @return the underlying class that this maps to (either via simple name or fully qualified class name).
*/
public static Class<? extends ConfigurationStrategy> resolveToConfigurationStrategy(final String value) {
if (CommonUtils.isBlank(value)) {
return DEFAULT.configurationStrategyClass;
}
for (final ConfigurationStrategyName csn : values()) {
if (csn.name().equalsIgnoreCase(value)) {
return csn.configurationStrategyClass;
}
}
try {
final Class<?> clazz = Class.forName(value);
if (ConfigurationStrategy.class.isAssignableFrom(clazz)) {
return (Class<? extends ConfigurationStrategy>) clazz;
}
} catch (final ClassNotFoundException e) {
LOGGER.error("Unable to locate strategy {} by name or class name. Using default strategy instead.", value, e);
}
return DEFAULT.configurationStrategyClass;
}
}

View File

@ -1,94 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.configuration;
import org.jasig.cas.client.util.CommonUtils;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
/**
* Loads configuration information from JNDI, using the <code>defaultValue</code> if it can't.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public class JndiConfigurationStrategyImpl extends BaseConfigurationStrategy {
private static final String ENVIRONMENT_PREFIX = "java:comp/env/cas/";
private final String environmentPrefix;
private InitialContext context;
private String simpleFilterName;
public JndiConfigurationStrategyImpl() {
this(ENVIRONMENT_PREFIX);
}
public JndiConfigurationStrategyImpl(final String environmentPrefix) {
this.environmentPrefix = environmentPrefix;
}
@Override
protected final String get(final ConfigurationKey configurationKey) {
if (context == null) {
return null;
}
final String propertyName = configurationKey.getName();
final String filterValue = loadFromContext(context, this.environmentPrefix + this.simpleFilterName + "/" + propertyName);
if (CommonUtils.isNotBlank(filterValue)) {
logger.info("Property [{}] loaded from JNDI Filter Specific Property with value [{}]", propertyName, filterValue);
return filterValue;
}
final String rootValue = loadFromContext(context, this.environmentPrefix + propertyName);
if (CommonUtils.isNotBlank(rootValue)) {
logger.info("Property [{}] loaded from JNDI with value [{}]", propertyName, rootValue);
return rootValue;
}
return null;
}
private String loadFromContext(final InitialContext context, final String path) {
try {
return (String) context.lookup(path);
} catch (final NamingException e) {
return null;
}
}
@Override
public final void init(final FilterConfig filterConfig, final Class<? extends Filter> clazz) {
this.simpleFilterName = clazz.getSimpleName();
try {
this.context = new InitialContext();
} catch (final NamingException e) {
logger.error("Unable to create InitialContext. No properties can be loaded via JNDI.", e);
}
}
}

View File

@ -1,55 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.configuration;
import org.jasig.cas.client.util.CommonUtils;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
/**
* Replicates the original behavior by checking the {@link org.jasig.cas.client.configuration.WebXmlConfigurationStrategyImpl} first, and then
* the {@link org.jasig.cas.client.configuration.JndiConfigurationStrategyImpl} before using the <code>defaultValue</code>.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public final class LegacyConfigurationStrategyImpl extends BaseConfigurationStrategy {
private final WebXmlConfigurationStrategyImpl webXmlConfigurationStrategy = new WebXmlConfigurationStrategyImpl();
private final JndiConfigurationStrategyImpl jndiConfigurationStrategy = new JndiConfigurationStrategyImpl();
@Override
public void init(final FilterConfig filterConfig, final Class<? extends Filter> filterClazz) {
this.webXmlConfigurationStrategy.init(filterConfig, filterClazz);
this.jndiConfigurationStrategy.init(filterConfig, filterClazz);
}
@Override
protected String get(final ConfigurationKey key) {
final String value1 = this.webXmlConfigurationStrategy.get(key);
if (CommonUtils.isNotBlank(value1)) {
return value1;
}
return this.jndiConfigurationStrategy.get(key);
}
}

View File

@ -1,101 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.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.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 final 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);
}
@Override
public void init(final FilterConfig filterConfig, final Class<? extends Filter> filterClazz) {
this.simpleFilterName = filterClazz.getSimpleName();
final String fileLocationFromFilterConfig = filterConfig.getInitParameter(CONFIGURATION_FILE_LOCATION);
final boolean filterConfigFileLoad = loadPropertiesFromFile(fileLocationFromFilterConfig);
if (!filterConfigFileLoad) {
final String fileLocationFromServletConfig = filterConfig.getServletContext().getInitParameter(CONFIGURATION_FILE_LOCATION);
final boolean servletContextFileLoad = loadPropertiesFromFile(fileLocationFromServletConfig);
if (!servletContextFileLoad) {
final boolean defaultConfigFileLoaded = loadPropertiesFromFile(DEFAULT_CONFIGURATION_FILE_LOCATION);
CommonUtils.assertTrue(defaultConfigFileLoaded, "unable to load properties to configure CAS client");
}
}
}
private boolean loadPropertiesFromFile(final String file) {
if (CommonUtils.isEmpty(file)) {
return false;
}
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
this.properties.load(fis);
return true;
} catch (final IOException e) {
LOGGER.warn("Unable to load properties for file {}", file, e);
return false;
} finally {
CommonUtils.closeQuietly(fis);
}
}
}

View File

@ -1,40 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.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 {
@Override
public void init(final FilterConfig filterConfig, final Class<? extends Filter> filterClazz) {
}
@Override
protected String get(final ConfigurationKey configurationKey) {
return System.getProperty(configurationKey.getName());
}
}

View File

@ -1,62 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.configuration;
import org.jasig.cas.client.util.CommonUtils;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
/**
* Implementation of the {@link org.jasig.cas.client.configuration.ConfigurationStrategy} that first checks the {@link javax.servlet.FilterConfig} and
* then checks the {@link javax.servlet.ServletContext}, ultimately falling back to the <code>defaultValue</code>.
*
* @author Scott Battaglia
* @since 3.4.0
*/
public final class WebXmlConfigurationStrategyImpl extends BaseConfigurationStrategy {
private FilterConfig filterConfig;
@Override
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;
}
@Override
public void init(final FilterConfig filterConfig, final Class<? extends Filter> clazz) {
this.filterConfig = filterConfig;
}
}

View File

@ -1,24 +1,26 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
@ -31,12 +33,12 @@ import org.jasig.cas.client.validation.Assertion;
*
*/
public class AssertionPrincipal extends SimplePrincipal implements Serializable {
/** AssertionPrincipal.java */
private static final long serialVersionUID = 2288520214366461693L;
/** CAS assertion describing authenticated state */
private final Assertion assertion;
private Assertion assertion;
/**
* Creates a new principal containing the CAS assertion.

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
@ -26,19 +27,26 @@ import java.io.IOException;
import java.security.Principal;
import java.security.acl.Group;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.security.auth.Subject;
import javax.security.auth.callback.*;
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.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
@ -71,8 +79,6 @@ import org.slf4j.LoggerFactory;
* 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.</li>
* <li>cacheTimeout (optional) - Assertion cache timeout in minutes.</li>
* <li>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.</li>
* </ul>
*
* <p>
@ -103,14 +109,14 @@ import org.slf4j.LoggerFactory;
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
@ -123,50 +129,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<TicketCredential, Assertion> ASSERTION_CACHE = new HashMap<TicketCredential, Assertion>();
protected static final Map<TicketCredential,Assertion> ASSERTION_CACHE = new HashMap<TicketCredential,Assertion>();
/** Executor responsible for assertion cache cleanup */
protected static Executor cacheCleanerExecutor = Executors.newSingleThreadExecutor();
/** Logger instance */
protected final Logger logger = LoggerFactory.getLogger(getClass());
protected final Log log = LogFactory.getLog(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<String, Object> sharedState;
protected Map<String,Object> 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 final Set<String> roleAttributeNames = new HashSet<String>();
protected Set<String> roleAttributeNames = new HashSet<String>();
/** 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;
@ -176,12 +182,8 @@ 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.
@ -199,152 +201,113 @@ public class CasLoginModule implements LoginModule {
* which by default are single use, reauthentication fails. Assertion caching addresses this
* behavior.</li>
* <li>cacheTimeout (optional) - assertion cache timeout in minutes.</li>
* <li>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.</li>
* </ul>
*/
@Override
public final void initialize(final Subject subject, final CallbackHandler handler, final Map<String, ?> state,
final Map<String, ?> options) {
public void initialize(final Subject subject, final CallbackHandler handler, final Map<String,?> state, final Map<String, ?> options) {
this.assertion = null;
this.callbackHandler = handler;
this.subject = subject;
this.sharedState = (Map<String, Object>) state;
this.sharedState = new HashMap<String, Object>(state);
String ticketValidatorClass = null;
for (final String key : options.keySet()) {
logger.trace("Processing option {}", key);
log.trace("Processing option " + key);
if ("service".equals(key)) {
this.service = (String) options.get(key);
logger.debug("Set service={}", this.service);
log.debug("Set service=" + this.service);
} else if ("ticketValidatorClass".equals(key)) {
ticketValidatorClass = (String) options.get(key);
logger.debug("Set ticketValidatorClass={}", ticketValidatorClass);
log.debug("Set ticketValidatorClass=" + ticketValidatorClass);
} else if ("defaultRoles".equals(key)) {
final String roles = (String) options.get(key);
logger.trace("Got defaultRoles value {}", roles);
log.trace("Got defaultRoles value " + roles);
this.defaultRoles = roles.split(",\\s*");
logger.debug("Set defaultRoles={}", Arrays.asList(this.defaultRoles));
log.debug("Set defaultRoles=" + Arrays.asList(this.defaultRoles));
} else if ("roleAttributeNames".equals(key)) {
final String attrNames = (String) options.get(key);
logger.trace("Got roleAttributeNames value {}", attrNames);
log.trace("Got roleAttributeNames value " + attrNames);
final String[] attributes = attrNames.split(",\\s*");
this.roleAttributeNames.addAll(Arrays.asList(attributes));
logger.debug("Set roleAttributeNames={}", this.roleAttributeNames);
log.debug("Set roleAttributeNames=" + this.roleAttributeNames);
} else if ("principalGroupName".equals(key)) {
this.principalGroupName = (String) options.get(key);
logger.debug("Set principalGroupName={}", this.principalGroupName);
log.debug("Set principalGroupName=" + this.principalGroupName);
} else if ("roleGroupName".equals(key)) {
this.roleGroupName = (String) options.get(key);
logger.debug("Set roleGroupName={}", this.roleGroupName);
log.debug("Set roleGroupName=" + this.roleGroupName);
} else if ("cacheAssertions".equals(key)) {
this.cacheAssertions = Boolean.parseBoolean((String) options.get(key));
logger.debug("Set cacheAssertions={}", this.cacheAssertions);
log.debug("Set cacheAssertions=" + this.cacheAssertions);
} else if ("cacheTimeout".equals(key)) {
this.cacheTimeout = Integer.parseInt((String) options.get(key));
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);
log.debug("Set cacheTimeout=" + this.cacheTimeout);
}
}
if (this.cacheAssertions) {
cleanCache();
cacheCleanerExecutor.execute(new CacheCleaner());
}
CommonUtils.assertNotNull(ticketValidatorClass, "ticketValidatorClass is required.");
this.ticketValidator = createTicketValidator(ticketValidatorClass, options);
}
/**
* Operations to perform before doing login.
*
* @return true if you'd like login to continue, false otherwise.
*/
protected boolean preLogin() {
public boolean login() throws LoginException {
log.debug("Performing login.");
final NameCallback serviceCallback = new NameCallback("service");
final PasswordCallback ticketCallback = new PasswordCallback("ticket", false);
try {
this.callbackHandler.handle(new Callback[] { ticketCallback, serviceCallback });
} catch (final IOException e) {
log.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);
}
if (ticketCallback.getPassword() != null) {
this.ticket = new TicketCredential(new String(ticketCallback.getPassword()));
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);
}
}
}
if (this.assertion == null) {
log.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.");
}
try {
if (log.isDebugEnabled()) {
log.debug("Attempting ticket validation with service=" + service + " and ticket=" + 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);
throw (LoginException) new LoginException("CAS ticket validation failed: " + e).initCause(e);
}
}
log.info("Login succeeded.");
} else {
log.info("Login failed because callback handler did not provide CAS ticket.");
throw new LoginException("Callback handler did not provide CAS ticket.");
}
return true;
}
/**
* This occurs after logout is processed.
*
* @param result the result from the login attempt.
*/
protected void postLogin(final boolean result) {
// template method
}
@Override
public final boolean login() throws LoginException {
logger.debug("Performing login.");
if (!preLogin()) {
logger.debug("preLogin failed.");
return false;
}
final NameCallback serviceCallback = new NameCallback("service");
final PasswordCallback ticketCallback = new PasswordCallback("ticket", false);
boolean result = false;
try {
try {
this.callbackHandler.handle(new Callback[] { ticketCallback, serviceCallback });
} catch (final IOException 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) {
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;
if (this.cacheAssertions) {
this.assertion = ASSERTION_CACHE.get(ticket);
if (this.assertion != null) {
logger.debug("Assertion found in cache.");
}
}
if (this.assertion == null) {
logger.debug("CAS assertion is null; ticket validation required.");
if (CommonUtils.isBlank(service)) {
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 {
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) {
logger.info("Login failed due to CAS ticket validation failure", e);
throw (LoginException) new LoginException("CAS ticket validation failed: " + e).initCause(e);
}
}
logger.info("Login succeeded.");
} else {
logger.info("Login failed because callback handler did not provide CAS ticket.");
throw new LoginException("Callback handler did not provide CAS ticket.");
}
result = true;
} finally {
postLogin(result);
}
return result;
}
@Override
public final boolean abort() throws LoginException {
public boolean abort() throws LoginException {
if (this.ticket != null) {
this.ticket = null;
}
@ -354,140 +317,86 @@ public class CasLoginModule implements LoginModule {
return true;
}
/**
* Operations to perform before doing commit.
*
* @return true if you'd like commit to continue, false otherwise.
*/
protected boolean preCommit() {
public boolean commit() throws LoginException {
if (this.assertion != null) {
if (this.ticket != null) {
this.subject.getPrivateCredentials().add(this.ticket);
} else {
throw new LoginException("Ticket credential not found.");
}
final AssertionPrincipal casPrincipal = new AssertionPrincipal(this.assertion.getPrincipal().getName(), this.assertion);
this.subject.getPrincipals().add(casPrincipal);
// Add group containing principal as sole member
// Supports JBoss JAAS use case
final Group principalGroup = new SimpleGroup(this.principalGroupName);
principalGroup.addMember(casPrincipal);
this.subject.getPrincipals().add(principalGroup);
// Add group principal containing role data
final Group roleGroup = new SimpleGroup(this.roleGroupName);
for (final String defaultRole : defaultRoles) {
roleGroup.addMember(new SimplePrincipal(defaultRole));
}
final Map<String,Object> 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
final Object value = attributes.get(key);
if (value instanceof Collection<?>) {
for (final Object o : (Collection<?>) value) {
roleGroup.addMember(new SimplePrincipal(o.toString()));
}
} else {
roleGroup.addMember(new SimplePrincipal(value.toString()));
}
}
}
this.subject.getPrincipals().add(roleGroup);
// Place principal name in shared state for downstream JAAS modules (module chaining use case)
this.sharedState.put(LOGIN_NAME, new Object()); // casPrincipal.getName());
if (log.isDebugEnabled()) {
if (log.isDebugEnabled()) {
log.debug("Created JAAS subject with principals: " + subject.getPrincipals());
}
}
if (this.cacheAssertions) {
if (log.isDebugEnabled()) {
log.debug("Caching assertion for principal " + this.assertion.getPrincipal());
}
ASSERTION_CACHE.put(this.ticket, this.assertion);
}
} else {
// Login must have failed if there is no assertion defined
// Need to clean up state
if (this.ticket != null) {
this.ticket = null;
}
}
return true;
}
/**
* This occurs after commit is processed.
*
* @param result the result from the login attempt.
*/
protected void postCommit(final boolean result) {
// template method
}
@Override
public final boolean commit() throws LoginException {
if (!preCommit()) {
return false;
}
boolean result = false;
try {
if (this.assertion != null) {
if (this.ticket != null) {
this.subject.getPrivateCredentials().add(this.ticket);
} else {
throw new LoginException("Ticket credential not found.");
}
final AssertionPrincipal casPrincipal = new AssertionPrincipal(this.assertion.getPrincipal().getName(),
this.assertion);
this.subject.getPrincipals().add(casPrincipal);
// Add group containing principal as sole member
// Supports JBoss JAAS use case
final Group principalGroup = new SimpleGroup(this.principalGroupName);
principalGroup.addMember(casPrincipal);
this.subject.getPrincipals().add(principalGroup);
// Add group principal containing role data
final Group roleGroup = new SimpleGroup(this.roleGroupName);
for (final String defaultRole : defaultRoles) {
roleGroup.addMember(new SimplePrincipal(defaultRole));
}
final Map<String, Object> 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
final Object value = attributes.get(key);
if (value instanceof Collection<?>) {
for (final Object o : (Collection<?>) value) {
roleGroup.addMember(new SimplePrincipal(o.toString()));
}
} else {
roleGroup.addMember(new SimplePrincipal(value.toString()));
}
}
}
this.subject.getPrincipals().add(roleGroup);
// Place principal name in shared state for downstream JAAS modules (module chaining use case)
this.sharedState.put(LOGIN_NAME, assertion.getPrincipal().getName());
logger.debug("Created JAAS subject with principals: {}", subject.getPrincipals());
if (this.cacheAssertions) {
logger.debug("Caching assertion for principal {}", this.assertion.getPrincipal());
ASSERTION_CACHE.put(this.ticket, this.assertion);
}
} else {
// Login must have failed if there is no assertion defined
// Need to clean up state
if (this.ticket != null) {
this.ticket = null;
}
}
result = true;
} finally {
postCommit(result);
}
return result;
}
@Override
public final boolean logout() throws LoginException {
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);
}
}
public boolean logout() throws LoginException {
log.debug("Performing logout.");
// Remove all CAS principals
removePrincipalsOfType(AssertionPrincipal.class);
removePrincipalsOfType(SimplePrincipal.class);
removePrincipalsOfType(SimpleGroup.class);
// Remove all CAS credentials
removeCredentialsOfType(TicketCredential.class);
logger.info("Logout succeeded.");
postLogout();
log.info("Logout succeeded.");
return true;
}
/**
* Happens before logout occurs.
*
* @return true if we should continue, false otherwise.
*/
protected boolean preLogout() {
return true;
}
/**
* Happens after logout.
*/
protected void postLogout() {
// template method
}
/**
* Creates a {@link TicketValidator} instance from a class name and map of property name/value pairs.
@ -495,34 +404,32 @@ public class CasLoginModule implements LoginModule {
* @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<String, ?> propertyMap) {
CommonUtils.assertTrue(propertyMap.containsKey("casServerUrlPrefix"),
"Required property casServerUrlPrefix not found.");
private TicketValidator createTicketValidator(final String className, final Map<String,?> propertyMap) {
CommonUtils.assertTrue(propertyMap.containsKey("casServerUrlPrefix"), "Required property casServerUrlPrefix not found.");
final Class<TicketValidator> 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)) {
logger.debug("Attempting to set TicketValidator property {}", property);
log.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);
logger.debug("Set {} = {}", property, value);
ReflectUtils.setProperty(property, convertIfNecessary(pd, value), validator, info);
log.debug("Set " + property + "=" + value);
} else {
logger.warn("Cannot find property {} on {}", property, className);
log.warn("Cannot find property " + property + " on " + className);
}
}
}
} catch (final IntrospectionException e) {
throw new RuntimeException("Error getting bean info for " + validatorClass, e);
}
return validator;
}
@ -546,8 +453,7 @@ public class CasLoginModule implements LoginModule {
} 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());
}
}
@ -567,21 +473,26 @@ public class CasLoginModule implements LoginModule {
this.subject.getPrivateCredentials().removeAll(this.subject.getPrivateCredentials(clazz));
}
/**
* Removes expired entries from the assertion cache.
*/
private void cleanCache() {
logger.debug("Cleaning assertion cache of size {}", ASSERTION_CACHE.size());
final Iterator<Map.Entry<TicketCredential, Assertion>> 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();
/** 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<Map.Entry<TicketCredential,Assertion>> 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();
}
}
}
}

View File

@ -1,25 +1,31 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.*;
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;
/**
* Callback handler that provides the CAS service and ticket to a
@ -35,10 +41,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.
*
@ -50,7 +56,6 @@ public class ServiceAndTicketCallbackHandler implements CallbackHandler {
this.ticket = ticket;
}
@Override
public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (final Callback callback : callbacks) {
if (callback instanceof NameCallback) {

View File

@ -1,94 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.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.
* <p>
* 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.
* <p>
* If a <code>service</code> 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);
}
@Override
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);
}
}

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
@ -32,9 +33,9 @@ public final class TicketCredential implements Principal {
/** Hash code seed value */
private static final int HASHCODE_SEED = 17;
/** Ticket ID string */
private final String ticket;
private String ticket;
/**
* Creates a new instance that wraps the given ticket.
@ -44,7 +45,6 @@ public final class TicketCredential implements Principal {
this.ticket = ticket;
}
@Override
public String getName() {
return this.ticket;
}
@ -53,16 +53,13 @@ public final class TicketCredential implements Principal {
return this.ticket;
}
public boolean equals(final Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
public boolean equals(Object o) {
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;
}

View File

@ -1,32 +1,13 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.proxy;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;
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
@ -38,12 +19,13 @@ import javax.crypto.spec.DESedeKeySpec;
*/
public abstract class AbstractEncryptedProxyGrantingTicketStorageImpl implements ProxyGrantingTicketStorage {
public static final String DEFAULT_ENCRYPTION_ALGORITHM = "DESede";
private Key key;
private String cipherAlgorithm = ConfigurationKeys.CIPHER_ALGORITHM.getDefaultValue();
private String cipherAlgorithm = DEFAULT_ENCRYPTION_ALGORITHM;
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()));
}
@ -60,12 +42,10 @@ public abstract class AbstractEncryptedProxyGrantingTicketStorageImpl implements
this.cipherAlgorithm = cipherAlgorithm;
}
@Override
public final void save(final String proxyGrantingTicketIou, final String proxyGrantingTicket) {
saveInternal(proxyGrantingTicketIou, encrypt(proxyGrantingTicket));
}
@Override
public final String retrieve(final String proxyGrantingTicketIou) {
return decrypt(retrieveInternal(proxyGrantingTicketIou));
}

View File

@ -1,49 +1,54 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.net.URL;
import java.net.URLEncoder;
import org.jasig.cas.client.ssl.HttpURLConnectionFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.XmlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
/**
* Implementation of a ProxyRetriever that follows the CAS 2.0 specification.
* For more information on the CAS 2.0 specification, please see the <a
* href="http://www.jasig.org/cas/protocol">specification
* href="http://www.ja-sig.org/products/cas/overview/protocol/index.html">specification
* document</a>.
* <p/>
* 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 = 560409469568911792L;
private static final long serialVersionUID = 560409469568911791L;
private static final Logger logger = LoggerFactory.getLogger(Cas20ProxyRetriever.class);
/**
* Instance of Commons Logging.
*/
private final Log log = LogFactory.getLog(this.getClass());
/**
* Url to CAS server.
@ -52,59 +57,39 @@ 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,
final HttpURLConnectionFactory urlFactory) {
public Cas20ProxyRetriever(final String casServerUrl, final String encoding) {
CommonUtils.assertNotNull(casServerUrl, "casServerUrl cannot be null.");
this.casServerUrl = casServerUrl;
this.encoding = encoding;
this.urlConnectionFactory = urlFactory;
}
@Override
public String getProxyTicketIdFor(final String proxyGrantingTicketId, final String targetService) {
CommonUtils.assertNotNull(proxyGrantingTicketId, "proxyGrantingTicketId cannot be null.");
CommonUtils.assertNotNull(targetService, "targetService cannot be null.");
public String getProxyTicketIdFor(final String proxyGrantingTicketId,
final String targetService) {
final URL url = constructUrl(proxyGrantingTicketId, targetService);
final String response;
if (this.urlConnectionFactory != null) {
response = CommonUtils.getResponseFromServer(url, this.urlConnectionFactory, this.encoding);
} else {
response = CommonUtils.getResponseFromServer(url, this.encoding);
}
final String url = constructUrl(proxyGrantingTicketId, targetService);
final String response = CommonUtils.getResponseFromServer(url, this.encoding);
final String error = XmlUtils.getTextForElement(response, "proxyFailure");
if (CommonUtils.isNotEmpty(error)) {
logger.debug(error);
log.debug(error);
return null;
}
final String ticket = XmlUtils.getTextForElement(response, "proxyTicket");
logger.debug("Got proxy ticket {}", ticket);
return ticket;
return XmlUtils.getTextForElement(response, "proxyTicket");
}
private URL constructUrl(final String proxyGrantingTicketId, final String targetService) {
private String constructUrl(final String proxyGrantingTicketId, final String targetService) {
try {
return new URL(this.casServerUrl + (this.casServerUrl.endsWith("/") ? "" : "/") + "proxy" + "?pgt="
+ proxyGrantingTicketId + "&targetService=" + URLEncoder.encode(targetService, "UTF-8"));
} catch (final Exception e) {
return this.casServerUrl + (this.casServerUrl.endsWith("/") ? "" : "/") + "proxy" + "?pgt="
+ proxyGrantingTicketId + "&targetService="
+ URLEncoder.encode(targetService, "UTF-8");
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
@ -38,8 +39,6 @@ public final class CleanUpTimerTask extends TimerTask {
public CleanUpTimerTask(final ProxyGrantingTicketStorage proxyGrantingTicketStorage) {
this.proxyGrantingTicketStorage = proxyGrantingTicketStorage;
}
@Override
public void run() {
this.proxyGrantingTicketStorage.cleanUp();
}

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
/**
@ -23,6 +24,7 @@ package org.jasig.cas.client.proxy;
* them to a specific ProxyGrantingTicketIou.
*
* @author Scott Battaglia
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public interface ProxyGrantingTicketStorage {
@ -45,7 +47,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.

View File

@ -1,29 +1,30 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.Map;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.jasig.cas.client.util.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Implementation of {@link ProxyGrantingTicketStorage} that is backed by a
@ -34,11 +35,12 @@ import org.slf4j.LoggerFactory;
*
* @author Scott Battaglia
* @author Brad Cupit (brad [at] lsu {dot} edu)
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public final class ProxyGrantingTicketStorageImpl implements ProxyGrantingTicketStorage {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final Log log = LogFactory.getLog(getClass());
/**
* Default timeout in milliseconds.
@ -48,7 +50,7 @@ public final class ProxyGrantingTicketStorageImpl implements ProxyGrantingTicket
/**
* Map that stores the PGTIOU to PGT mappings.
*/
private final ConcurrentMap<String, ProxyGrantingTicketHolder> cache = new ConcurrentHashMap<String, ProxyGrantingTicketHolder>();
private final ConcurrentMap<String,ProxyGrantingTicketHolder> cache = new ConcurrentHashMap<String,ProxyGrantingTicketHolder>();
/**
* time, in milliseconds, before a {@link ProxyGrantingTicketHolder}
@ -56,7 +58,7 @@ public final class ProxyGrantingTicketStorageImpl implements ProxyGrantingTicket
*
* @see ProxyGrantingTicketStorageImpl#DEFAULT_TIMEOUT
*/
private final long timeout;
private long timeout;
/**
* Constructor set the timeout to the default value.
@ -72,38 +74,35 @@ public final class ProxyGrantingTicketStorageImpl implements ProxyGrantingTicket
* @param timeout the time to hold on to the ProxyGrantingTicket
*/
public ProxyGrantingTicketStorageImpl(final long timeout) {
this.timeout = timeout;
this.timeout = timeout;
}
/**
* NOTE: you can only retrieve a ProxyGrantingTicket once with this method.
* Its removed after retrieval.
*/
@Override
public String retrieve(final String proxyGrantingTicketIou) {
if (CommonUtils.isBlank(proxyGrantingTicketIou)) {
return null;
}
final ProxyGrantingTicketHolder holder = this.cache.get(proxyGrantingTicketIou);
if (holder == null) {
logger.info("No Proxy Ticket found for [{}].", proxyGrantingTicketIou);
log.info("No Proxy Ticket found for [" + proxyGrantingTicketIou + "].");
return null;
}
this.cache.remove(proxyGrantingTicketIou);
logger.debug("Returned ProxyGrantingTicket of [{}]", holder.getProxyGrantingTicket());
if (log.isDebugEnabled()) {
log.debug("Returned ProxyGrantingTicket of [" + holder.getProxyGrantingTicket() + "]");
}
return holder.getProxyGrantingTicket();
}
@Override
public void save(final String proxyGrantingTicketIou, final String proxyGrantingTicket) {
final ProxyGrantingTicketHolder holder = new ProxyGrantingTicketHolder(proxyGrantingTicket);
logger.debug("Saving ProxyGrantingTicketIOU and ProxyGrantingTicket combo: [{}, {}]", proxyGrantingTicketIou,
proxyGrantingTicket);
if (log.isDebugEnabled()) {
log.debug("Saving ProxyGrantingTicketIOU and ProxyGrantingTicket combo: [" + proxyGrantingTicketIou + ", " + proxyGrantingTicket + "]");
}
this.cache.put(proxyGrantingTicketIou, holder);
}
@ -111,15 +110,14 @@ public final class ProxyGrantingTicketStorageImpl implements ProxyGrantingTicket
* Cleans up old, expired proxy tickets. This method must be
* called regularly via an external thread or timer.
*/
@Override
public void cleanUp() {
for (final Map.Entry<String, ProxyGrantingTicketHolder> holder : this.cache.entrySet()) {
for (final Map.Entry<String,ProxyGrantingTicketHolder> holder : this.cache.entrySet()) {
if (holder.getValue().isExpired(this.timeout)) {
this.cache.remove(holder.getKey());
}
}
}
private static final class ProxyGrantingTicketHolder {
private final String proxyGrantingTicket;

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
@ -25,6 +26,7 @@ import java.io.Serializable;
* implementation a black box to the client.
*
* @author Scott Battaglia
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public interface ProxyRetriever extends Serializable {
@ -36,5 +38,6 @@ 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);
}

View File

@ -1,23 +1,24 @@
<!--
Licensed to Apereo under one or more contributor license
Licensed to Jasig 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,
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:
except in compliance with the License. You may obtain a
copy of the License at:
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.
-->
<html>
<body>
<p>The proxy package includes a servlet to act as a proxy receptor,

View File

@ -1,28 +1,31 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.
@ -33,51 +36,50 @@ import org.slf4j.LoggerFactory;
*
*/
public final class HashMapBackedSessionMappingStorage implements SessionMappingStorage {
/**
* Maps the ID from the CAS server to the Session.
*/
private final Map<String, HttpSession> MANAGED_SESSIONS = new HashMap<String, HttpSession>();
private final Map<String,HttpSession> MANAGED_SESSIONS = new HashMap<String,HttpSession>();
/**
* Maps the Session ID to the key from the CAS Server.
*/
private final Map<String, String> ID_TO_SESSION_KEY_MAPPING = new HashMap<String, String>();
private final Map<String,String> ID_TO_SESSION_KEY_MAPPING = new HashMap<String,String>();
private final Logger logger = LoggerFactory.getLogger(getClass());
private final Log log = LogFactory.getLog(getClass());
@Override
public synchronized void addSessionById(final String mappingId, final HttpSession session) {
public synchronized void addSessionById(String mappingId, HttpSession session) {
ID_TO_SESSION_KEY_MAPPING.put(session.getId(), mappingId);
MANAGED_SESSIONS.put(mappingId, session);
}
}
@Override
public synchronized void removeBySessionById(final String sessionId) {
logger.debug("Attempting to remove Session=[{}]", sessionId);
public synchronized void removeBySessionById(String sessionId) {
if (log.isDebugEnabled()) {
log.debug("Attempting to remove Session=[" + sessionId + "]");
}
final String key = ID_TO_SESSION_KEY_MAPPING.get(sessionId);
if (logger.isDebugEnabled()) {
if (log.isDebugEnabled()) {
if (key != null) {
logger.debug("Found mapping for session. Session Removed.");
log.debug("Found mapping for session. Session Removed.");
} else {
logger.debug("No mapping for session found. Ignoring.");
log.debug("No mapping for session found. Ignoring.");
}
}
MANAGED_SESSIONS.remove(key);
ID_TO_SESSION_KEY_MAPPING.remove(sessionId);
}
}
@Override
public synchronized HttpSession removeSessionByMappingId(final 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;
}
}
}

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
@ -29,26 +30,26 @@ import javax.servlet.http.HttpSession;
*
*/
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);
}

View File

@ -1,33 +1,34 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.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;
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;
/**
* Implements the Single Sign Out protocol. It handles registering the session and destroying the session.
*
@ -37,70 +38,49 @@ import org.jasig.cas.client.util.AbstractConfigurationFilter;
*/
public final class SingleSignOutFilter extends AbstractConfigurationFilter {
private static final SingleSignOutHandler HANDLER = new SingleSignOutHandler();
private static final SingleSignOutHandler handler = new SingleSignOutHandler();
private final AtomicBoolean handlerInitialized = new AtomicBoolean(false);
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
super.init(filterConfig);
if (!isIgnoreInitConfiguration()) {
setArtifactParameterName(getString(ConfigurationKeys.ARTIFACT_PARAMETER_NAME));
setLogoutParameterName(getString(ConfigurationKeys.LOGOUT_PARAMETER_NAME));
setRelayStateParameterName(getString(ConfigurationKeys.RELAY_STATE_PARAMETER_NAME));
setLogoutCallbackPath(getString(ConfigurationKeys.LOGOUT_CALLBACK_PATH));
HANDLER.setArtifactParameterOverPost(getBoolean(ConfigurationKeys.ARTIFACT_PARAMETER_OVER_POST));
HANDLER.setEagerlyCreateSessions(getBoolean(ConfigurationKeys.EAGERLY_CREATE_SESSIONS));
handler.setArtifactParameterName(getPropertyFromInitParams(filterConfig, "artifactParameterName", "ticket"));
handler.setLogoutParameterName(getPropertyFromInitParams(filterConfig, "logoutParameterName", "logoutRequest"));
}
HANDLER.init();
handlerInitialized.set(true);
handler.init();
}
public void setArtifactParameterName(final String name) {
HANDLER.setArtifactParameterName(name);
}
public void setLogoutParameterName(final String name) {
HANDLER.setLogoutParameterName(name);
handler.setArtifactParameterName(name);
}
public void setRelayStateParameterName(final String name) {
HANDLER.setRelayStateParameterName(name);
}
public void setLogoutCallbackPath(final String logoutCallbackPath) {
HANDLER.setLogoutCallbackPath(logoutCallbackPath);
public void setLogoutParameterName(final String name) {
handler.setLogoutParameterName(name);
}
public void setSessionMappingStorage(final SessionMappingStorage storage) {
HANDLER.setSessionMappingStorage(storage);
handler.setSessionMappingStorage(storage);
}
@Override
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;
/**
* <p>Workaround for now for the fact that Spring Security will fail since it doesn't call {@link #init(javax.servlet.FilterConfig)}.</p>
* <p>Ultimately we need to allow deployers to actually inject their fully-initialized {@link org.jasig.cas.client.session.SingleSignOutHandler}.</p>
*/
if (!this.handlerInitialized.getAndSet(true)) {
HANDLER.init();
if (handler.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());
}
if (HANDLER.process(request, response)) {
filterChain.doFilter(servletRequest, servletResponse);
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
// nothing to do
}
protected static SingleSignOutHandler getSingleSignOutHandler() {
return HANDLER;
return handler;
}
}

View File

@ -1,40 +1,31 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.Collections;
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 javax.xml.bind.DatatypeConverter;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.XmlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Performs CAS single sign-out operations in an API-agnostic fashion.
@ -46,42 +37,23 @@ import org.slf4j.LoggerFactory;
*/
public final class SingleSignOutHandler {
private final static int DECOMPRESSION_FACTOR = 10;
/** Logger instance */
private final Logger logger = LoggerFactory.getLogger(getClass());
private final Log log = LogFactory.getLog(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 = Protocol.CAS2.getArtifactParameterName();
/** Parameter name that stores logout request for SLO */
private String logoutParameterName = ConfigurationKeys.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 name of the artifact parameter. This is used to capture the session identifier. */
private String artifactParameterName = "ticket";
/** The logout callback path configured at the CAS server, if there is one */
private String logoutCallbackPath;
/** Parameter name that stores logout request */
private String logoutParameterName = "logoutRequest";
private boolean artifactParameterOverPost = false;
private boolean eagerlyCreateSessions = true;
private List<String> safeParameters;
private final LogoutStrategy logoutStrategy = isServlet30() ? new Servlet30LogoutStrategy() : new Servlet25LogoutStrategy();
public void setSessionMappingStorage(final SessionMappingStorage storage) {
this.sessionMappingStorage = storage;
}
public void setArtifactParameterOverPost(final boolean artifactParameterOverPost) {
this.artifactParameterOverPost = artifactParameterOverPost;
}
public SessionMappingStorage getSessionMappingStorage() {
return this.sessionMappingStorage;
}
@ -94,48 +66,21 @@ public final class SingleSignOutHandler {
}
/**
* @param name Name of parameter containing CAS logout request message for SLO.
* @param name Name of parameter containing CAS logout request message.
*/
public void setLogoutParameterName(final String name) {
this.logoutParameterName = name;
}
/**
* @param logoutCallbackPath The logout callback path configured at the CAS server.
*/
public void setLogoutCallbackPath(final String logoutCallbackPath) {
this.logoutCallbackPath = logoutCallbackPath;
}
/**
* @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 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.sessionMappingStorage, "sessionMappingStorage cannot be null.");
CommonUtils.assertNotNull(this.relayStateParameterName, "relayStateParameterName cannot be null.");
if (this.artifactParameterOverPost) {
this.safeParameters = Arrays.asList(this.logoutParameterName, this.artifactParameterName);
} else {
this.safeParameters = Collections.singletonList(this.logoutParameterName);
}
}
public void init() {
CommonUtils.assertNotNull(this.artifactParameterName, "artifactParameterName cannot be null.");
CommonUtils.assertNotNull(this.logoutParameterName, "logoutParameterName cannot be null.");
CommonUtils.assertNotNull(this.sessionMappingStorage, "sessionMappingStorage cannote be null.");
}
/**
* Determines whether the given request contains an authentication token.
*
@ -143,61 +88,20 @@ public final class SingleSignOutHandler {
*
* @return True if request contains authentication token, false otherwise.
*/
private boolean isTokenRequest(final HttpServletRequest request) {
return CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.artifactParameterName,
this.safeParameters));
public boolean isTokenRequest(final HttpServletRequest request) {
return CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.artifactParameterName));
}
/**
* Determines whether the given request is a CAS logout request.
* Determines whether the given request is a CAS logout request.
*
* @param request HTTP request.
*
* @return True if request is logout request, false otherwise.
*/
private boolean isLogoutRequest(final HttpServletRequest request) {
if ("POST".equalsIgnoreCase(request.getMethod())) {
return !isMultipartRequest(request)
&& pathEligibleForLogout(request)
&& CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName,
this.safeParameters));
}
if ("GET".equalsIgnoreCase(request.getMethod())) {
return CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName, this.safeParameters));
}
return false;
}
private boolean pathEligibleForLogout(final HttpServletRequest request) {
return logoutCallbackPath == null || logoutCallbackPath.equals(getPath(request));
}
private String getPath(final HttpServletRequest request) {
return request.getServletPath() + CommonUtils.nullToEmpty(request.getPathInfo());
}
/**
* 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;
}
if (isLogoutRequest(request)) {
logger.trace("Received a logout request");
destroySession(request);
return false;
}
logger.trace("Ignoring URI for logout: {}", request.getRequestURI());
return true;
public boolean isLogoutRequest(final HttpServletRequest request) {
return "POST".equals(request.getMethod()) && !isMultipartRequest(request) &&
CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName));
}
/**
@ -206,86 +110,48 @@ public final class SingleSignOutHandler {
*
* @param request HTTP request containing an authentication token.
*/
private void recordSession(final HttpServletRequest request) {
final HttpSession session = request.getSession(this.eagerlyCreateSessions);
public void recordSession(final HttpServletRequest request) {
final HttpSession session = request.getSession(true);
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);
if (log.isDebugEnabled()) {
log.debug("Recording session for token " + token);
}
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) {
// ignore if the session is already marked as invalid. Nothing we can do!
// ignore if the session is already marked as invalid. Nothing we can do!
}
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 = DatatypeConverter.parseBase64Binary(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.
*/
private void destroySession(final HttpServletRequest request) {
String logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName, this.safeParameters);
if (CommonUtils.isBlank(logoutMessage)) {
logger.error("Could not locate logout message of the request from {}", this.logoutParameterName);
return;
public void destroySession(final HttpServletRequest request) {
final String logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName);
if (log.isTraceEnabled()) {
log.trace ("Logout request:\n" + logoutMessage);
}
if (!logoutMessage.contains("SessionIndex")) {
logoutMessage = uncompressLogoutMessage(logoutMessage);
}
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) {
final String sessionID = session.getId();
logger.debug("Invalidating session [{}] for token [{}]", sessionID, token);
String sessionID = session.getId();
if (log.isDebugEnabled()) {
log.debug ("Invalidating session [" + sessionID + "] for token [" + token + "]");
}
try {
session.invalidate();
} catch (final IllegalStateException e) {
logger.debug("Error invalidating session.", e);
log.debug("Error invalidating session.", e);
}
this.logoutStrategy.logout(request);
}
}
}
@ -293,41 +159,4 @@ public final class SingleSignOutHandler {
private boolean isMultipartRequest(final HttpServletRequest request) {
return request.getContentType() != null && request.getContentType().toLowerCase().startsWith("multipart");
}
private static boolean isServlet30() {
try {
return HttpServletRequest.class.getMethod("logout") != null;
} catch (final NoSuchMethodException e) {
return false;
}
}
/**
* Abstracts the ways we can force logout with the Servlet spec.
*/
private interface LogoutStrategy {
void logout(HttpServletRequest request);
}
private class Servlet25LogoutStrategy implements LogoutStrategy {
@Override
public void logout(final HttpServletRequest request) {
// nothing additional to do here
}
}
private class Servlet30LogoutStrategy implements LogoutStrategy {
@Override
public void logout(final HttpServletRequest request) {
try {
request.logout();
} catch (final ServletException e) {
logger.debug("Error performing request.logout.");
}
}
}
}

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
@ -34,18 +35,16 @@ import javax.servlet.http.HttpSessionListener;
*/
public final class SingleSignOutHttpSessionListener implements HttpSessionListener {
private SessionMappingStorage sessionMappingStorage;
@Override
private SessionMappingStorage sessionMappingStorage;
public void sessionCreated(final HttpSessionEvent event) {
// nothing to do at the moment
}
@Override
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());
}
@ -57,6 +56,6 @@ public final class SingleSignOutHttpSessionListener implements HttpSessionListen
* @return the SessionMappingStorage
*/
protected static SessionMappingStorage getSessionMappingStorage() {
return SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage();
return SingleSignOutFilter.getSingleSignOutHandler().getSessionMappingStorage();
}
}

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
@ -32,7 +33,6 @@ import javax.net.ssl.SSLSession;
public final class AnyHostnameVerifier implements HostnameVerifier {
/** {@inheritDoc} */
@Override
public boolean verify(final String hostname, final SSLSession session) {
return true;
}

View File

@ -1,46 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.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);
}

View File

@ -1,192 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.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 <i>https</i> 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;
}
@Override
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
* <p>
* Sample properties file:
* <pre>
* protocol=TLS
* keyStoreType=JKS
* keyStorePath=/var/secure/location/.keystore
* keyStorePass=changeit
* certificatePassword=aGoodPass
* </pre>
* @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(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final HttpsURLConnectionFactory that = (HttpsURLConnectionFactory) o;
if (!hostnameVerifier.equals(that.hostnameVerifier)) return false;
if (!sslConfiguration.equals(that.sslConfiguration)) return false;
return true;
}
@Override
public int hashCode() {
int result = hostnameVerifier.hashCode();
result = 31 * result + sslConfiguration.hashCode();
return result;
}
private void writeObject(final ObjectOutputStream out) throws IOException {
if (this.hostnameVerifier == HttpsURLConnection.getDefaultHostnameVerifier()) {
out.writeObject(null);
} else {
out.writeObject(this.hostnameVerifier);
}
out.writeObject(this.sslConfiguration);
}
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
final Object internalHostNameVerifier = in.readObject();
if (internalHostNameVerifier == null) {
this.hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
} else {
this.hostnameVerifier = (HostnameVerifier) internalHostNameVerifier;
}
this.sslConfiguration = (Properties) in.readObject();
}
}

View File

@ -1,25 +1,25 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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,13 +32,12 @@ import javax.net.ssl.SSLSession;
* @since 3.1.10
*
*/
public final class RegexHostnameVerifier implements HostnameVerifier, Serializable {
private static final long serialVersionUID = 1L;
public final class RegexHostnameVerifier implements HostnameVerifier {
/** Allowed hostname pattern */
private final Pattern pattern;
private Pattern pattern;
/**
* Creates a new instance using the given regular expression.
*
@ -48,8 +47,8 @@ public final class RegexHostnameVerifier implements HostnameVerifier, Serializab
this.pattern = Pattern.compile(regex);
}
/** {@inheritDoc} */
@Override
public boolean verify(final String hostname, final SSLSession session) {
return pattern.matcher(hostname).matches();
}

View File

@ -1,26 +1,26 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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,11 @@ import java.io.Serializable;
* @since 3.1.10
*
*/
public final class WhitelistHostnameVerifier implements HostnameVerifier, Serializable {
private static final long serialVersionUID = 1L;
public final class WhitelistHostnameVerifier implements HostnameVerifier {
/** Allowed hosts */
private final String[] allowedHosts;
private String[] allowedHosts;
/**
* Creates a new instance using the given array of allowed hosts.
@ -46,6 +45,7 @@ public final class WhitelistHostnameVerifier implements HostnameVerifier, Serial
this.allowedHosts = allowed;
}
/**
* Creates a new instance using the given list of allowed hosts.
*
@ -56,7 +56,6 @@ public final class WhitelistHostnameVerifier implements HostnameVerifier, Serial
}
/** {@inheritDoc} */
@Override
public boolean verify(final String hostname, final SSLSession session) {
for (final String allowedHost : this.allowedHosts) {

View File

@ -1,31 +1,31 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.Protocol;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
/**
* Abstract filter that contains code that is common to all CAS filters.
@ -38,16 +38,23 @@ import java.util.Arrays;
* <p>Please note that one of the two above parameters must be set.</p>
*
* @author Scott Battaglia
* @author Misagh Moayyed
* @version $Revision$ $Date$
* @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_";
private final Protocol protocol;
/** Instance of commons logging for logging purposes. */
protected final Log log = LogFactory.getLog(getClass());
/** 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;
@ -59,24 +66,24 @@ public abstract class AbstractCasFilter extends AbstractConfigurationFilter {
/** The exact url of the service. */
private String service;
protected AbstractCasFilter(final Protocol protocol) {
this.protocol = protocol;
}
@Override
public final void init(final FilterConfig filterConfig) throws ServletException {
super.init(filterConfig);
if (!isIgnoreInitConfiguration()) {
setServerName(getString(ConfigurationKeys.SERVER_NAME));
setService(getString(ConfigurationKeys.SERVICE));
setEncodeServiceUrl(getBoolean(ConfigurationKeys.ENCODE_SERVICE_URL));
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);
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.
@ -91,22 +98,19 @@ public abstract class AbstractCasFilter extends AbstractConfigurationFilter {
* afterPropertiesSet();
*/
public void init() {
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.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.");
}
// empty implementation as most filters won't need this.
@Override
public void destroy() {
// nothing to do
}
protected final String constructServiceUrl(final HttpServletRequest request, final HttpServletResponse response) {
return CommonUtils.constructServiceUrl(request, response, this.service, this.serverName,
this.protocol.getServiceParameterName(),
this.protocol.getArtifactParameterName(), this.encodeServiceUrl);
return CommonUtils.constructServiceUrl(request, response, this.service, this.serverName, this.artifactParameterName, this.encodeServiceUrl);
}
/**
@ -117,8 +121,8 @@ public abstract class AbstractCasFilter extends AbstractConfigurationFilter {
*/
public final void setServerName(final String serverName) {
if (serverName != null && serverName.endsWith("/")) {
this.serverName = serverName.substring(0, serverName.length() - 1);
logger.info("Eliminated extra slash from serverName [{}]. It is now [{}]", serverName, this.serverName);
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));
} else {
this.serverName = serverName;
}
@ -128,22 +132,23 @@ public abstract class AbstractCasFilter extends AbstractConfigurationFilter {
this.service = service;
}
public final void setArtifactParameterName(final String artifactParameterName) {
this.artifactParameterName = artifactParameterName;
}
public final void setServiceParameterName(final String serviceParameterName) {
this.serviceParameterName = serviceParameterName;
}
public final void setEncodeServiceUrl(final boolean encodeServiceUrl) {
this.encodeServiceUrl = encodeServiceUrl;
this.encodeServiceUrl = encodeServiceUrl;
}
protected Protocol getProtocol() {
return this.protocol;
public final String getArtifactParameterName() {
return this.artifactParameterName;
}
/**
* Template method to allow you to change how you retrieve the ticket.
*
* @param request the HTTP ServletRequest. CANNOT be NULL.
* @return the ticket if its found, null otherwise.
*/
protected String retrieveTicketFromRequest(final HttpServletRequest request) {
return CommonUtils.safeGetParameter(request, this.protocol.getArtifactParameterName(),
Arrays.asList(this.protocol.getArtifactParameterName()));
public final String getServiceParameterName() {
return this.serviceParameterName;
}
}

View File

@ -1,32 +1,31 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.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;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Abstracts out the ability to configure the filters from the initial properties provided.
@ -36,43 +35,90 @@ import org.slf4j.LoggerFactory;
* @since 3.1
*/
public abstract class AbstractConfigurationFilter implements Filter {
private static final String CONFIGURATION_STRATEGY_KEY = "configurationStrategy";
protected final Logger logger = LoggerFactory.getLogger(getClass());
protected final Log log = LogFactory.getLog(getClass());
private boolean ignoreInitConfiguration = false;
private ConfigurationStrategy configurationStrategy;
/**
* Retrieves the property from the FilterConfig. First it checks the FilterConfig's initParameters to see if it
* has a value.
* If it does, it returns that, otherwise it retrieves the ServletContext's initParameters and returns that value if any.
* <p>
* Finally, it will check JNDI if all other methods fail. All the JNDI properties should be stored under either java:comp/env/cas/SHORTFILTERNAME/{propertyName}
* or java:comp/env/cas/{propertyName}
* <p>
* Essentially the documented order is:
* <ol>
* <li>FilterConfig.getInitParameter</li>
* <li>ServletContext.getInitParameter</li>
* <li>java:comp/env/cas/SHORTFILTERNAME/{propertyName}</li>
* <li>java:comp/env/cas/{propertyName}</li>
* <li>Default Value</li>
* </ol>
*
*
* @param filterConfig the Filter Configuration.
* @param propertyName the property to retrieve.
* @param defaultValue the default value if the property is not found.
* @return the property value, following the above conventions. It will always return the more specific value (i.e.
* filter vs. context).
*/
protected final String getPropertyFromInitParams(final FilterConfig filterConfig, final String propertyName, final String defaultValue) {
final String value = filterConfig.getInitParameter(propertyName);
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
final String configurationStrategyName = filterConfig.getServletContext().getInitParameter(CONFIGURATION_STRATEGY_KEY);
this.configurationStrategy = ReflectUtils.newInstance(ConfigurationStrategyName.resolveToConfigurationStrategy(configurationStrategyName));
this.configurationStrategy.init(filterConfig, getClass());
if (CommonUtils.isNotBlank(value)) {
log.info("Property [" + propertyName + "] loaded from FilterConfig.getInitParameter with value [" + value + "]");
return value;
}
final String value2 = filterConfig.getServletContext().getInitParameter(propertyName);
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;
}
log.info("Property [" + propertyName + "] not found. Using default value [" + defaultValue + "]");
return defaultValue;
}
protected final boolean parseBoolean(final String value) {
return ((value != null) && value.equalsIgnoreCase("true"));
}
protected final String loadFromContext(final InitialContext context, final String path) {
try {
return (String) context.lookup(path);
} catch (final NamingException e) {
return null;
}
}
protected final boolean getBoolean(final ConfigurationKey<Boolean> configurationKey) {
return this.configurationStrategy.getBoolean(configurationKey);
}
protected final String getString(final ConfigurationKey<String> configurationKey) {
return this.configurationStrategy.getString(configurationKey);
}
protected final long getLong(final ConfigurationKey<Long> configurationKey) {
return this.configurationStrategy.getLong(configurationKey);
}
protected final int getInt(final ConfigurationKey<Integer> configurationKey) {
return this.configurationStrategy.getInt(configurationKey);
}
protected final <T> Class<? extends T> getClass(final ConfigurationKey<Class<? extends T>> configurationKey) {
return this.configurationStrategy.getClass(configurationKey);
}
public final void setIgnoreInitConfiguration(final boolean ignoreInitConfiguration) {
public final void setIgnoreInitConfiguration(boolean ignoreInitConfiguration) {
this.ignoreInitConfiguration = ignoreInitConfiguration;
}

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
@ -24,6 +25,7 @@ import org.jasig.cas.client.validation.Assertion;
* Static holder that places Assertion in a ThreadLocal.
*
* @author Scott Battaglia
* @version $Revision: 11728 $ $Date: 2007-09-26 14:20:43 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public class AssertionHolder {
@ -33,6 +35,7 @@ public class AssertionHolder {
*/
private static final ThreadLocal<Assertion> threadLocal = new ThreadLocal<Assertion>();
/**
* Retrieve the assertion from the ThreadLocal.
*

View File

@ -1,50 +1,53 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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 javax.servlet.*;
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 javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.jasig.cas.client.validation.Assertion;
import java.io.IOException;
/**
* Places the assertion in a ThreadLocal such that other resources can access it that do not have access to the web tier session.
*
* @author Scott Battaglia
* @version $Revision: 11728 $ $Date: 2007-09-26 14:20:43 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public final class AssertionThreadLocalFilter implements Filter {
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
// nothing to do here
}
@Override
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);
@ -54,7 +57,6 @@ public final class AssertionThreadLocalFilter implements Filter {
}
}
@Override
public void destroy() {
// nothing to do
}

View File

@ -1,59 +1,62 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.Protocol;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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;
import javax.net.ssl.SSLException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Arrays;
import java.net.URL;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Date;
import java.util.TimeZone;
/**
* Common utilities so that we don't need to include Commons Lang.
*
* @author Scott Battaglia
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public final class CommonUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(CommonUtils.class);
/** Instance of Commons Logging. */
private static final Log LOG = LogFactory.getLog(CommonUtils.class);
/**
* Constant representing the ProxyGrantingTicket IOU Request Parameter.
*/
@ -64,24 +67,16 @@ 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;
static {
final Set<String> serviceParameterSet = new HashSet<String>(4);
for (final Protocol protocol : Protocol.values()) {
serviceParameterSet.add(protocol.getServiceParameterName());
}
SERVICE_PARAMETER_NAMES = serviceParameterSet.toString()
.replaceAll("\\[|\\]", "")
.replaceAll("\\s", "");
}
private CommonUtils() {
// nothing to do
}
public static String formatForUtcTime(final Date date) {
final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return dateFormat.format(date);
}
/**
* Check whether the object is null or not. If it is, throw an exception and
* display the message.
@ -99,7 +94,7 @@ public final class CommonUtils {
* Check whether the collection is null or empty. If it is, throw an
* exception and display the message.
*
* @param c the collection to check.
* @param c the collecion to check.
* @param message the message to display if the object is null.
*/
public static void assertNotEmpty(final Collection<?> c, final String message) {
@ -113,7 +108,7 @@ public final class CommonUtils {
* Assert that the statement is true, otherwise throw an exception with the
* provided message.
*
* @param cond the condition to assert is true.
* @param cond the codition 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) {
@ -122,20 +117,6 @@ public final class CommonUtils {
}
}
/**
* Assert that the statement is true, otherwise throw an exception with the
* provided message.
*
* @param cond the condition to assert is false.
* @param message the message to display if the condition is not false.
*/
public static void assertFalse(final boolean cond, final String message) {
if (cond) {
throw new IllegalArgumentException(message);
}
}
/**
* Determines whether the String is null or of length 0.
*
@ -143,7 +124,7 @@ public final class CommonUtils {
* @return true if its null or length of 0, false otherwise.
*/
public static boolean isEmpty(final String string) {
return string == null || string.isEmpty();
return string == null || string.length() == 0;
}
/**
@ -165,7 +146,7 @@ public final class CommonUtils {
* @return true if its blank, false otherwise.
*/
public static boolean isBlank(final String string) {
return isEmpty(string) || string.trim().isEmpty();
return isEmpty(string) || string.trim().length() == 0;
}
/**
@ -187,191 +168,108 @@ public final class CommonUtils {
* @param serviceUrl the actual service's url.
* @param renew whether we should send renew or not.
* @param gateway where we should send gateway or not.
* @param method the method used by the CAS server to send the user back to the application.
* @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, final String method) {
return casServerLoginUrl + (casServerLoginUrl.contains("?") ? "&" : "?") + serviceParameterName + "="
+ urlEncode(serviceUrl) + (renew ? "&renew=true" : "") + (gateway ? "&gateway=true" : "")
+ (method != null ? "&method=" + method : "");
}
/**
* Construct redirect url to a CAS server.
*
* @param casServerLoginUrl the cas server login url
* @param serviceParameterName the service parameter name
* @param serviceUrl the service url
* @param renew the renew
* @param gateway the gateway
* @return the string
*/
public static String constructRedirectUrl(final String casServerLoginUrl, final String serviceParameterName,
final String serviceUrl, final boolean renew, final boolean gateway) {
return constructRedirectUrl(casServerLoginUrl, serviceParameterName, serviceUrl, renew, gateway, null);
}
/**
* Url encode a value using UTF-8 encoding.
*
* @param value the value to encode.
* @return the encoded value.
*/
public static String urlEncode(final String value) {
public static String constructRedirectUrl(final String casServerLoginUrl, final String serviceParameterName, final String serviceUrl, final boolean renew, final boolean gateway) {
try {
return URLEncoder.encode(value, "UTF-8");
return casServerLoginUrl + (casServerLoginUrl.indexOf("?") != -1 ? "&" : "?") + serviceParameterName + "="
+ URLEncoder.encode(serviceUrl, "UTF-8")
+ (renew ? "&renew=true" : "")
+ (gateway ? "&gateway=true" : "");
} 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 + "]");
}
proxyGrantingTicketStorage.save(proxyGrantingTicketIou, proxyGrantingTicket);
if (LOG.isDebugEnabled()) {
LOG.debug("Successfully saved proxyGrantingTicketId ["
+ proxyGrantingTicket + "] for proxyGrantingTicketIou ["
+ proxyGrantingTicketIou + "]");
}
LOGGER.debug("Received proxyGrantingTicketId [{}] for proxyGrantingTicketIou [{}]", proxyGrantingTicket,
proxyGrantingTicketIou);
proxyGrantingTicketStorage.save(proxyGrantingTicketIou, proxyGrantingTicket);
LOGGER.debug("Successfully saved proxyGrantingTicketId [{}] for proxyGrantingTicketIou [{}]",
proxyGrantingTicket, proxyGrantingTicketIou);
response.getWriter().write("<?xml version=\"1.0\"?>");
response.getWriter().write("<casClient:proxySuccess xmlns:casClient=\"http://www.yale.edu/tp/casClient\" />");
response.getWriter().write("<?xml version=\"1.0\"?>");
response.getWriter().write("<casClient:proxySuccess xmlns:casClient=\"http://www.yale.edu/tp/casClient\" />");
}
protected static String findMatchingServerName(final HttpServletRequest request, final String serverName) {
final String[] serverNames = serverName.split(" ");
if (serverNames.length == 0 || serverNames.length == 1) {
return serverName;
}
final String host = request.getHeader("Host");
final String xHost = request.getHeader("X-Forwarded-Host");
final String comparisonHost;
comparisonHost = (xHost != null) ? xHost : host;
if (comparisonHost == null) {
return serverName;
}
for (final String server : serverNames) {
final String lowerCaseServer = server.toLowerCase();
if (lowerCaseServer.contains(comparisonHost)) {
return server;
}
}
return serverNames[0];
}
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 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 serverName the server name to use to constuct the service url if the service param is empty
* @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 serviceParameterName,
final String artifactParameterName, final boolean encode) {
public static String constructServiceUrl(final HttpServletRequest request,
final HttpServletResponse response, final String service, final String serverName, final String artifactParameterName, final boolean encode) {
if (CommonUtils.isNotBlank(service)) {
return encode ? response.encodeURL(service) : service;
}
final String serverName = findMatchingServerName(request, serverNames);
final URIBuilder originalRequestUrl = new URIBuilder(request.getRequestURL().toString(), encode);
originalRequestUrl.setParameters(request.getQueryString());
final StringBuilder buffer = new StringBuilder();
final URIBuilder builder;
if (!serverName.startsWith("https://") && !serverName.startsWith("http://")) {
final String scheme = request.isSecure() ? "https://" : "http://";
builder = new URIBuilder(scheme + serverName, encode);
} else {
builder = new URIBuilder(serverName, encode);
buffer.append(request.isSecure() ? "https://" : "http://");
}
if (builder.getPort() == -1 && !requestIsOnStandardPort(request)) {
builder.setPort(request.getServerPort());
}
buffer.append(serverName);
buffer.append(request.getRequestURI());
builder.setEncodedPath(builder.getEncodedPath() + request.getRequestURI());
if (CommonUtils.isNotBlank(request.getQueryString())) {
final int location = request.getQueryString().indexOf(artifactParameterName + "=");
final List<String> serviceParameterNames = Arrays.asList(serviceParameterName.split(","));
if (!serviceParameterNames.isEmpty() && !originalRequestUrl.getQueryParams().isEmpty()) {
for (final URIBuilder.BasicNameValuePair pair : originalRequestUrl.getQueryParams()) {
final String name = pair.getName();
if (!name.equals(artifactParameterName) && !serviceParameterNames.contains(name)) {
if (name.contains("&") || name.contains("=")) {
final URIBuilder encodedParamBuilder = new URIBuilder();
encodedParamBuilder.setParameters(name);
for (final URIBuilder.BasicNameValuePair pair2 : encodedParamBuilder.getQueryParams()) {
final String name2 = pair2.getName();
if (!name2.equals(artifactParameterName) && !serviceParameterNames.contains(name2)) {
builder.addParameter(name2, pair2.getValue());
}
}
} else {
builder.addParameter(name, pair.getValue());
}
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 (location == -1) {
buffer.append(request.getQueryString());
} else if (location > 0) {
final int actualLocation = request.getQueryString()
.indexOf("&" + artifactParameterName + "=");
if (actualLocation == -1) {
buffer.append(request.getQueryString());
} else if (actualLocation > 0) {
buffer.append(request.getQueryString().substring(0,
actualLocation));
}
}
}
final String result = builder.toString();
final String returnValue = encode ? response.encodeURL(result) : result;
LOGGER.debug("serviceUrl generated: {}", returnValue);
final String returnValue = encode ? response.encodeURL(buffer.toString()) : buffer.toString();
if (LOG.isDebugEnabled()) {
LOG.debug("serviceUrl generated: " + returnValue);
}
return returnValue;
}
@ -383,29 +281,19 @@ public final class CommonUtils {
* parameter is ALWAYS in the GET request.
* <p>
* If we see the "logoutRequest" parameter we MUST treat it as if calling the standard request.getParameter.
* <p>
* Note, that as of 3.3.0, we've made it more generic.
* </p>
*
* @param request the request to check.
* @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<String> parameters) {
if ("POST".equals(request.getMethod()) && parameters.contains(parameter)) {
LOGGER.debug("safeGetParameter called on a POST HttpServletRequest for Restricted Parameters. Cannot complete check safely. Reverting to standard behavior for this Parameter");
public static String safeGetParameter(final HttpServletRequest request, final String parameter) {
if ("POST".equals(request.getMethod()) && "logoutRequest".equals(parameter)) {
LOG.debug("safeGetParameter called on a POST HttpServletRequest for LogoutRequest. 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().indexOf(parameter) == -1 ? 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.
*
@ -413,64 +301,64 @@ public final class CommonUtils {
* @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 IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
@Deprecated
public static String getResponseFromServer(final URL constructedUrl, final String encoding) {
return getResponseFromServer(constructedUrl, DEFAULT_URL_CONNECTION_FACTORY, encoding);
return getResponseFromServer(constructedUrl, HttpsURLConnection.getDefaultHostnameVerifier(), encoding);
}
/**
* Contacts the remote URL and returns the response.
*
* @param constructedUrl the url to contact.
* @param factory connection factory to prepare the URL connection instance
* @param hostnameVerifier Host name verifier to use for HTTPS connections.
* @param encoding the encoding to use.
* @return the response.
*/
public static String getResponseFromServer(final URL constructedUrl, final HttpURLConnectionFactory factory,
final String encoding) {
HttpURLConnection conn = null;
InputStreamReader in = null;
public static String getResponseFromServer(final URL constructedUrl, final HostnameVerifier hostnameVerifier, final String encoding) {
URLConnection conn = null;
try {
conn = factory.buildHttpURLConnection(constructedUrl.openConnection());
conn = constructedUrl.openConnection();
if (conn instanceof HttpsURLConnection) {
((HttpsURLConnection)conn).setHostnameVerifier(hostnameVerifier);
}
final BufferedReader in;
if (CommonUtils.isEmpty(encoding)) {
in = new InputStreamReader(conn.getInputStream());
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
} else {
in = new InputStreamReader(conn.getInputStream(), encoding);
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), encoding));
}
final StringBuilder builder = new StringBuilder(255);
int byteRead;
while ((byteRead = in.read()) != -1) {
builder.append((char) byteRead);
}
String line;
final StringBuilder stringBuffer = new StringBuilder(255);
return builder.toString();
} catch (final RuntimeException e) {
throw e;
} catch (final SSLException e) {
LOGGER.error("SSL error getting response from host: {} : Error Message: {}", constructedUrl.getHost(), e.getMessage(), e);
throw new RuntimeException(e);
} catch (final IOException e) {
LOGGER.error("Error getting response from host: [{}] with path: [{}] and protocol: [{}] Error Message: {}",
constructedUrl.getHost(), constructedUrl.getPath(), constructedUrl.getProtocol(), e.getMessage(), e);
while ((line = in.readLine()) != null) {
stringBuffer.append(line);
stringBuffer.append("\n");
}
return stringBuffer.toString();
} catch (final Exception e) {
LOG.error(e.getMessage(), e);
throw new RuntimeException(e);
} finally {
closeQuietly(in);
if (conn != null) {
conn.disconnect();
if (conn != null && conn instanceof HttpURLConnection) {
((HttpURLConnection)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) {
@ -481,7 +369,7 @@ public final class CommonUtils {
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.
@ -492,256 +380,9 @@ public final class CommonUtils {
public static void sendRedirect(final HttpServletResponse response, final String url) {
try {
response.sendRedirect(url);
} catch (final IOException e) {
LOGGER.warn(e.getMessage(), e);
} catch (final Exception e) {
LOG.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
}
}
/**
* <p>Converts a String to a boolean (optimised for performance).</p>
*
* <p>{@code 'true'}, {@code 'on'}, {@code 'y'}, {@code 't'} or {@code 'yes'}
* (case insensitive) will return {@code true}. Otherwise,
* {@code false} is returned.</p>
*
* <p>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.
*
* <pre>
* 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
* </pre>
*
* @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;
}
/**
* <p>Converts a String to a Boolean.</p>
*
* <p>{@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.</p>
*
* <p>NOTE: This returns null and will throw a NullPointerException if autoboxed to a boolean. </p>
*
* <pre>
* // 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
* </pre>
*
* @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;
}
/**
* <p>Convert a <code>String</code> to a <code>long</code>, returning a
* default value if the conversion fails.</p>
*
* <p>If the string is <code>null</code>, the default value is returned.</p>
*
* <pre>
* NumberUtils.toLong(null, 1L) = 1L
* NumberUtils.toLong("", 1L) = 1L
* NumberUtils.toLong("1", 0L) = 1L
* </pre>
*
* @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;
}
}
/**
* <p>Convert a <code>String</code> to an <code>int</code>, returning a
* default value if the conversion fails.</p>
*
* <p>If the string is <code>null</code>, the default value is returned.</p>
*
* <pre>
* NumberUtils.toInt(null, 1) = 1
* NumberUtils.toInt("", 1) = 1
* NumberUtils.toInt("1", 0) = 1
* </pre>
*
* @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;
}
}
/**
* Returns the string as-is, unless it's <code>null</code>;
* in this case an empty string is returned.
*
* @param string a possibly <code>null</code> string
* @return a non-<code>null</code> string
*/
public static String nullToEmpty(final String string) {
return string == null ? "" : string;
}
/**
* Adds a trailing slash to the given uri, if it doesn't already have one.
*
* @param uri a string that may or may not end with a slash
* @return the same string, except with a slash suffix (if necessary).
*/
public static String addTrailingSlash(final String uri) {
return uri.endsWith("/") ? uri : uri + "/";
}
}

View File

@ -1,29 +1,36 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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
@ -31,6 +38,7 @@ import org.slf4j.LoggerFactory;
* the associated filter is executed. Otherwise, the normal chain is executed.
*
* @author Scott Battaglia
* @version $Revision: 11729 $ $Date: 2006-09-26 14:22:30 -0400 (Tue, 26 Sep 2006) $
* @since 3.0
*/
public final class DelegatingFilter implements Filter {
@ -38,7 +46,7 @@ public final class DelegatingFilter implements Filter {
/**
* Instance of Commons Logging.
*/
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final Log log = LogFactory.getLog(this.getClass());
/**
* The request parameter to look for in the Request object.
@ -48,7 +56,7 @@ public final class DelegatingFilter implements Filter {
/**
* The map of filters to delegate to and the criteria (as key).
*/
private final Map<String, Filter> delegators;
private final Map<String,Filter> delegators;
/**
* The default filter to use if there is no match.
@ -61,13 +69,11 @@ public final class DelegatingFilter implements Filter {
*/
private final boolean exactMatch;
public DelegatingFilter(final String requestParameterName, final Map<String, Filter> delegators,
final boolean exactMatch) {
public DelegatingFilter(final String requestParameterName, final Map<String,Filter> delegators, final boolean exactMatch) {
this(requestParameterName, delegators, exactMatch, null);
}
public DelegatingFilter(final String requestParameterName, final Map<String, Filter> delegators,
final boolean exactMatch, final Filter defaultFilter) {
public DelegatingFilter(final String requestParameterName, final Map<String,Filter> delegators, final boolean exactMatch, final Filter defaultFilter) {
CommonUtils.assertNotNull(requestParameterName, "requestParameterName cannot be null.");
CommonUtils.assertTrue(!delegators.isEmpty(), "delegators cannot be empty.");
@ -77,14 +83,11 @@ public final class DelegatingFilter implements Filter {
this.exactMatch = exactMatch;
}
@Override
public void destroy() {
// nothing to do here
}
@Override
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);
@ -92,15 +95,19 @@ public final class DelegatingFilter implements Filter {
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);
logger.debug("Match found for parameter [{}] with value [{}]. Delegating to filter [{}]",
this.requestParameterName, parameter, filter.getClass().getName());
if (log.isDebugEnabled()) {
log.debug("Match found for parameter ["
+ this.requestParameterName + "] with value ["
+ parameter + "]. Delegating to filter ["
+ filter.getClass().getName() + "]");
}
filter.doFilter(request, response, filterChain);
return;
}
}
}
logger.debug("No match found for parameter [{}] with value [{}]", this.requestParameterName, parameter);
log.debug("No match found for parameter [" + this.requestParameterName + "] with value [" + parameter + "]");
if (this.defaultFilter != null) {
this.defaultFilter.doFilter(request, response, filterChain);
@ -109,7 +116,6 @@ public final class DelegatingFilter implements Filter {
}
}
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
// nothing to do here.
}

View File

@ -1,139 +1,128 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.*;
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.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Filters that redirects to the supplied url based on an exception. Exceptions and the urls are configured via
* init filter name/param values.
* <p/>
* <p>
* If there is an exact match the filter uses that value. If there's a non-exact match (i.e. inheritance), then the filter
* uses the last value that matched.
* <p/>
* <p>
* If there is no match it will redirect to a default error page. The default exception is configured via the "defaultErrorRedirectPage" property.
*
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1.4
*
*/
public final class ErrorRedirectFilter implements Filter {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final Log log = LogFactory.getLog(getClass());
private final List<ErrorHolder> errors = new ArrayList<ErrorHolder>();
private String defaultErrorRedirectPage;
public void destroy() {
// nothing to do here
}
private final List<ErrorHolder> errors = new ArrayList<ErrorHolder>();
private String defaultErrorRedirectPage;
@Override
public void destroy() {
// nothing to do here
}
@Override
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;
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;
for (final ErrorHolder errorHolder : this.errors) {
if (errorHolder.exactMatch(t)) {
currentMatch = errorHolder;
break;
} else if (errorHolder.inheritanceMatch(t)) {
currentMatch = errorHolder;
}
}
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);
}
}
}
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;
}
@Override
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;
}
}
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;
}
}
}

View File

@ -1,33 +1,38 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.security.Principal;
import java.util.Collection;
import javax.servlet.*;
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 org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.jasig.cas.client.validation.Assertion;
import java.io.IOException;
import java.security.Principal;
import java.util.Collection;
/**
* Implementation of a filter that wraps the normal HttpServletRequest with a
@ -44,17 +49,17 @@ import org.jasig.cas.client.validation.Assertion;
*
* @author Scott Battaglia
* @author Marvin S. Addison
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public final class HttpServletRequestWrapperFilter extends AbstractConfigurationFilter {
/** 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;
@Override
public void destroy() {
// nothing to do
}
@ -64,30 +69,23 @@ public final class HttpServletRequestWrapperFilter extends AbstractConfiguration
* <code>request.getRemoteUser</code> to the underlying Assertion object
* stored in the user session.
*/
@Override
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();
}
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
super.init(filterConfig);
this.roleAttribute = getString(ConfigurationKeys.ROLE_ATTRIBUTE);
this.ignoreCase = getBoolean(ConfigurationKeys.IGNORE_CASE);
this.roleAttribute = getPropertyFromInitParams(filterConfig, "roleAttribute", null);
this.ignoreCase = Boolean.parseBoolean(getPropertyFromInitParams(filterConfig, "ignoreCase", "false"));
}
final class CasHttpServletRequestWrapper extends HttpServletRequestWrapper {
@ -99,49 +97,46 @@ public final class HttpServletRequestWrapperFilter extends AbstractConfiguration
this.principal = principal;
}
@Override
public Principal getUserPrincipal() {
return this.principal;
}
@Override
public String getRemoteUser() {
return principal != null ? this.principal.getName() : null;
}
@Override
public boolean isUserInRole(final String role) {
if (CommonUtils.isBlank(role)) {
logger.debug("No valid role provided. Returning false.");
log.debug("No valid role provided. Returning false.");
return false;
}
if (this.principal == null) {
logger.debug("No Principal in Request. Returning false.");
log.debug("No Principal in Request. Returning false.");
return false;
}
if (CommonUtils.isBlank(roleAttribute)) {
logger.debug("No Role Attribute Configured. Returning false.");
log.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)) {
logger.debug("User [{}] is in role [{}]: true", getRemoteUser(), role);
log.debug("User [" + getRemoteUser() + "] is in role [" + role + "]: " + true);
return true;
}
}
}
final boolean isMember = rolesEqual(role, value);
logger.debug("User [{}] is in role [{}]: {}", getRemoteUser(), role, isMember);
log.debug("User [" + getRemoteUser() + "] is in role [" + role + "]: " + isMember);
return isMember;
}
/**
* Determines whether the given role is equal to the candidate
* role attribute taking into account case sensitivity.

View File

@ -1,92 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.util;
import java.io.*;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
/**
* IO utility class.
*
* @author Marvin S. Addison
* @since 3.4
*/
public final class IOUtils {
/** UTF-8 character set. */
public static final Charset UTF8 = Charset.forName("UTF-8");
private IOUtils() { /** Utility class pattern. */ }
/**
* Reads all data from the given stream as UTF-8 character data and closes it on completion or errors.
*
* @param in Input stream containing character data.
*
* @return String of all data in stream.
*
* @throws IOException On IO errors.
*/
public static String readString(final InputStream in) throws IOException {
return readString(in, UTF8);
}
/**
* Reads all data from the given stream as character data in the given character set and closes it on completion
* or errors.
*
* @param in Input stream containing character data.
* @param charset Character set of data in stream.
*
* @return String of all data in stream.
*
* @throws IOException On IO errors.
*/
public static String readString(final InputStream in, final Charset charset) throws IOException {
final Reader reader = new InputStreamReader(in, charset);
final StringBuilder builder = new StringBuilder();
final CharBuffer buffer = CharBuffer.allocate(2048);
try {
while (reader.read(buffer) > -1) {
buffer.flip();
builder.append(buffer);
}
} finally {
closeQuietly(reader);
}
return builder.toString();
}
/**
* Unconditionally close a {@link Closeable} resource. Errors on close are ignored.
*
* @param resource Resource to close.
*/
public static void closeQuietly(final Closeable resource) {
try {
if (resource != null) {
resource.close();
}
} catch (final IOException e) {
//ignore
}
}
}

View File

@ -1,83 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.util;
import javax.xml.namespace.NamespaceContext;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Namespace context implementation backed by a map of XML prefixes to namespace URIs.
*
* @author Marvin S. Addison
* @since 3.4
*/
public class MapNamespaceContext implements NamespaceContext {
private final Map<String, String> namespaceMap;
/**
* Creates a new instance from an array of namespace delcarations.
*
* @param namespaceDeclarations An array of namespace declarations of the form prefix->uri.
*/
public MapNamespaceContext(final String ... namespaceDeclarations) {
namespaceMap = new HashMap<String, String>();
int index;
String key;
String value;
for (final String decl : namespaceDeclarations) {
index = decl.indexOf('-');
key = decl.substring(0, index);
value = decl.substring(index + 2);
namespaceMap.put(key, value);
}
}
/**
* Creates a new instance from a map.
*
* @param namespaceMap Map of XML namespace prefixes (keys) to URIs (values).
*/
public MapNamespaceContext(final Map<String, String> namespaceMap) {
this.namespaceMap = namespaceMap;
}
@Override
public String getNamespaceURI(final String prefix) {
return namespaceMap.get(prefix);
}
@Override
public String getPrefix(final String namespaceURI) {
for (final Map.Entry<String, String> entry : namespaceMap.entrySet()) {
if (entry.getValue().equalsIgnoreCase(namespaceURI)) {
return entry.getKey();
}
}
return null;
}
@Override
public Iterator getPrefixes(final String namespaceURI) {
return Collections.singleton(getPrefix(namespaceURI)).iterator();
}
}

View File

@ -1,108 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.util;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.Security;
import java.security.spec.PKCS8EncodedKeySpec;
/**
* Utility class to parse private keys.
*
* @author Jerome LELEU
* @since 3.6.0
*/
public class PrivateKeyUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(PrivateKeyUtils.class);
static {
Security.addProvider(new BouncyCastleProvider());
}
public static PrivateKey createKey(final String path, final String algorithm) {
final PrivateKey key = readPemPrivateKey(path);
if (key == null) {
return readDERPrivateKey(path, algorithm);
} else {
return key;
}
}
private static PrivateKey readPemPrivateKey(final String path) {
LOGGER.debug("Attempting to read as PEM [{}]", path);
final File file = new File(path);
InputStreamReader isr = null;
BufferedReader br = null;
try {
isr = new FileReader(file);
br = new BufferedReader(isr);
final PEMParser pp = new PEMParser(br);
final PEMKeyPair pemKeyPair = (PEMKeyPair) pp.readObject();
final KeyPair kp = new JcaPEMKeyConverter().getKeyPair(pemKeyPair);
return kp.getPrivate();
} catch (final Exception e) {
LOGGER.error("Unable to read key", e);
return null;
} finally {
try {
if (br != null) {
br.close();
}
if (isr != null) {
isr.close();
}
} catch (final IOException e) {}
}
}
private static PrivateKey readDERPrivateKey(final String path, final String algorithm) {
LOGGER.debug("Attempting to read key as DER [{}]", path);
final File file = new File(path);
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
final long byteLength = file.length();
final byte[] bytes = new byte[(int) byteLength];
fis.read(bytes, 0, (int) byteLength);
final PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(bytes);
final KeyFactory factory = KeyFactory.getInstance(algorithm);
return factory.generatePrivate(privSpec);
} catch (final Exception e) {
LOGGER.error("Unable to read key", e);
return null;
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (final IOException e) {}
}
}
}

View File

@ -1,28 +1,28 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
@ -54,6 +54,7 @@ public final class ReflectUtils {
}
}
/**
* Creates a new instance of the given class by passing the given arguments
* to the constructor.
@ -61,10 +62,10 @@ public final class ReflectUtils {
* @param args Constructor arguments.
* @return New instance of given class.
*/
public static <T> T newInstance(final String className, final Object... args) {
return newInstance(ReflectUtils.<T> loadClass(className), args);
public static <T> T newInstance(final String className, final Object ... args) {
return newInstance(ReflectUtils.<T>loadClass(className), args);
}
/**
* Creates a new instance of the given class by passing the given arguments
* to the constructor.
@ -72,7 +73,7 @@ public final class ReflectUtils {
* @param args Constructor arguments.
* @return New instance of given class.
*/
public static <T> T newInstance(final Class<T> clazz, final Object... args) {
public static <T> T newInstance(final Class<T> clazz, final Object ... args) {
final Class<?>[] argClasses = new Class[args.length];
for (int i = 0; i < args.length; i++) {
argClasses[i] = args[i].getClass();
@ -138,8 +139,7 @@ public final class ReflectUtils {
* @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);
@ -149,35 +149,4 @@ public final class ReflectUtils {
throw new RuntimeException("Error setting property " + propertyName, e);
}
}
/**
* Gets the value of the given declared field on the target object or any of its superclasses.
*
* @param fieldName Name of field to get.
* @param target Target object that possesses field.
*
* @return Field value.
*/
public static Object getField(final String fieldName, final Object target) {
Class<?> clazz = target.getClass();
Field field = null;
do {
try {
field = clazz.getDeclaredField(fieldName);
} catch (final NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
} while (field == null && clazz != null);
if (field == null) {
throw new IllegalArgumentException(fieldName + " does not exist on " + target);
}
try {
if (!field.isAccessible()) {
field.setAccessible(true);
}
return field.get(target);
} catch (final Exception e) {
throw new IllegalArgumentException("Error getting field " + fieldName, e);
}
}
}

View File

@ -1,109 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.util;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.xpath.*;
/**
* Thread local XPath expression.
*
* @author Marvin S. Addison
* @since 3.4
*/
public class ThreadLocalXPathExpression extends ThreadLocal<XPathExpression> implements XPathExpression {
/** XPath expression */
private final String expression;
/** Namespace context. */
private final NamespaceContext context;
/**
* Creates a new instance from an XPath expression and namespace context.
*
* @param xPath XPath expression.
* @param context Namespace context for handling namespace prefix to URI mappings.
*/
public ThreadLocalXPathExpression(final String xPath, final NamespaceContext context) {
this.expression = xPath;
this.context = context;
}
@Override
public Object evaluate(final Object o, final QName qName) throws XPathExpressionException {
return get().evaluate(o, qName);
}
@Override
public String evaluate(final Object o) throws XPathExpressionException {
return get().evaluate(o);
}
@Override
public Object evaluate(final InputSource inputSource, final QName qName) throws XPathExpressionException {
return get().evaluate(inputSource, qName);
}
@Override
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 (final XPathExpressionException e) {
throw new IllegalArgumentException("Invalid XPath expression");
}
}
}

View File

@ -1,682 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.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.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<BasicNameValuePair> 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, final 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<BasicNameValuePair> parseQuery(final String query) {
try {
final Charset utf8 = Charset.forName("UTF-8");
if (query != null && !query.isEmpty()) {
final List<BasicNameValuePair> list = new ArrayList<BasicNameValuePair>();
final String[] parametersArray = query.split("&");
for (final String parameter : parametersArray) {
final int firstIndex = parameter.indexOf("=");
if (firstIndex != -1) {
final String paramName = parameter.substring(0, firstIndex);
final String decodedParamName = URLDecoder.decode(paramName, utf8.name());
final String paramVal = parameter.substring(firstIndex + 1);
final String decodedParamVal = URLDecoder.decode(paramVal, utf8.name());
list.add(new BasicNameValuePair(decodedParamName, decodedParamVal));
} else {
// Either we do not have a query parameter, or it might be encoded; take it verbaitm
final String[] parameterCombo = parameter.split("=");
if (parameterCombo.length >= 1) {
final String key = URLDecoder.decode(parameterCombo[0], utf8.name());
final String val = parameterCombo.length == 2 ? URLDecoder.decode(parameterCombo[1], utf8.name()) : "";
list.add(new BasicNameValuePair(key, val));
}
}
}
return list;
}
} catch (final UnsupportedEncodingException e) {
LOGGER.error(e.getMessage(), e);
}
return new ArrayList<BasicNameValuePair>();
}
/**
* 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<BasicNameValuePair> 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(final 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.
* <p>
* Please note query parameters and custom query component are mutually exclusive. This method
* will remove custom query if present.
* </p>
*/
public URIBuilder setParameters(final List<BasicNameValuePair> nvps) {
this.queryParams = new ArrayList<BasicNameValuePair>();
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<BasicNameValuePair>();
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.
* <p>
* Please note query parameters and custom query component are mutually exclusive. This method
* will remove custom query if present.
* </p>
*/
public URIBuilder addParameters(final List<BasicNameValuePair> nvps) {
if (this.queryParams == null || this.queryParams.isEmpty()) {
this.queryParams = new ArrayList<BasicNameValuePair>();
}
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.
* <p>
* Please note query parameters and custom query component are mutually exclusive. This method
* will remove custom query if present.
* </p>
*/
public URIBuilder setParameters(final BasicNameValuePair... nvps) {
if (this.queryParams == null) {
this.queryParams = new ArrayList<BasicNameValuePair>();
} 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.
* <p>
* Please note query parameters and custom query component are mutually exclusive. This method
* will remove custom query if present.
* </p>
*/
public URIBuilder addParameter(final String param, final String value) {
if (this.queryParams == null) {
this.queryParams = new ArrayList<BasicNameValuePair>();
}
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.
* <p>
* Please note query parameters and custom query component are mutually exclusive. This method
* will remove custom query if present.
* </p>
*/
public URIBuilder setParameter(final String param, final String value) {
if (this.queryParams == null) {
this.queryParams = new ArrayList<BasicNameValuePair>();
}
if (!this.queryParams.isEmpty()) {
for (final Iterator<BasicNameValuePair> 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.
* <p>
* Please note query parameters and custom query component are mutually exclusive. This method
* will remove query parameters if present.
* </p>
*/
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 String getEncodedPath() {
return this.encodedPath;
}
public List<BasicNameValuePair> getQueryParams() {
if (this.queryParams != null) {
return new ArrayList<BasicNameValuePair>(this.queryParams);
}
return new ArrayList<BasicNameValuePair>();
}
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();
}
}
}

View File

@ -1,44 +1,42 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.StringReader;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.XMLConstants;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
/**
* Common utilities for easily parsing XML without duplicating logic.
*
* @author Scott Battaglia
* @version $Revision: 11729 $ $Date: 2007-09-26 14:22:30 -0400 (Tue, 26 Sep 2007) $
* @since 3.0
*/
public final class XmlUtils {
@ -46,37 +44,7 @@ public final class XmlUtils {
/**
* Static instance of Commons Logging.
*/
private final static Logger LOGGER = LoggerFactory.getLogger(XmlUtils.class);
/**
* Creates a new namespace-aware DOM document object by parsing the given XML.
*
* @param xml XML content.
*
* @return DOM document.
*/
public static Document newDocument(final String xml) {
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
final Map<String, Boolean> features = new HashMap<String, Boolean>();
features.put(XMLConstants.FEATURE_SECURE_PROCESSING, true);
features.put("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
features.put("http://apache.org/xml/features/disallow-doctype-decl", true);
for (final Map.Entry<String, Boolean> entry : features.entrySet()) {
try {
factory.setFeature(entry.getKey(), entry.getValue());
} catch (final ParserConfigurationException e) {
LOGGER.warn("Failed setting XML feature {}: {}", entry.getKey(), e);
}
}
factory.setExpandEntityReferences(false);
factory.setNamespaceAware(true);
try {
return factory.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
} catch (final Exception e) {
throw new RuntimeException("XML parsing error: " + e);
}
}
private final static Log LOG = LogFactory.getLog(XmlUtils.class);
/**
* Get an instance of an XML reader from the XMLReaderFactory.
@ -85,20 +53,12 @@ public final class XmlUtils {
*/
public static XMLReader getXmlReader() {
try {
final SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
return factory.newSAXParser().getXMLReader();
} catch (final Exception e) {
return XMLReaderFactory.createXMLReader();
} catch (final SAXException 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.
@ -108,7 +68,8 @@ public final class XmlUtils {
* @param element the element to look for
* @return the list of text from the elements.
*/
public static List<String> getTextForElements(final String xmlAsString, final String element) {
public static List<String> getTextForElements(final String xmlAsString,
final String element) {
final List<String> elements = new ArrayList<String>(2);
final XMLReader reader = getXmlReader();
@ -118,16 +79,16 @@ public final class XmlUtils {
private StringBuilder buffer = new StringBuilder();
@Override
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;
}
}
@Override
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());
@ -135,8 +96,8 @@ public final class XmlUtils {
}
}
@Override
public void characters(final char[] ch, final int start, final int length) throws SAXException {
public void characters(char[] ch, int start, int length)
throws SAXException {
if (this.foundElement) {
this.buffer.append(ch, start, length);
}
@ -149,7 +110,7 @@ public final class XmlUtils {
try {
reader.parse(new InputSource(new StringReader(xmlAsString)));
} catch (final Exception e) {
LOGGER.error(e.getMessage(), e);
LOG.error(e, e);
return null;
}
@ -164,7 +125,8 @@ public final class XmlUtils {
* @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();
@ -172,23 +134,23 @@ public final class XmlUtils {
private boolean foundElement = false;
@Override
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;
}
}
@Override
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;
}
}
@Override
public void characters(final char[] ch, final int start, final int length) throws SAXException {
public void characters(char[] ch, int start, int length)
throws SAXException {
if (this.foundElement) {
builder.append(ch, start, length);
}
@ -201,7 +163,7 @@ public final class XmlUtils {
try {
reader.parse(new InputSource(new StringReader(xmlAsString)));
} catch (final Exception e) {
LOGGER.error(e.getMessage(), e);
LOG.error(e, e);
return null;
}

View File

@ -1,23 +1,24 @@
<!--
Licensed to Apereo under one or more contributor license
Licensed to Jasig 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,
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:
except in compliance with the License. You may obtain a
copy of the License at:
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.
-->
<html>
<body>
<p>The validation package includes interfaces for validating Tickets, as well as the common implementations.</p>

View File

@ -1,26 +1,28 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.net.URL;
import org.jasig.cas.client.util.CommonUtils;
import java.net.URL;
/**
* Abstract class that knows the protocol for validating a CAS ticket.
*
@ -34,11 +36,18 @@ public abstract class AbstractCasProtocolUrlBasedTicketValidator extends Abstrac
super(casServerUrlPrefix);
}
protected final void setDisableXmlSchemaValidation(final boolean disable) {
// nothing to do
}
/**
* Retrieves the response from the server by opening a connection and merely reading the response.
*/
@Override
protected final String retrieveResponseFromServer(final URL validationUrl, final String ticket) {
return CommonUtils.getResponseFromServer(validationUrl, getURLConnectionFactory(), getEncoding());
if (this.hostnameVerifier != null) {
return CommonUtils.getResponseFromServer(validationUrl, this.hostnameVerifier, getEncoding());
} else {
return CommonUtils.getResponseFromServer(validationUrl, getEncoding());
}
}
}

View File

@ -1,49 +1,47 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.FileInputStream;
import java.io.IOException;
import java.util.Properties;
import javax.net.ssl.HostnameVerifier;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jasig.cas.client.Protocol;
import org.jasig.cas.client.configuration.ConfigurationKeys;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.ReflectUtils;
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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* The filter that handles all the work of validating ticket requests.
* <p>
* This filter can be configured with the following values:
* <ul>
* <li><code>redirectAfterValidation</code> - redirect the CAS client to the same URL without the ticket.
* (default: true, Will be forced to false when {@link #useSession} is false.)</li>
* <li><code>redirectAfterValidation</code> - redirect the CAS client to the same URL without the ticket.</li>
* <li><code>exceptionOnValidationFailure</code> - throw an exception if the validation fails. Otherwise, continue
* processing. (default: true)</li>
* <li><code>useSession</code> - store any of the useful information in a session attribute. (default: true)</li>
* <li><code>hostnameVerifier</code> - name of class implementing a {@link HostnameVerifier}.</li>
* <li><code>hostnameVerifierConfig</code> - name of configuration class (constructor argument of verifier).</li>
* processing.</li>
* <li><code>useSession</code> - store any of the useful information in a session attribute.</li>
* </ul>
*
* @author Scott Battaglia
@ -60,21 +58,13 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
* successful validation to remove the ticket parameter from the query
* string.
*/
private boolean redirectAfterValidation = true;
private boolean redirectAfterValidation = false;
/** Determines whether an exception is thrown when there is a ticket validation failure. */
private boolean exceptionOnValidationFailure = false;
private boolean exceptionOnValidationFailure = true;
/**
* 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.
*
@ -84,39 +74,18 @@ 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 Class<? extends HostnameVerifier> className = getClass(ConfigurationKeys.HOSTNAME_VERIFIER);
final String config = getString(ConfigurationKeys.HOSTNAME_VERIFIER_CONFIG);
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);
if (className != null) {
if (config != null) {
return ReflectUtils.newInstance(className, config);
@ -127,22 +96,17 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
return null;
}
@Override
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
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);
}
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);
setTicketValidator(getTicketValidator(filterConfig));
super.initInternal(filterConfig);
}
@Override
public void init() {
super.init();
CommonUtils.assertNotNull(this.ticketValidator, "ticketValidator cannot be null.");
@ -158,8 +122,7 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
* @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;
}
@ -172,9 +135,8 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
* @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.
}
/**
@ -188,9 +150,7 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
// nothing to do here.
}
@Override
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;
@ -198,16 +158,19 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final String ticket = retrieveTicketFromRequest(request);
final String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());
if (CommonUtils.isNotBlank(ticket)) {
logger.debug("Attempting to validate ticket: {}", ticket);
if (log.isDebugEnabled()) {
log.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));
logger.debug("Successfully authenticated user: {}", assertion.getPrincipal().getName());
if (log.isDebugEnabled()) {
log.debug("Successfully authenticated user: " + assertion.getPrincipal().getName());
}
request.setAttribute(CONST_CAS_ASSERTION, assertion);
@ -217,12 +180,13 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
onSuccessfulValidation(request, response, assertion);
if (this.redirectAfterValidation) {
logger.debug("Redirecting after successful ticket validation.");
log. debug("Redirecting after successful ticket validation.");
response.sendRedirect(constructServiceUrl(request, response));
return;
}
} catch (final TicketValidationException e) {
logger.debug(e.getMessage(), e);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
log.warn(e, e);
onFailedValidation(request, response);
@ -230,8 +194,6 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
throw new ServletException(e);
}
response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
return;
}
}
@ -241,8 +203,8 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
}
public final void setTicketValidator(final TicketValidator ticketValidator) {
this.ticketValidator = ticketValidator;
}
this.ticketValidator = ticketValidator;
}
public final void setRedirectAfterValidation(final boolean redirectAfterValidation) {
this.redirectAfterValidation = redirectAfterValidation;
@ -255,4 +217,8 @@ public abstract class AbstractTicketValidationFilter extends AbstractCasFilter {
public final void setUseSession(final boolean useSession) {
this.useSession = useSession;
}
}

View File

@ -1,50 +1,55 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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 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;
import javax.net.ssl.HostnameVerifier;
/**
* 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());
/**
* URLConnection factory instance to use when making validation requests to the CAS server.
* Defaults to {@link HttpsURLConnectionFactory}
* Commons Logging instance.
*/
private HttpURLConnectionFactory urlConnectionFactory = new HttpsURLConnectionFactory();
protected final Log log = LogFactory.getLog(getClass());
/**
* Hostname verifier used when making an SSL request to the CAS server.
*/
protected HostnameVerifier hostnameVerifier;
/**
* Prefix for the CAS server. Should be everything up to the url endpoint, including the /.
@ -61,7 +66,7 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
/**
* A map containing custom parameters to pass to the validation url.
*/
private Map<String, String> customParameters;
private Map<String,String> customParameters;
private String encoding;
@ -71,8 +76,8 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
* @param casServerUrlPrefix the location of the CAS server.
*/
protected AbstractUrlBasedTicketValidator(final String casServerUrlPrefix) {
CommonUtils.assertNotNull(casServerUrlPrefix, "casServerUrlPrefix cannot be null.");
this.casServerUrlPrefix = CommonUtils.addTrailingSlash(casServerUrlPrefix);
this.casServerUrlPrefix = casServerUrlPrefix;
CommonUtils.assertNotNull(this.casServerUrlPrefix, "casServerUrlPrefix cannot be null.");
}
/**
@ -80,7 +85,7 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
*
* @param urlParameters the map containing the parameters.
*/
protected void populateUrlAttributeMap(final Map<String, String> urlParameters) {
protected void populateUrlAttributeMap(final Map<String,String> urlParameters) {
// nothing to do
}
@ -90,6 +95,13 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
*/
protected abstract String getUrlSuffix();
/**
* Disable XML Schema validation. Note, setting this to true may not be reversable. Defaults to false. Setting it to false
* after setting it to true may not have any affect.
*
* @param disabled whether to disable or not.
*/
protected abstract void setDisableXmlSchemaValidation(boolean disabled);
/**
* Constructs the URL to send the validation request to.
@ -99,9 +111,9 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
* @return the fully constructed URL.
*/
protected final String constructValidationUrl(final String ticket, final String serviceUrl) {
final Map<String, String> urlParameters = new HashMap<String, String>();
final Map<String,String> urlParameters = new HashMap<String,String>();
logger.debug("Placing URL parameters in map.");
log.debug("Placing URL parameters in map.");
urlParameters.put("ticket", ticket);
urlParameters.put("service", serviceUrl);
@ -109,24 +121,26 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
urlParameters.put("renew", "true");
}
logger.debug("Calling template URL attribute map.");
log.debug("Calling template URL attribute map.");
populateUrlAttributeMap(urlParameters);
logger.debug("Loading custom parameters from configuration.");
log.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;
buffer.append(this.casServerUrlPrefix);
if (!this.casServerUrlPrefix.endsWith("/")) {
buffer.append("/");
}
buffer.append(suffix);
for (final Map.Entry<String, String> entry : urlParameters.entrySet()) {
for (Map.Entry<String,String> entry : urlParameters.entrySet()) {
final String key = entry.getKey();
final String value = entry.getValue();
@ -150,10 +164,10 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
* @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) {
@ -181,20 +195,25 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
protected abstract String retrieveResponseFromServer(URL validationUrl, String ticket);
@Override
public final Assertion validate(final String ticket, final String service) throws TicketValidationException {
public Assertion validate(final String ticket, final String service) throws TicketValidationException {
final String validationUrl = constructValidationUrl(ticket, service);
logger.debug("Constructing validation url: {}", validationUrl);
if (log.isDebugEnabled()) {
log.debug("Constructing validation url: " + validationUrl);
}
try {
logger.debug("Retrieving response from server.");
log.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.");
}
logger.debug("Server response: {}", serverResponse);
if (log.isDebugEnabled()) {
log.debug("Server response: " + serverResponse);
}
return parseResponseFromServer(serverResponse);
} catch (final MalformedURLException e) {
@ -206,9 +225,13 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
this.renew = renew;
}
public final void setCustomParameters(final Map<String, String> customParameters) {
public final void setCustomParameters(final Map<String,String> customParameters) {
this.customParameters = customParameters;
}
public final void setHostnameVerifier(final HostnameVerifier verifier) {
this.hostnameVerifier = verifier;
}
public final void setEncoding(final String encoding) {
this.encoding = encoding;
@ -217,24 +240,4 @@ public abstract class AbstractUrlBasedTicketValidator implements TicketValidator
protected final String getEncoding() {
return this.encoding;
}
protected final boolean isRenew() {
return this.renew;
}
protected final String getCasServerUrlPrefix() {
return this.casServerUrlPrefix;
}
protected final Map<String, String> getCustomParameters() {
return this.customParameters;
}
protected HttpURLConnectionFactory getURLConnectionFactory() {
return this.urlConnectionFactory;
}
public void setURLConnectionFactory(final HttpURLConnectionFactory urlConnectionFactory) {
this.urlConnectionFactory = urlConnectionFactory;
}
}

View File

@ -1,27 +1,29 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.
@ -46,20 +48,12 @@ 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<String, Object> getAttributes();
Map<String,Object> getAttributes();
/**
* The principal for which this assertion is valid.
@ -67,12 +61,4 @@ 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();
}

View File

@ -1,29 +1,31 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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}.
@ -36,18 +38,16 @@ import org.jasig.cas.client.util.CommonUtils;
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<String, Object> attributes;
private final Map<String,Object> 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 final class AssertionImpl implements Assertion {
* @param principal the Principal to associate with the Assertion.
*/
public AssertionImpl(final AttributePrincipal principal) {
this(principal, Collections.<String, Object> emptyMap());
this(principal, Collections.<String, Object>emptyMap());
}
/**
@ -76,8 +76,8 @@ public final class AssertionImpl implements Assertion {
* @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<String, Object> attributes) {
this(principal, new Date(), null, new Date(), attributes);
public AssertionImpl(final AttributePrincipal principal, final Map<String,Object> attributes) {
this(principal, new Date(), null, attributes);
}
/**
@ -88,52 +88,29 @@ public final class AssertionImpl implements Assertion {
* @param validUntilDate when the assertion is valid to.
* @param attributes the key/value pairs for this attribute.
*/
public AssertionImpl(final AttributePrincipal principal, final Date validFromDate, final Date validUntilDate,
final Date authenticationDate, final Map<String, Object> attributes) {
public AssertionImpl(final AttributePrincipal principal, final Date validFromDate, final Date validUntilDate, final Map<String,Object> 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.");
}
@Override
public Date getAuthenticationDate() {
return this.authenticationDate;
}
@Override
public Date getValidFromDate() {
return this.validFromDate;
}
@Override
public Date getValidUntilDate() {
return this.validUntilDate;
}
@Override
public Map<String, Object> getAttributes() {
public Map<String,Object> getAttributes() {
return this.attributes;
}
@Override
public AttributePrincipal getPrincipal() {
return this.principal;
}
@Override
public boolean isValid() {
if (this.validFromDate == null) {
return true;
}
final Date now = new Date();
return (this.validFromDate.before(now) || this.validFromDate.equals(now))
&& (this.validUntilDate == null || this.validUntilDate.after(now) || this.validUntilDate.equals(now));
}
}

View File

@ -1,55 +1,43 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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 creates a Cas10TicketValidator.
* Implementation of AbstractTicketValidatorFilter that instanciates a Cas10TicketValidator.
* <p>Deployers can provide the "casServerPrefix" and the "renew" attributes via the standard context or filter init
* parameters.
*
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public class Cas10TicketValidationFilter extends AbstractTicketValidationFilter {
public Cas10TicketValidationFilter() {
super(Protocol.CAS1);
}
@Override
protected final TicketValidator getTicketValidator(final FilterConfig filterConfig) {
final String casServerUrlPrefix = getString(ConfigurationKeys.CAS_SERVER_URL_PREFIX);
final String casServerUrlPrefix = getPropertyFromInitParams(filterConfig, "casServerUrlPrefix", null);
final Cas10TicketValidator validator = new Cas10TicketValidator(casServerUrlPrefix);
validator.setRenew(getBoolean(ConfigurationKeys.RENEW));
final HttpURLConnectionFactory factory = new HttpsURLConnectionFactory(getHostnameVerifier(),
getSSLConfig());
validator.setURLConnectionFactory(factory);
validator.setEncoding(getString(ConfigurationKeys.ENCODING));
validator.setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
validator.setHostnameVerifier(getHostnameVerifier(filterConfig));
validator.setEncoding(getPropertyFromInitParams(filterConfig, "encoding", null));
return validator;
}

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
@ -35,12 +36,10 @@ public final class Cas10TicketValidator extends AbstractCasProtocolUrlBasedTicke
super(casServerUrlPrefix);
}
@Override
protected String getUrlSuffix() {
return "validate";
}
@Override
protected Assertion parseResponseFromServer(final String response) throws TicketValidationException {
if (!response.startsWith("yes")) {
throw new TicketValidationException("CAS Server could not validate ticket.");

View File

@ -1,45 +1,43 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.security.PrivateKey;
import java.util.*;
import javax.servlet.*;
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.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.PrivateKeyUtils;
import org.jasig.cas.client.util.ReflectUtils;
import static org.jasig.cas.client.configuration.ConfigurationKeys.*;
/**
* Creates either a CAS20ProxyTicketValidator or a CAS20ServiceTicketValidator depending on whether any of the
* proxy parameters are set.
* <p/>
* <p>
* This filter can also pass additional parameters to the ticket validator. Any init parameter not included in the
* reserved list {@link org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter#RESERVED_INIT_PARAMS}.
*
@ -47,20 +45,16 @@ import static org.jasig.cas.client.configuration.ConfigurationKeys.*;
* @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[]{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(), RELAY_STATE_PARAMETER_NAME.getName(), METHOD.getName(), PRIVATE_KEY_PATH.getName(), PRIVATE_KEY_ALGORITHM.getName()
};
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;
/**
* 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;
@ -69,41 +63,24 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
private TimerTask timerTask;
private int millisBetweenCleanUps;
protected Class<? extends Cas20ServiceTicketValidator> defaultServiceTicketValidatorClass;
protected Class<? extends Cas20ProxyTicketValidator> defaultProxyTicketValidatorClass;
private PrivateKey privateKey;
/**
* 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);
}
@Override
protected void initInternal(final FilterConfig filterConfig) throws ServletException {
setProxyReceptorUrl(getString(ConfigurationKeys.PROXY_RECEPTOR_URL));
setProxyReceptorUrl(getPropertyFromInitParams(filterConfig, "proxyReceptorUrl", null));
final Class<? extends ProxyGrantingTicketStorage> proxyGrantingTicketStorageClass = getClass(ConfigurationKeys.PROXY_GRANTING_TICKET_STORAGE_CLASS);
final String proxyGrantingTicketStorageClass = getPropertyFromInitParams(filterConfig, "proxyGrantingTicketStorageClass", null);
if (proxyGrantingTicketStorageClass != null) {
this.proxyGrantingTicketStorage = ReflectUtils.newInstance(proxyGrantingTicketStorageClass);
if (this.proxyGrantingTicketStorage instanceof AbstractEncryptedProxyGrantingTicketStorageImpl) {
final AbstractEncryptedProxyGrantingTicketStorageImpl p = (AbstractEncryptedProxyGrantingTicketStorageImpl) this.proxyGrantingTicketStorage;
final String cipherAlgorithm = getString(ConfigurationKeys.CIPHER_ALGORITHM);
final String secretKey = getString(ConfigurationKeys.SECRET_KEY);
final String cipherAlgorithm = getPropertyFromInitParams(filterConfig, "cipherAlgorithm", AbstractEncryptedProxyGrantingTicketStorageImpl.DEFAULT_ENCRYPTION_ALGORITHM);
final String secretKey = getPropertyFromInitParams(filterConfig, "secretKey", null);
p.setCipherAlgorithm(cipherAlgorithm);
@ -117,13 +94,11 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
}
}
this.millisBetweenCleanUps = getInt(ConfigurationKeys.MILLIS_BETWEEN_CLEAN_UPS);
this.privateKey = buildPrivateKey(getString(PRIVATE_KEY_PATH), getString(PRIVATE_KEY_ALGORITHM));
log.trace("Setting proxyReceptorUrl parameter: " + this.proxyReceptorUrl);
this.millisBetweenCleanUps = Integer.parseInt(getPropertyFromInitParams(filterConfig, "millisBetweenCleanUps", Integer.toString(DEFAULT_MILLIS_BETWEEN_CLEANUPS)));
super.initInternal(filterConfig);
}
@Override
public void init() {
super.init();
CommonUtils.assertNotNull(this.proxyGrantingTicketStorage, "proxyGrantingTicketStorage cannot be null.");
@ -138,61 +113,36 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
this.timer.schedule(this.timerTask, this.millisBetweenCleanUps, this.millisBetweenCleanUps);
}
private <T> T createNewTicketValidator(final Class<? extends Cas20ServiceTicketValidator> ticketValidatorClass, final String casServerUrlPrefix,
final Class<T> clazz) {
if (ticketValidatorClass == null) {
return ReflectUtils.newInstance(clazz, casServerUrlPrefix);
}
return (T) ReflectUtils.newInstance(ticketValidatorClass, casServerUrlPrefix);
}
public static PrivateKey buildPrivateKey(final String keyPath, final String keyAlgorithm) {
if (keyPath != null) {
return PrivateKeyUtils.createKey(keyPath, keyAlgorithm);
}
return null;
}
/**
* Constructs a Cas20ServiceTicketValidator or a Cas20ProxyTicketValidator based on supplied parameters.
*
* @param filterConfig the Filter Configuration object.
* @return a fully constructed TicketValidator.
*/
@Override
protected final TicketValidator getTicketValidator(final FilterConfig filterConfig) {
final boolean allowAnyProxy = getBoolean(ConfigurationKeys.ACCEPT_ANY_PROXY);
final String allowedProxyChains = getString(ConfigurationKeys.ALLOWED_PROXY_CHAINS);
final String casServerUrlPrefix = getString(ConfigurationKeys.CAS_SERVER_URL_PREFIX);
final Class<? extends Cas20ServiceTicketValidator> ticketValidatorClass = getClass(ConfigurationKeys.TICKET_VALIDATOR_CLASS);
final String allowAnyProxy = getPropertyFromInitParams(filterConfig, "acceptAnyProxy", null);
final String allowedProxyChains = getPropertyFromInitParams(filterConfig, "allowedProxyChains", null);
final String casServerUrlPrefix = getPropertyFromInitParams(filterConfig, "casServerUrlPrefix", null);
final Cas20ServiceTicketValidator validator;
if (allowAnyProxy || CommonUtils.isNotBlank(allowedProxyChains)) {
final Cas20ProxyTicketValidator v = createNewTicketValidator(ticketValidatorClass, casServerUrlPrefix,
this.defaultProxyTicketValidatorClass);
v.setAcceptAnyProxy(allowAnyProxy);
if (CommonUtils.isNotBlank(allowAnyProxy) || CommonUtils.isNotBlank(allowedProxyChains)) {
final Cas20ProxyTicketValidator v = new Cas20ProxyTicketValidator(casServerUrlPrefix);
v.setAcceptAnyProxy(parseBoolean(allowAnyProxy));
v.setAllowedProxyChains(CommonUtils.createProxyList(allowedProxyChains));
validator = v;
} else {
validator = createNewTicketValidator(ticketValidatorClass, casServerUrlPrefix,
this.defaultServiceTicketValidatorClass);
validator = new Cas20ServiceTicketValidator(casServerUrlPrefix);
}
validator.setProxyCallbackUrl(getString(ConfigurationKeys.PROXY_CALLBACK_URL));
validator.setProxyCallbackUrl(getPropertyFromInitParams(filterConfig, "proxyCallbackUrl", null));
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 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<String, String> additionalParameters = new HashMap<String, String>();
final Map<String,String> additionalParameters = new HashMap<String,String>();
final List<String> params = Arrays.asList(RESERVED_INIT_PARAMS);
for (final Enumeration<?> e = filterConfig.getInitParameterNames(); e.hasMoreElements(); ) {
for (final Enumeration<?> e = filterConfig.getInitParameterNames(); e.hasMoreElements();) {
final String s = (String) e.nextElement();
if (!params.contains(s)) {
@ -200,13 +150,12 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
}
}
validator.setPrivateKey(this.privateKey);
validator.setCustomParameters(additionalParameters);
validator.setHostnameVerifier(getHostnameVerifier(filterConfig));
return validator;
}
@Override
public void destroy() {
super.destroy();
this.timer.cancel();
@ -215,9 +164,7 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
/**
* This processes the ProxyReceptor request before the ticket validation code executes.
*/
@Override
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();
@ -229,7 +176,7 @@ public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketVal
try {
CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage);
} catch (final RuntimeException e) {
logger.error(e.getMessage(), e);
log.error(e.getMessage(), e);
throw e;
}

View File

@ -1,31 +1,33 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.Arrays;
import java.util.List;
import org.jasig.cas.client.util.XmlUtils;
import java.util.List;
/**
* 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 {
@ -35,84 +37,39 @@ 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);
}
protected final ProxyList getAllowedProxyChains() {
public ProxyList getAllowedProxyChains() {
return this.allowedProxyChains;
}
@Override
protected String getUrlSuffix() {
return "proxyValidate";
}
@Override
protected void customParseResponse(final String response, final Assertion assertion)
throws TicketValidationException {
final List<String> proxies = parseProxiesFromResponse(response);
if (proxies == null) {
throw new InvalidProxyChainTicketValidationException(
"Invalid proxy chain: No proxy could be retrieved from response. "
+ "This indicates a problem with CAS validation. Review logs/configuration to find the root cause."
);
}
// this means there was nothing in the proxy chain, which is okay
if (this.allowEmptyProxyChain && proxies.isEmpty()) {
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;
}
protected void customParseResponse(final String response, final Assertion assertion) throws TicketValidationException {
final List<String> proxies = XmlUtils.getTextForElements(response, "proxy");
final String[] proxiedList = proxies.toArray(new String[proxies.size()]);
// this means there was nothing in the proxy chain, which is okay
if (proxies.isEmpty() || this.acceptAnyProxy) {
return;
}
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());
}
protected List<String> parseProxiesFromResponse(final String response) {
return XmlUtils.getTextForElements(response, "proxy");
}
public final void setAcceptAnyProxy(final boolean acceptAnyProxy) {
public void setAcceptAnyProxy(final boolean acceptAnyProxy) {
this.acceptAnyProxy = acceptAnyProxy;
}
public final void setAllowedProxyChains(final ProxyList allowedProxyChains) {
public void setAllowedProxyChains(final ProxyList allowedProxyChains) {
this.allowedProxyChains = allowedProxyChains;
}
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;
}
}

View File

@ -1,31 +1,24 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.security.PrivateKey;
import java.util.*;
import javax.crypto.Cipher;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.commons.codec.binary.Base64;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.authentication.AttributePrincipalImpl;
import org.jasig.cas.client.proxy.Cas20ProxyRetriever;
@ -33,23 +26,25 @@ import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
import org.jasig.cas.client.proxy.ProxyRetriever;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.XmlUtils;
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 java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 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 {
public static final String PGT_ATTRIBUTE = "proxyGrantingTicket";
private static final String PGTIOU_PREFIX = "PGTIOU-";
/** The CAS 2.0 protocol proxy callback url. */
private String proxyCallbackUrl;
@ -59,9 +54,6 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick
/** Implementation of the proxy retriever. */
private ProxyRetriever proxyRetriever;
/** Private key for decryption */
private PrivateKey privateKey;
/**
* Constructs an instance of the CAS 2.0 Service Ticket Validator with the supplied
* CAS server url prefix.
@ -70,7 +62,7 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick
*/
public Cas20ServiceTicketValidator(final String casServerUrlPrefix) {
super(casServerUrlPrefix);
this.proxyRetriever = new Cas20ProxyRetriever(casServerUrlPrefix, getEncoding(), getURLConnectionFactory());
this.proxyRetriever = new Cas20ProxyRetriever(casServerUrlPrefix, getEncoding());
}
/**
@ -78,37 +70,34 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick
*
* @param urlParameters the Map containing the existing parameters to send to the server.
*/
@Override
protected final void populateUrlAttributeMap(final Map<String, String> urlParameters) {
urlParameters.put("pgtUrl", this.proxyCallbackUrl);
}
@Override
protected String getUrlSuffix() {
return "serviceValidate";
}
@Override
protected Assertion parseResponseFromServer(final String response) throws TicketValidationException {
final String error = parseAuthenticationFailureFromResponse(response);
protected final Assertion parseResponseFromServer(final String response) throws TicketValidationException {
final String error = XmlUtils.getTextForElement(response,
"authenticationFailure");
if (CommonUtils.isNotBlank(error)) {
throw new TicketValidationException(error);
}
final String principal = parsePrincipalFromResponse(response);
final String proxyGrantingTicket = retrieveProxyGrantingTicket(response);
final String principal = XmlUtils.getTextForElement(response, "user");
final String proxyGrantingTicketIou = XmlUtils.getTextForElement(response, "proxyGrantingTicket");
final String proxyGrantingTicket = this.proxyGrantingTicketStorage != null ? this.proxyGrantingTicketStorage.retrieve(proxyGrantingTicketIou) : null;
if (CommonUtils.isEmpty(principal)) {
throw new TicketValidationException("No principal was found in the response from the CAS server.");
}
final Assertion assertion;
final Map<String, Object> attributes = extractCustomAttributes(response);
final Map<String,Object> attributes = extractCustomAttributes(response);
if (CommonUtils.isNotBlank(proxyGrantingTicket)) {
attributes.remove(PGT_ATTRIBUTE);
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));
@ -119,52 +108,6 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick
return assertion;
}
protected String retrieveProxyGrantingTicket(final String response) {
final List<String> values = XmlUtils.getTextForElements(response, PGT_ATTRIBUTE);
for (final String value : values) {
if (value != null) {
if (value.startsWith(PGTIOU_PREFIX)) {
return retrieveProxyGrantingTicketFromStorage(value);
} else {
return retrieveProxyGrantingTicketViaEncryption(value);
}
}
}
return null;
}
protected String retrieveProxyGrantingTicketFromStorage(final String pgtIou) {
if (this.proxyGrantingTicketStorage != null) {
return this.proxyGrantingTicketStorage.retrieve(pgtIou);
}
return null;
}
protected String retrieveProxyGrantingTicketViaEncryption(final String encryptedPgt) {
if (this.privateKey != null) {
try {
final Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
final byte[] cred64 = new Base64().decode(encryptedPgt);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
final byte[] cipherData = cipher.doFinal(cred64);
final String pgt = new String(cipherData);
logger.debug("Decrypted PGT: {}", pgt);
return pgt;
} catch (final Exception e) {
logger.error("Unable to decrypt PGT", e);
}
}
return null;
}
protected String parsePrincipalFromResponse(final String response) {
return XmlUtils.getTextForElement(response, "user");
}
protected String parseAuthenticationFailureFromResponse(final String response) {
return XmlUtils.getTextForElement(response, "authenticationFailure");
}
/**
* Default attribute parsing of attributes that look like the following:
* &lt;cas:attributes&gt;
@ -172,32 +115,52 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick
* &lt;cas:attribute2&gt;value&lt;/cas:attribute2&gt;
* &lt;/cas:attributes&gt;
* <p>
*
* Attributes look like following also parsed correctly:
* &lt;cas:attributes&gt;&lt;cas:attribute1&gt;value&lt;/cas:attribute1&gt;&lt;cas:attribute2&gt;value&lt;/cas:attribute2&gt;&lt;/cas:attributes&gt;
* <p>
*
* This code is here merely for sample/demonstration purposes for those wishing to modify the CAS2 protocol. You'll
* probably want a more robust implementation or to use SAML 1.1
*
* @param xml the XML to parse.
* @return the map of attributes.
*/
protected Map<String, Object> extractCustomAttributes(final String xml) {
final SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
spf.setValidating(false);
try {
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();
}
protected Map<String,Object> extractCustomAttributes(final String xml) {
final int pos1 = xml.indexOf("<cas:attributes>");
final int pos2 = xml.indexOf("</cas:attributes>");
if (pos1 == -1) {
return Collections.emptyMap();
}
final String attributesText = xml.substring(pos1+16, pos2);
final Map<String,Object> attributes = new HashMap<String,Object>();
final BufferedReader br = new BufferedReader(new StringReader(attributesText));
String line;
final List<String> attributeNames = new ArrayList<String>();
try {
while ((line = br.readLine()) != null) {
final String trimmedLine = line.trim();
if (trimmedLine.length() > 0) {
final int leftPos = trimmedLine.indexOf(":");
final int rightPos = trimmedLine.indexOf(">");
attributeNames.add(trimmedLine.substring(leftPos+1, rightPos));
}
}
br.close();
} catch (final IOException e) {
//ignore
}
for (final String name : attributeNames) {
final List<String> values = XmlUtils.getTextForElements(xml, name);
if (values.size() == 1) {
attributes.put(name, values.get(0));
} else {
attributes.put(name, values);
}
}
return attributes;
}
/**
@ -207,8 +170,7 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick
* @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
}
@ -222,88 +184,5 @@ public class Cas20ServiceTicketValidator extends AbstractCasProtocolUrlBasedTick
public final void setProxyRetriever(final ProxyRetriever proxyRetriever) {
this.proxyRetriever = proxyRetriever;
}
protected final String getProxyCallbackUrl() {
return this.proxyCallbackUrl;
}
protected final ProxyGrantingTicketStorage getProxyGrantingTicketStorage() {
return this.proxyGrantingTicketStorage;
}
protected final ProxyRetriever getProxyRetriever() {
return this.proxyRetriever;
}
private class CustomAttributeHandler extends DefaultHandler {
private Map<String, Object> attributes;
private boolean foundAttributes;
private String currentAttribute;
private StringBuilder value;
@Override
public void startDocument() throws SAXException {
this.attributes = new HashMap<String, Object>();
}
@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<Object> items;
if (o instanceof List) {
items = (List<Object>) o;
} else {
items = new LinkedList<Object>();
items.add(o);
this.attributes.put(this.currentAttribute, items);
}
items.add(this.value.toString());
}
}
}
public Map<String, Object> getAttributes() {
return this.attributes;
}
}
public PrivateKey getPrivateKey() {
return privateKey;
}
public void setPrivateKey(final PrivateKey privateKey) {
this.privateKey = privateKey;
}
}
}

View File

@ -1,40 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.validation;
import org.jasig.cas.client.Protocol;
/**
* Creates either a Cas30ProxyTicketValidator or a Cas30ServiceTicketValidator depending on whether any of the
* proxy parameters are set.
* <p/>
* This filter can also pass additional parameters to the ticket validator. Any init parameter not included in the
* reserved list {@link org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter#RESERVED_INIT_PARAMS}.
*
* @author Jerome Leleu
* @since 3.4.0
*/
public class Cas30ProxyReceivingTicketValidationFilter extends Cas20ProxyReceivingTicketValidationFilter {
public Cas30ProxyReceivingTicketValidationFilter() {
super(Protocol.CAS3);
this.defaultServiceTicketValidatorClass = Cas30ServiceTicketValidator.class;
this.defaultProxyTicketValidatorClass = Cas30ProxyTicketValidator.class;
}
}

View File

@ -1,37 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.validation;
/**
* Service and proxy tickets validation service for the CAS protocol v3.
*
* @author Jerome Leleu
* @since 3.4.0
*/
public class Cas30ProxyTicketValidator extends Cas20ProxyTicketValidator {
public Cas30ProxyTicketValidator(final String casServerUrlPrefix) {
super(casServerUrlPrefix);
}
@Override
protected String getUrlSuffix() {
return "p3/proxyValidate";
}
}

View File

@ -1,90 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.validation;
import org.jasig.cas.client.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* Service tickets validation service for the CAS protocol v3.
*
* @author Jerome Leleu
* @since 3.4.0
*/
public class Cas30ServiceTicketValidator extends Cas20ServiceTicketValidator {
public Cas30ServiceTicketValidator(final String casServerUrlPrefix) {
super(casServerUrlPrefix);
}
@Override
protected String getUrlSuffix() {
return "p3/serviceValidate";
}
/**
* Custom attribute extractor that will account for inlined CAS attributes. Useful when CAS is acting as
* as SAML 2 IdP and returns SAML attributes with names that contains namespaces.
*
* @param xml the XML to parse.
* @return - Map of attributes
*/
@Override
protected Map<String, Object> extractCustomAttributes(final String xml) {
final Document document = XmlUtils.newDocument(xml);
// Check if attributes are inlined. If not return default super method results
final NodeList attributeList = document.getElementsByTagName("cas:attribute");
if (attributeList.getLength() == 0) {
return super.extractCustomAttributes(xml);
}
final HashMap<String, Object> attributes = new HashMap<String, Object>();
for (int i = 0; i < attributeList.getLength(); i++) {
final Node casAttributeNode = attributeList.item(i);
final NamedNodeMap nodeAttributes = casAttributeNode.getAttributes();
final String name = nodeAttributes.getNamedItem("name").getNodeValue();
final String value = nodeAttributes.getNamedItem("value").getTextContent();
final Object mapValue = attributes.get(name);
if (mapValue != null) {
if (mapValue instanceof List) {
((List) mapValue).add(value);
} else {
final LinkedList<Object> list = new LinkedList<Object>();
list.add(mapValue);
list.add(value);
attributes.put(name, list);
}
} else {
attributes.put(name, value);
}
}
return attributes;
}
}

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
/**
@ -28,11 +29,11 @@ package org.jasig.cas.client.validation;
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.
*/

View File

@ -1,31 +1,29 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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 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;
import java.util.Arrays;
/**
* Holding class for the proxy list to make Spring configuration easier.
@ -36,66 +34,28 @@ import org.slf4j.LoggerFactory;
*/
public final class ProxyList {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final List<List<UrlPatternMatcherStrategy>> proxyChains;
private final List<String[]> proxyChains;
public ProxyList(final List<String[]> proxyChains) {
CommonUtils.assertNotNull(proxyChains, "List of proxy chains cannot be null.");
this.proxyChains = new ArrayList<List<UrlPatternMatcherStrategy>>();
for (final String[] list : proxyChains) {
final List<UrlPatternMatcherStrategy> chain = new ArrayList<UrlPatternMatcherStrategy>();
for (final String item : list) {
if (item.startsWith("^")) {
chain.add(new RegexUrlPatternMatcherStrategy(item));
} else {
chain.add(new ExactUrlPatternMatcherStrategy(item));
}
}
this.proxyChains.add(chain);
}
this.proxyChains = proxyChains;
}
public ProxyList() {
this(new ArrayList<String[]>());
}
public boolean contains(final String[] proxiedList) {
StringBuilder loggingOutput;
for (final List<UrlPatternMatcherStrategy> 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("->");
}
public boolean contains(String[] proxiedList) {
for (final String[] list : this.proxyChains) {
if (Arrays.equals(proxiedList, list)) {
return true;
}
}
logger.warn("No proxy chain matched the allowedProxyChains list.");
return false;
}
public String toString() {
return this.proxyChains.toString();
return this.proxyChains.toString();
}
}

View File

@ -1,30 +1,32 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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.
@ -36,28 +38,27 @@ import org.jasig.cas.client.util.CommonUtils;
*/
public final class ProxyListEditor extends PropertyEditorSupport {
@Override
public void setAsText(final String text) throws IllegalArgumentException {
final BufferedReader reader = new BufferedReader(new StringReader(text));
final List<String[]> proxyChains = new ArrayList<String[]>();
public void setAsText(final String text) throws IllegalArgumentException {
final BufferedReader reader = new BufferedReader(new StringReader(text));
final List<String[]> proxyChains = new ArrayList<String[]>();
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));
}
}

View File

@ -0,0 +1,64 @@
/**
* 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.
* <p>
* Deployers can provide the "casServerUrlPrefix" and "tolerance" properties of the Saml11TicketValidator via the
* context or filter init parameters.
* <p>
* Note, the "final" on this class helps ensure the compliance required in the initInternal method.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public final class Saml11TicketValidationFilter extends AbstractTicketValidationFilter {
public Saml11TicketValidationFilter() {
setArtifactParameterName("SAMLart");
setServiceParameterName("TARGET");
}
protected 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;
}
}

View File

@ -0,0 +1,242 @@
/**
* 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<String, String> urlParameters) {
final String service = urlParameters.get("service");
urlParameters.remove("service");
urlParameters.remove("ticket");
urlParameters.put("TARGET", service);
}
@Override
protected void setDisableXmlSchemaValidation(final boolean disabled) {
if (disabled) {
// 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 Assertion parseResponseFromServer(final String response) throws TicketValidationException {
try {
final String removeStartOfSoapBody = response.substring(response.indexOf("<SOAP-ENV:Body>") + 15);
final String removeEndOfSoapBody = removeStartOfSoapBody.substring(0, removeStartOfSoapBody.indexOf("</SOAP-ENV:Body>"));
final SAMLResponse samlResponse = new SAMLResponse(new ByteArrayInputStream(CommonUtils.isNotBlank(getEncoding()) ? removeEndOfSoapBody.getBytes(Charset.forName(getEncoding())) : removeEndOfSoapBody.getBytes()));
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<String,Object> personAttributes = new HashMap<String,Object>();
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<String,Object> authenticationAttributes = new HashMap<String,Object>();
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<SAMLAttribute> attributes = new ArrayList<SAMLAttribute>();
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<Object> list = new ArrayList<Object>();
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 = "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"><SOAP-ENV:Header/><SOAP-ENV:Body><samlp:Request xmlns:samlp=\"urn:oasis:names:tc:SAML:1.0:protocol\" MajorVersion=\"1\" MinorVersion=\"1\" RequestID=\"" + SAMLIdentifierFactory.getInstance().getIdentifier() + "\" IssueInstant=\"" + CommonUtils.formatForUtcTime(new Date()) + "\">"
+ "<samlp:AssertionArtifact>" + ticket
+ "</samlp:AssertionArtifact></samlp:Request></SOAP-ENV:Body></SOAP-ENV:Envelope>";
} 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;
}
}

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
/**
@ -28,11 +29,11 @@ package org.jasig.cas.client.validation;
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

View File

@ -1,21 +1,22 @@
/**
* Licensed to Apereo under one or more contributor license
* Licensed to Jasig 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,
* 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:
* except in compliance with the License. You may obtain a
* copy of the License at:
*
* 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;
/**

View File

@ -1,35 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.validation.json;
import org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter;
/**
* Creates either a Cas30JsonServiceTicketValidator to validate tickets.
*
* @author Misagh Moayyed
*/
public class Cas30JsonProxyReceivingTicketValidationFilter extends Cas30ProxyReceivingTicketValidationFilter {
public Cas30JsonProxyReceivingTicketValidationFilter() {
super();
this.defaultServiceTicketValidatorClass = Cas30JsonServiceTicketValidator.class;
this.defaultProxyTicketValidatorClass = Cas30JsonProxyTicketValidator.class;
}
}

View File

@ -1,61 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.validation.json;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.Cas30ProxyTicketValidator;
import org.jasig.cas.client.validation.TicketValidationException;
import java.util.Collections;
import java.util.List;
/**
* This is {@link Cas30JsonProxyTicketValidator} that attempts to parse the CAS validation response
* as JSON. Very similar to {@link Cas30JsonServiceTicketValidator}, it also honors proxies as the name suggests.
*
* @author Misagh Moayyed
*/
public class Cas30JsonProxyTicketValidator extends Cas30ProxyTicketValidator {
public Cas30JsonProxyTicketValidator(final String casServerUrlPrefix) {
super(casServerUrlPrefix);
setCustomParameters(Collections.singletonMap("format", "JSON"));
}
@Override
protected Assertion parseResponseFromServer(final String response) throws TicketValidationException {
try {
final TicketValidationJsonResponse json = new JsonValidationResponseParser().parse(response);
return json.getAssertion(getProxyGrantingTicketStorage(), getProxyRetriever());
} catch (final Exception e) {
logger.warn("Unable parse the JSON response");
return super.parseResponseFromServer(response);
}
}
@Override
protected List<String> parseProxiesFromResponse(final String response) {
try {
final TicketValidationJsonResponse json = new JsonValidationResponseParser().parse(response);
return json.getServiceResponse().getAuthenticationSuccess().getProxies();
} catch (final Exception e) {
logger.warn("Unable to locate proxies from the JSON response", e);
return super.parseProxiesFromResponse(response);
}
}
}

View File

@ -1,62 +0,0 @@
/**
* Licensed to Apereo under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Apereo licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.client.validation.json;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.Cas30ServiceTicketValidator;
import org.jasig.cas.client.validation.TicketValidationException;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
/**
* This is {@link Cas30JsonServiceTicketValidator} that attempts to parse the CAS validation response
* as JSON. If the response is not formatted as JSON, it shall fallback to the XML default syntax.
* The JSON response provides advantages in terms of naming and parsing CAS attributes that have special
* names that otherwise may not be encoded as XML, such as the invalid {@code <cas:special:attribute>value</cas:special:attribute>}
*
* @author Misagh Moayyed
*/
public class Cas30JsonServiceTicketValidator extends Cas30ServiceTicketValidator {
public Cas30JsonServiceTicketValidator(final String casServerUrlPrefix) {
super(casServerUrlPrefix);
setCustomParameters(Collections.singletonMap("format", "JSON"));
}
@Override
protected Assertion parseResponseFromServer(final String response) throws TicketValidationException {
try {
final TicketValidationJsonResponse json = new JsonValidationResponseParser().parse(response);
return json.getAssertion(getProxyGrantingTicketStorage(), getProxyRetriever());
} catch (final JsonProcessingException e) {
logger.warn("Unable parse the JSON response. Falling back to XML", e);
return super.parseResponseFromServer(response);
} catch (final IOException e) {
throw new TicketValidationException(e.getMessage(), e);
}
}
@Override
protected Map<String, Object> extractCustomAttributes(final String xml) {
return Collections.emptyMap();
}
}

Some files were not shown because too many files have changed in this diff Show More