first blood

This commit is contained in:
东皇 2017-08-02 15:05:06 +08:00
commit 41db46a834
815 changed files with 154493 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.*
!.gitignore
!.gitkeep
*.iml
*/src/main/resources/application.properties
*/src/main/resources/log4j2.xml
target/
bin/

92
INSTALL.txt Normal file
View File

@ -0,0 +1,92 @@
JA-SIG Central Authentication Service
Quick Installation Guide
----------------------------------
This guide is meant to be a quickstart for you configure and install CAS
as quickly and easily as possible. If you need more information,
you can check the CAS website at
http://www.ja-sig.org/products/cas/
and you can subscribe to the CAS mailing list by visiting:
http://www.ja-sig.org/products/cas/community/lists/
Quick Demo
----------
The following steps will deploy the included demo of CAS. Start here if
you're new to CAS and want to see it in action. These steps assume
you will be using Tomcat as your servlet container.
Quick Demo
----------
The following steps will deploy the included demo of CAS. Start here if
you're new to CAS and want to see it in action. These steps assume
you will be using Tomcat as your servlet container.
1. copy modules/cas-server-webapp-VERSION.war to Tomcat's webapps/ directory
2. start Tomcat
3. access the CAS login page by opening up a web browser and visiting:
http://hostname:8080/cas-server-webapp-VERSION/login (see note below)
You should see the CAS login page asking you for your username and
password. The default authentication plugin accepts NetID=password.
Enter in an identical value for NetID and password and click LOGIN.
If everything is set up correctly, you should see a page stating that
you've successfully logged into CAS. Congratulations!
Note: this URL assumes that port 8080 is not blocked by a firewall,
and Tomcat is configured to listen on that port (it is by default).
Since we are only testing CAS, this configuration uses http for transport
rather than https -- this is not something you would do in production.
Customization
-------------
After you've gotten CAS working, one of the first things you will want
to do is create your own skin.
There is one skin included with CAS; Its called "default" If you look in cas-server-webapp/src/main/webapp/WEB-INF/view/jsp/default/ui,
you will see that a skin is made up of the following files:
casConfirmView.jsp displayed when the user is warned before being
redirected to the service
casGenericSuccess.jsp displayed when the user has been logged in without
providing a service to be redirected to
casLoginView.jsp the login form itself
casLogoutView.jsp displayed when the user has been logged out
serviceErrorView.jsp used in conjunction with the service registry feature,
displayed when the service the user is trying to
access is not allowed to use CAS. Note that this
feature is not enabled by default, in which case
all services are able to use CAS.
Steps for creating a custom skin
--------------------------------
Edit each of the pages to appear as you want them. Take care when
you're editing these pages not to change any of the forms or logic tags
unless you're sure you know what you're doing. If you need to include
a css file, put it in cas-server-webapp/src/main/webapp/themes/default/cas.css and reference it
with the following link tag:
<link rel="stylesheet" href="<spring:theme code="css" />" type="text/css"
media="all" />
The "<spring:theme code="css" />" will be replaced with the appropriate
URL to that directory.
Documentation
---------------------------------
Documentation for CAS can be found in the CAS User Manual:
http://www.ja-sig.org/wiki/display/CASUM/Home
--------------------------
Author: Drew Mazurek
Version: $Revision$ $Date$
Since: 3.0

11
README.md Normal file
View File

@ -0,0 +1,11 @@
cas-server-3.5.2
================
对于JasigCAS 3.5.2版本进行简化只保留基本的功能用JDBC方式验证并使用Metronic模板美化界面以达到可以直接在项目中使用的目的。
关于代码改动内容,很早之前写过一篇文章,如下;
http://blog.csdn.net/puma_dong/article/details/11564309
cas的java客户端也进行了小调整让登陆之后自动返回到登陆之前的页面不再使用p:service定义的值并有一个客户端配置单点登陆的例子如下
https://github.com/pumadong/cas-client-3.2.1

139
assembly.xml Normal file
View File

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>release</id>
<formats>
<format>zip</format>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<lineEnding>unix</lineEnding>
<useDefaultExcludes>true</useDefaultExcludes>
<directory>${basedir}</directory>
<outputDirectory></outputDirectory>
<includes>
<include>*.xml</include>
<include>*.txt</include>
</includes>
</fileSet>
</fileSets>
<moduleSets>
<moduleSet>
<includes></includes>
<excludes>
<exclude>cas-server-webapp</exclude>
</excludes>
<sources>
<fileSets>
<fileSet>
<directory>src</directory>
<outputDirectory>src</outputDirectory>
<lineEnding>unix</lineEnding>
<useDefaultExcludes>true</useDefaultExcludes>
<includes>
<include>**/*.java</include>
<include>**/*.xml</include>
<include>**/*.properties</include>
<include>**/*.jsp</include>
<include>**/*.css</include>
<include>**/*.jsp</include>
<include>**/*.html</include>
<include>**/*.txt</include>
<include>**/*.js</include>
<include>**/*.conf</include>
<include>**/*.crt</include>
<include>**/*.crl</include>
</includes>
</fileSet>
<fileSet>
<directory>src</directory>
<outputDirectory>src</outputDirectory>
<includes>
<include>**/*.gif</include>
<include>**/*.ico</include>
<include>**/*.key</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>
<includeModuleDirectory>true</includeModuleDirectory>
<useDefaultExcludes>true</useDefaultExcludes>
</sources>
<binaries>
<outputDirectory>modules</outputDirectory>
<includeDependencies>false</includeDependencies>
<unpack>false</unpack>
<includes />
</binaries>
</moduleSet>
<moduleSet>
<includes>
<include>cas-server-webapp</include>
</includes>
<sources>
<fileSets>
<fileSet>
<lineEnding>unix</lineEnding>
<useDefaultExcludes>true</useDefaultExcludes>
<includes>
<include>*.xml</include>
</includes>
</fileSet>
</fileSets>
<includeModuleDirectory>true</includeModuleDirectory>
<useDefaultExcludes>true</useDefaultExcludes>
</sources>
<binaries>
<outputDirectory>modules</outputDirectory>
<includeDependencies>false</includeDependencies>
<unpack>false</unpack>
<outputFileNameMapping>cas.war</outputFileNameMapping>
<includes />
</binaries>
</moduleSet>
</moduleSets>
</assembly>

255
cas-server-core/pom.xml Normal file
View File

@ -0,0 +1,255 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<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>
<artifactId>cas-server</artifactId>
<groupId>org.jasig.cas</groupId>
<version>3.5.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server-core</artifactId>
<name>Jasig CAS Core</name>
<description>CAS core</description>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<type>jar</type>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.inspektr</groupId>
<artifactId>inspektr-audit</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jasig.service.persondir</groupId>
<artifactId>person-directory-impl</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>jdom</groupId>
<artifactId>jdom</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<scope>test</scope>
<version>3.12.1.GA</version>
</dependency>
<dependency>
<groupId>org.apache.santuario</groupId>
<artifactId>xmlsec</artifactId>
<version>1.4.3</version>
<scope>runtime</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml</artifactId>
<version>2.5.1-1</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</exclusion>
<exclusion>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.xml</groupId>
<artifactId>xmldsig</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.perf4j</groupId>
<artifactId>perf4j</artifactId>
<version>${perf4j.version}</version>
<classifier>log4jonly</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>spring-webflow</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-jexl</groupId>
<artifactId>commons-jexl</artifactId>
<scope>runtime</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
<scope>compile</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.opensymphony.quartz</groupId>
<artifactId>quartz</artifactId>
<version>1.6.1</version>
<scope>test</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.github.inspektr</groupId>
<artifactId>inspektr-support-spring</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,48 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas;
/**
* Class that exposes the CAS version. Fetches the "Implementation-Version"
* manifest attribute from the jar file.
*
* @author Dmitriy Kopylenko
* @version $Revision$ $Date$
* @since 3.0
*/
public final class CasVersion {
/**
* Private constructor for CasVersion. You should not be able to instanciate
* this class.
*/
private CasVersion() {
// this class is not instantiable
}
/**
* Return the full CAS version string.
*
* @see java.lang.Package#getImplementationVersion
*/
public static String getVersion() {
return CasVersion.class.getPackage().getImplementationVersion();
}
}

View File

@ -0,0 +1,120 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas;
import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.ticket.TicketException;
import org.jasig.cas.validation.Assertion;
/**
* CAS viewed as a set of services to generate and validate Tickets.
* <p>
* This is the interface between a Web HTML, Web Services, RMI, or any other
* request processing layer and the CAS Service viewed as a mechanism to
* generate, store, validate, and retrieve Tickets containing Authentication
* information. The features of the request processing layer (the HttpXXX
* Servlet objects) are not visible here or in any modules behind this layer. In
* theory, a standalone application could call these methods directly as a
* private authentication service.
* </p>
*
* @author William G. Thompson, Jr.
* @author Dmitry Kopylenko
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
* <p>
* This is a published and supported CAS Server 3 API.
* </p>
*/
public interface CentralAuthenticationService {
/**
* Create a TicketGrantingTicket based on opaque credentials supplied by the
* caller.
*
* @param credentials The credentials to create the ticket for
* @return The String identifier of the ticket (may not be null).
* @throws TicketException if ticket cannot be created
*/
String createTicketGrantingTicket(Credentials credentials)
throws TicketException;
/**
* Grant a ServiceTicket for a Service.
*
* @param ticketGrantingTicketId Proof of prior authentication.
* @param service The target service of the ServiceTicket.
* @return the ServiceTicket for target Service.
* @throws TicketException if the ticket could not be created.
*/
String grantServiceTicket(String ticketGrantingTicketId, Service service)
throws TicketException;
/**
* Grant a ServiceTicket for a Service *if* the principal resolved from the
* credentials matches the principal associated with the
* TicketGrantingTicket.
*
* @param ticketGrantingTicketId Proof of prior authentication.
* @param service The target service of the ServiceTicket.
* @param credentials the Credentials to present to receive the
* ServiceTicket
* @return the ServiceTicket for target Service.
* @throws TicketException if the ticket could not be created.
*/
String grantServiceTicket(final String ticketGrantingTicketId,
final Service service, final Credentials credentials)
throws TicketException;
/**
* Validate a ServiceTicket for a particular Service.
*
* @param serviceTicketId Proof of prior authentication.
* @param service Service wishing to validate a prior authentication.
* @return ServiceTicket if valid for the service
* @throws TicketException if there was an error validating the ticket.
*/
Assertion validateServiceTicket(final String serviceTicketId,
final Service service) throws TicketException;
/**
* Destroy a TicketGrantingTicket. This has the effect of invalidating any
* Ticket that was derived from the TicketGrantingTicket being destroyed.
*
* @param ticketGrantingTicketId the id of the ticket we want to destroy
*/
void destroyTicketGrantingTicket(final String ticketGrantingTicketId);
/**
* Delegate a TicketGrantingTicket to a Service for proxying authentication
* to other Services.
*
* @param serviceTicketId The service ticket that will delegate to a
* TicketGrantingTicket
* @param credentials The credentials of the service that wishes to have a
* TicketGrantingTicket delegated to it.
* @return TicketGrantingTicket that can grant ServiceTickets that proxy
* authentication.
* @throws TicketException if there was an error creating the ticket
*/
String delegateTicketGrantingTicket(final String serviceTicketId,
final Credentials credentials) throws TicketException;
}

View File

@ -0,0 +1,563 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas;
import com.github.inspektr.audit.annotation.Audit;
import org.apache.commons.lang.StringUtils;
import org.jasig.cas.authentication.Authentication;
import org.jasig.cas.authentication.AuthenticationManager;
import org.jasig.cas.authentication.MutableAuthentication;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.PersistentIdGenerator;
import org.jasig.cas.authentication.principal.Principal;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.authentication.principal.ShibbolethCompatiblePersistentIdGenerator;
import org.jasig.cas.authentication.principal.SimplePrincipal;
import org.jasig.cas.services.RegisteredService;
import org.jasig.cas.services.ServicesManager;
import org.jasig.cas.services.UnauthorizedProxyingException;
import org.jasig.cas.services.UnauthorizedServiceException;
import org.jasig.cas.services.UnauthorizedSsoServiceException;
import org.jasig.cas.ticket.ExpirationPolicy;
import org.jasig.cas.ticket.InvalidTicketException;
import org.jasig.cas.ticket.ServiceTicket;
import org.jasig.cas.ticket.TicketCreationException;
import org.jasig.cas.ticket.TicketException;
import org.jasig.cas.ticket.TicketGrantingTicket;
import org.jasig.cas.ticket.TicketGrantingTicketImpl;
import org.jasig.cas.ticket.TicketValidationException;
import org.jasig.cas.ticket.registry.TicketRegistry;
import org.jasig.cas.util.UniqueTicketIdGenerator;
import org.jasig.cas.validation.Assertion;
import org.jasig.cas.validation.ImmutableAssertionImpl;
import org.perf4j.aop.Profiled;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Concrete implementation of a CentralAuthenticationService, and also the
* central, organizing component of CAS's internal implementation.
* <p>
* This class is threadsafe.
* <p>
* This class has the following properties that must be set:
* <ul>
* <li> <code>ticketRegistry</code> - The Ticket Registry to maintain the list
* of available tickets.</li>
* <li> <code>serviceTicketRegistry</code> - Provides an alternative to configure separate registries for TGTs and ST in order to store them
* in different locations (i.e. long term memory or short-term)</li>
* <li> <code>authenticationManager</code> - The service that will handle
* authentication.</li>
* <li> <code>ticketGrantingTicketUniqueTicketIdGenerator</code> - Plug in to
* generate unique secure ids for TicketGrantingTickets.</li>
* <li> <code>serviceTicketUniqueTicketIdGenerator</code> - Plug in to
* generate unique secure ids for ServiceTickets.</li>
* <li> <code>ticketGrantingTicketExpirationPolicy</code> - The expiration
* policy for TicketGrantingTickets.</li>
* <li> <code>serviceTicketExpirationPolicy</code> - The expiration policy for
* ServiceTickets.</li>
* </ul>
*
* @author William G. Thompson, Jr.
* @author Scott Battaglia
* @author Dmitry Kopylenko
* @version $Revision: 1.16 $ $Date: 2007/04/24 18:11:36 $
* @since 3.0
*/
public final class CentralAuthenticationServiceImpl implements CentralAuthenticationService {
/** Log instance for logging events, info, warnings, errors, etc. */
private final Logger log = LoggerFactory.getLogger(this.getClass());
/** TicketRegistry for storing and retrieving tickets as needed. */
@NotNull
private TicketRegistry ticketRegistry;
/** New Ticket Registry for storing and retrieving services tickets. Can point to the same one as the ticketRegistry variable. */
@NotNull
private TicketRegistry serviceTicketRegistry;
/**
* AuthenticationManager for authenticating credentials for purposes of
* obtaining tickets.
*/
@NotNull
private AuthenticationManager authenticationManager;
/**
* UniqueTicketIdGenerator to generate ids for TicketGrantingTickets
* created.
*/
@NotNull
private UniqueTicketIdGenerator ticketGrantingTicketUniqueTicketIdGenerator;
/** Map to contain the mappings of service->UniqueTicketIdGenerators */
@NotNull
private Map<String, UniqueTicketIdGenerator> uniqueTicketIdGeneratorsForService;
/** Expiration policy for ticket granting tickets. */
@NotNull
private ExpirationPolicy ticketGrantingTicketExpirationPolicy;
/** ExpirationPolicy for Service Tickets. */
@NotNull
private ExpirationPolicy serviceTicketExpirationPolicy;
/** Implementation of Service Manager */
@NotNull
private ServicesManager servicesManager;
/** Encoder to generate PseudoIds. */
@NotNull
private PersistentIdGenerator persistentIdGenerator = new ShibbolethCompatiblePersistentIdGenerator();
/**
* Implementation of destoryTicketGrantingTicket expires the ticket provided
* and removes it from the TicketRegistry.
*
* @throws IllegalArgumentException if the TicketGrantingTicket ID is null.
*/
@Audit(
action="TICKET_GRANTING_TICKET_DESTROYED",
actionResolverName="DESTROY_TICKET_GRANTING_TICKET_RESOLVER",
resourceResolverName="DESTROY_TICKET_GRANTING_TICKET_RESOURCE_RESOLVER")
@Profiled(tag = "DESTROY_TICKET_GRANTING_TICKET",logFailuresSeparately = false)
@Transactional(readOnly = false)
public void destroyTicketGrantingTicket(final String ticketGrantingTicketId) {
Assert.notNull(ticketGrantingTicketId);
if (log.isDebugEnabled()) {
log.debug("Removing ticket [" + ticketGrantingTicketId + "] from registry.");
}
final TicketGrantingTicket ticket = (TicketGrantingTicket) this.ticketRegistry.getTicket(ticketGrantingTicketId, TicketGrantingTicket.class);
if (ticket == null) {
return;
}
if (log.isDebugEnabled()) {
log.debug("Ticket found. Expiring and then deleting.");
}
ticket.expire();
this.ticketRegistry.deleteTicket(ticketGrantingTicketId);
}
/**
* @throws IllegalArgumentException if TicketGrantingTicket ID, Credentials
* or Service are null.
*/
@Audit(
action="SERVICE_TICKET",
actionResolverName="GRANT_SERVICE_TICKET_RESOLVER",
resourceResolverName="GRANT_SERVICE_TICKET_RESOURCE_RESOLVER")
@Profiled(tag="GRANT_SERVICE_TICKET", logFailuresSeparately = false)
@Transactional(readOnly = false)
public String grantServiceTicket(final String ticketGrantingTicketId, final Service service, final Credentials credentials) throws TicketException {
Assert.notNull(ticketGrantingTicketId, "ticketGrantingticketId cannot be null");
Assert.notNull(service, "service cannot be null");
final TicketGrantingTicket ticketGrantingTicket;
ticketGrantingTicket = (TicketGrantingTicket) this.ticketRegistry.getTicket(ticketGrantingTicketId, TicketGrantingTicket.class);
if (ticketGrantingTicket == null) {
throw new InvalidTicketException();
}
synchronized (ticketGrantingTicket) {
if (ticketGrantingTicket.isExpired()) {
this.ticketRegistry.deleteTicket(ticketGrantingTicketId);
throw new InvalidTicketException();
}
}
final RegisteredService registeredService = this.servicesManager
.findServiceBy(service);
if (registeredService == null || !registeredService.isEnabled()) {
log.warn("ServiceManagement: Unauthorized Service Access. Service [" + service.getId() + "] not found in Service Registry.");
throw new UnauthorizedServiceException();
}
if (!registeredService.isSsoEnabled() && credentials == null
&& ticketGrantingTicket.getCountOfUses() > 0) {
log.warn("ServiceManagement: Service Not Allowed to use SSO. Service [" + service.getId() + "]");
throw new UnauthorizedSsoServiceException();
}
//CAS-1019
final List<Authentication> authns = ticketGrantingTicket.getChainedAuthentications();
if(authns.size() > 1) {
if (!registeredService.isAllowedToProxy()) {
final String message = String.format("ServiceManagement: Service Attempted to Proxy, but is not allowed. Service: [%s] | Registered Service: [%s]", service.getId(), registeredService.toString());
log.warn(message);
throw new UnauthorizedProxyingException(message);
}
}
if (credentials != null) {
try {
final Authentication authentication = this.authenticationManager
.authenticate(credentials);
final Authentication originalAuthentication = ticketGrantingTicket.getAuthentication();
if (!(authentication.getPrincipal().equals(originalAuthentication.getPrincipal()) && authentication.getAttributes().equals(originalAuthentication.getAttributes()))) {
throw new TicketCreationException();
}
} catch (final AuthenticationException e) {
throw new TicketCreationException(e);
}
}
// this code is a bit brittle by depending on the class name. Future versions (i.e. CAS4 will know inherently how to identify themselves)
final UniqueTicketIdGenerator serviceTicketUniqueTicketIdGenerator = this.uniqueTicketIdGeneratorsForService
.get(service.getClass().getName());
final ServiceTicket serviceTicket = ticketGrantingTicket
.grantServiceTicket(serviceTicketUniqueTicketIdGenerator
.getNewTicketId(ServiceTicket.PREFIX), service,
this.serviceTicketExpirationPolicy, credentials != null);
this.serviceTicketRegistry.addTicket(serviceTicket);
if (log.isInfoEnabled()) {
final List<Authentication> authentications = serviceTicket.getGrantingTicket().getChainedAuthentications();
final String formatString = "Granted %s ticket [%s] for service [%s] for user [%s]";
final String type;
final String principalId = authentications.get(authentications.size()-1).getPrincipal().getId();
if (authentications.size() == 1) {
type = "service";
} else {
type = "proxy";
}
log.info(String.format(formatString, type, serviceTicket.getId(), service.getId(), principalId));
}
return serviceTicket.getId();
}
@Audit(
action="SERVICE_TICKET",
actionResolverName="GRANT_SERVICE_TICKET_RESOLVER",
resourceResolverName="GRANT_SERVICE_TICKET_RESOURCE_RESOLVER")
@Profiled(tag = "GRANT_SERVICE_TICKET",logFailuresSeparately = false)
@Transactional(readOnly = false)
public String grantServiceTicket(final String ticketGrantingTicketId,
final Service service) throws TicketException {
return this.grantServiceTicket(ticketGrantingTicketId, service, null);
}
/**
* @throws IllegalArgumentException if the ServiceTicketId or the
* Credentials are null.
*/
@Audit(
action="PROXY_GRANTING_TICKET",
actionResolverName="GRANT_PROXY_GRANTING_TICKET_RESOLVER",
resourceResolverName="GRANT_PROXY_GRANTING_TICKET_RESOURCE_RESOLVER")
@Profiled(tag="GRANT_PROXY_GRANTING_TICKET",logFailuresSeparately = false)
@Transactional(readOnly = false)
public String delegateTicketGrantingTicket(final String serviceTicketId,
final Credentials credentials) throws TicketException {
Assert.notNull(serviceTicketId, "serviceTicketId cannot be null");
Assert.notNull(credentials, "credentials cannot be null");
try {
final Authentication authentication = this.authenticationManager
.authenticate(credentials);
final ServiceTicket serviceTicket;
serviceTicket = (ServiceTicket) this.serviceTicketRegistry.getTicket(serviceTicketId, ServiceTicket.class);
if (serviceTicket == null || serviceTicket.isExpired()) {
throw new InvalidTicketException();
}
final RegisteredService registeredService = this.servicesManager
.findServiceBy(serviceTicket.getService());
if (registeredService == null || !registeredService.isEnabled()
|| !registeredService.isAllowedToProxy()) {
log.warn("ServiceManagement: Service Attempted to Proxy, but is not allowed. Service: [" + serviceTicket.getService().getId() + "]");
throw new UnauthorizedProxyingException();
}
final TicketGrantingTicket ticketGrantingTicket = serviceTicket
.grantTicketGrantingTicket(
this.ticketGrantingTicketUniqueTicketIdGenerator
.getNewTicketId(TicketGrantingTicket.PREFIX),
authentication, this.ticketGrantingTicketExpirationPolicy);
this.ticketRegistry.addTicket(ticketGrantingTicket);
return ticketGrantingTicket.getId();
} catch (final AuthenticationException e) {
throw new TicketCreationException(e);
}
}
/**
* @throws IllegalArgumentException if the ServiceTicketId or the Service
* are null.
*/
@Audit(
action="SERVICE_TICKET_VALIDATE",
actionResolverName="VALIDATE_SERVICE_TICKET_RESOLVER",
resourceResolverName="VALIDATE_SERVICE_TICKET_RESOURCE_RESOLVER")
@Profiled(tag="VALIDATE_SERVICE_TICKET",logFailuresSeparately = false)
@Transactional(readOnly = false)
public Assertion validateServiceTicket(final String serviceTicketId, final Service service) throws TicketException {
Assert.notNull(serviceTicketId, "serviceTicketId cannot be null");
Assert.notNull(service, "service cannot be null");
final ServiceTicket serviceTicket = (ServiceTicket) this.serviceTicketRegistry.getTicket(serviceTicketId, ServiceTicket.class);
final RegisteredService registeredService = this.servicesManager.findServiceBy(service);
if (registeredService == null || !registeredService.isEnabled()) {
log.warn("ServiceManagement: Service does not exist is not enabled, and thus not allowed to validate tickets. Service: [" + service.getId() + "]");
throw new UnauthorizedServiceException("Service not allowed to validate tickets.");
}
if (serviceTicket == null) {
log.info("ServiceTicket [" + serviceTicketId + "] does not exist.");
throw new InvalidTicketException();
}
try {
synchronized (serviceTicket) {
if (serviceTicket.isExpired()) {
log.info("ServiceTicket [" + serviceTicketId + "] has expired.");
throw new InvalidTicketException();
}
if (!serviceTicket.isValidFor(service)) {
log.error("ServiceTicket [" + serviceTicketId + "] with service [" + serviceTicket.getService().getId() + " does not match supplied service [" + service + "]");
throw new TicketValidationException(serviceTicket.getService());
}
}
final List<Authentication> chainedAuthenticationsList = serviceTicket.getGrantingTicket().getChainedAuthentications();
final Authentication authentication = chainedAuthenticationsList.get(chainedAuthenticationsList.size() - 1);
final Principal principal = authentication.getPrincipal();
final String principalId = determinePrincipalIdForRegisteredService(principal, registeredService, serviceTicket);
final Authentication authToUse;
if (!registeredService.isIgnoreAttributes()) {
final Map<String, Object> attributes = new HashMap<String, Object>();
for (final String attribute : registeredService.getAllowedAttributes()) {
final Object value = principal.getAttributes().get(attribute);
if (value != null) {
attributes.put(attribute, value);
}
}
final Principal modifiedPrincipal = new SimplePrincipal(principalId, attributes);
final MutableAuthentication mutableAuthentication = new MutableAuthentication(
modifiedPrincipal, authentication.getAuthenticatedDate());
mutableAuthentication.getAttributes().putAll(
authentication.getAttributes());
mutableAuthentication.getAuthenticatedDate().setTime(
authentication.getAuthenticatedDate().getTime());
authToUse = mutableAuthentication;
} else {
final Principal modifiedPrincipal = new SimplePrincipal(principalId, principal.getAttributes());
authToUse = new MutableAuthentication(modifiedPrincipal, authentication.getAuthenticatedDate());
}
final List<Authentication> authentications = new ArrayList<Authentication>();
for (int i = 0; i < chainedAuthenticationsList.size() - 1; i++) {
authentications.add(serviceTicket.getGrantingTicket().getChainedAuthentications().get(i));
}
authentications.add(authToUse);
return new ImmutableAssertionImpl(authentications, serviceTicket.getService(), serviceTicket.isFromNewLogin());
} finally {
if (serviceTicket.isExpired()) {
this.serviceTicketRegistry.deleteTicket(serviceTicketId);
}
}
}
/**
* Determines the principal id to use for a {@link RegisteredService} using the following rules:
*
* <ul>
* <li> If the service is marked to allow anonymous access, a persistent id is returned. </li>
* <li> If the attribute name matches {@link RegisteredService#DEFAULT_USERNAME_ATTRIBUTE}, then the default principal id is returned.</li>
* <li>If the service is set to ignore attributes, or the username attribute exists in the allowed attributes for the service,
* the corresponding attribute value will be returned.
* </li>
* <li>Otherwise, the default principal's id is returned as the username attribute with an additional warning.</li>
* </ul>
*
* @param principal The principal object to be validated and constructed
* @param registeredService Requesting service for which a principal is being validated.
* @param serviceTicket An instance of the service ticket used for validation
*
* @return The principal id to use for the requesting registered service
*/
private String determinePrincipalIdForRegisteredService(final Principal principal, final RegisteredService registeredService,
final ServiceTicket serviceTicket) {
String principalId = null;
final String serviceUsernameAttribute = registeredService.getUsernameAttribute();
if (registeredService.isAnonymousAccess()) {
principalId = this.persistentIdGenerator.generate(principal, serviceTicket.getService());
} else if (StringUtils.isBlank(serviceUsernameAttribute)) {
principalId = principal.getId();
} else {
if ((registeredService.isIgnoreAttributes() || registeredService.getAllowedAttributes().contains(serviceUsernameAttribute)) &&
principal.getAttributes().containsKey(serviceUsernameAttribute)) {
principalId = principal.getAttributes().get(registeredService.getUsernameAttribute()).toString();
} else {
principalId = principal.getId();
final Object[] errorLogParameters = new Object[] { principalId, registeredService.getUsernameAttribute(),
principal.getAttributes(), registeredService.getServiceId(), principalId };
log.warn("Principal [{}] did not have attribute [{}] among attributes [{}] so CAS cannot "
+ "provide on the validation response the user attribute the registered service [{}] expects. "
+ "CAS will instead return the default username attribute [{}]", errorLogParameters);
}
}
log.debug("Principal id to return for service [{}] is [{}]. The default principal id is [{}].",
new Object[] {registeredService.getName(), principal.getId(), principalId});
return principalId;
}
/**
* @throws IllegalArgumentException if the credentials are null.
*/
@Audit(
action="TICKET_GRANTING_TICKET",
actionResolverName="CREATE_TICKET_GRANTING_TICKET_RESOLVER",
resourceResolverName="CREATE_TICKET_GRANTING_TICKET_RESOURCE_RESOLVER")
@Profiled(tag = "CREATE_TICKET_GRANTING_TICKET", logFailuresSeparately = false)
@Transactional(readOnly = false)
public String createTicketGrantingTicket(final Credentials credentials) throws TicketCreationException {
Assert.notNull(credentials, "credentials cannot be null");
try {
final Authentication authentication = this.authenticationManager
.authenticate(credentials);
final TicketGrantingTicket ticketGrantingTicket = new TicketGrantingTicketImpl(
this.ticketGrantingTicketUniqueTicketIdGenerator
.getNewTicketId(TicketGrantingTicket.PREFIX),
authentication, this.ticketGrantingTicketExpirationPolicy);
this.ticketRegistry.addTicket(ticketGrantingTicket);
return ticketGrantingTicket.getId();
} catch (final AuthenticationException e) {
throw new TicketCreationException(e);
}
}
/**
* Method to set the TicketRegistry.
*
* @param ticketRegistry the TicketRegistry to set.
*/
public void setTicketRegistry(final TicketRegistry ticketRegistry) {
this.ticketRegistry = ticketRegistry;
if (this.serviceTicketRegistry == null) {
this.serviceTicketRegistry = ticketRegistry;
}
}
public void setServiceTicketRegistry(final TicketRegistry serviceTicketRegistry) {
this.serviceTicketRegistry = serviceTicketRegistry;
}
/**
* Method to inject the AuthenticationManager into the class.
*
* @param authenticationManager The authenticationManager to set.
*/
public void setAuthenticationManager(
final AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
/**
* Method to inject the TicketGrantingTicket Expiration Policy.
*
* @param ticketGrantingTicketExpirationPolicy The
* ticketGrantingTicketExpirationPolicy to set.
*/
public void setTicketGrantingTicketExpirationPolicy(
final ExpirationPolicy ticketGrantingTicketExpirationPolicy) {
this.ticketGrantingTicketExpirationPolicy = ticketGrantingTicketExpirationPolicy;
}
/**
* Method to inject the Unique Ticket Id Generator into the class.
*
* @param uniqueTicketIdGenerator The uniqueTicketIdGenerator to use
*/
public void setTicketGrantingTicketUniqueTicketIdGenerator(
final UniqueTicketIdGenerator uniqueTicketIdGenerator) {
this.ticketGrantingTicketUniqueTicketIdGenerator = uniqueTicketIdGenerator;
}
/**
* Method to inject the TicketGrantingTicket Expiration Policy.
*
* @param serviceTicketExpirationPolicy The serviceTicketExpirationPolicy to
* set.
*/
public void setServiceTicketExpirationPolicy(
final ExpirationPolicy serviceTicketExpirationPolicy) {
this.serviceTicketExpirationPolicy = serviceTicketExpirationPolicy;
}
public void setUniqueTicketIdGeneratorsForService(
final Map<String, UniqueTicketIdGenerator> uniqueTicketIdGeneratorsForService) {
this.uniqueTicketIdGeneratorsForService = uniqueTicketIdGeneratorsForService;
}
public void setServicesManager(final ServicesManager servicesManager) {
this.servicesManager = servicesManager;
}
public void setPersistentIdGenerator(
final PersistentIdGenerator persistentIdGenerator) {
this.persistentIdGenerator = persistentIdGenerator;
}
}

View File

@ -0,0 +1,76 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
/**
*
*
* @version $Revision$ $Date$
* @since 3.3.6
*/
@Aspect
public class LogAspect {
@Around("(execution (public * org.jasig.cas..*.*(..))) && !(execution( * org.jasig.cas..*.set*(..)))")
public Object traceMethod(final ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object returnVal = null;
final Logger log = getLog(proceedingJoinPoint);
final String methodName = proceedingJoinPoint.getSignature().getName();
try {
if (log.isTraceEnabled()) {
final Object[] args = proceedingJoinPoint.getArgs();
final String arguments;
if (args == null || args.length == 0) {
arguments = "";
} else {
arguments = Arrays.deepToString(args);
}
log.trace("Entering method [" + methodName + " with arguments [" + arguments + "]");
}
returnVal = proceedingJoinPoint.proceed();
return returnVal;
} finally {
if (log.isTraceEnabled()) {
log.trace("Leaving method [" + methodName + "] with return value [" + (returnVal != null ? returnVal.toString() : "null") + "].");
}
}
}
protected Logger getLog(final JoinPoint joinPoint) {
final Object target = joinPoint.getTarget();
if (target != null) {
return LoggerFactory.getLogger(target.getClass());
}
return LoggerFactory.getLogger(getClass());
}
}

View File

@ -0,0 +1,45 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.audit.spi;
import org.aspectj.lang.JoinPoint;
import com.github.inspektr.audit.spi.AuditResourceResolver;
import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.util.AopUtils;
/**
* Converts the Credentials object into a String resource identifier.
*
* @author Scott Battaglia
* @version $Revision: 1.1 $ $Date: 2005/08/19 18:27:17 $
* @since 3.1.2
*
*/
public final class CredentialsAsFirstParameterResourceResolver implements AuditResourceResolver {
public String[] resolveFrom(final JoinPoint joinPoint, final Object retval) {
final Credentials credentials = (Credentials) AopUtils.unWrapJoinPoint(joinPoint).getArgs()[0];
return new String[] { "supplied credentials: " + credentials.toString() };
}
public String[] resolveFrom(final JoinPoint joinPoint, final Exception exception) {
final Credentials credentials = (Credentials) AopUtils.unWrapJoinPoint(joinPoint).getArgs()[0];
return new String[] { "supplied credentials: " + credentials.toString() };
}
}

View File

@ -0,0 +1,60 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.audit.spi;
import com.github.inspektr.audit.spi.AuditResourceResolver;
import org.aspectj.lang.JoinPoint;
import org.jasig.cas.services.RegisteredService;
import org.jasig.cas.services.ServicesManager;
import org.jasig.cas.util.AopUtils;
import javax.validation.constraints.NotNull;
/**
* Resolves a service id to the service.
* <p>
* The expectation is that args[0] is a Long.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.4.6
*/
public final class ServiceManagementResourceResolver implements AuditResourceResolver {
public String[] resolveFrom(final JoinPoint target, final Object returnValue) {
return findService(target);
}
public String[] resolveFrom(final JoinPoint target, final Exception exception) {
return findService(target);
}
private String[] findService(final JoinPoint joinPoint) {
final JoinPoint j = AopUtils.unWrapJoinPoint(joinPoint);
final Long id = (Long) j.getArgs()[0];
if (id == null) {
return new String[] {""};
}
return new String[] {"id=" + id};
}
}

View File

@ -0,0 +1,44 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at 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.audit.spi;
import org.aspectj.lang.JoinPoint;
import com.github.inspektr.audit.spi.AuditResourceResolver;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.util.AopUtils;
/**
*
* @author Scott Battaglia
* @version $Revision: 1.1 $ $Date: 2005/08/19 18:27:17 $
* @since 3.1.2
*
*/
public final class ServiceResourceResolver implements AuditResourceResolver {
public String[] resolveFrom(final JoinPoint joinPoint, final Object retval) {
final Service service = (Service) AopUtils.unWrapJoinPoint(joinPoint).getArgs()[1];
return new String[] { retval.toString() + " for " + service.getId() };
}
public String[] resolveFrom(final JoinPoint joinPoint, final Exception ex) {
final Service service = (Service) AopUtils.unWrapJoinPoint(joinPoint).getArgs()[1];
return new String[] { service.getId() };
}
}

View File

@ -0,0 +1,42 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.audit.spi;
import com.github.inspektr.audit.spi.AuditResourceResolver;
import org.aspectj.lang.JoinPoint;
import org.jasig.cas.util.AopUtils;
/**
* Implementation of the ResourceResolver that can determine the Ticket Id from the first parameter of the method call.
* @author Scott Battaglia
* @version $Revision: 1.1 $ $Date: 2005/08/19 18:27:17 $
* @since 3.1.2
*
*/
public final class TicketAsFirstParameterResourceResolver implements AuditResourceResolver {
public String[] resolveFrom(final JoinPoint joinPoint, final Exception exception) {
return new String[] { AopUtils.unWrapJoinPoint(joinPoint).getArgs()[0].toString() };
}
public String[] resolveFrom(final JoinPoint joinPoint, final Object object) {
return new String[] { AopUtils.unWrapJoinPoint(joinPoint).getArgs()[0].toString() };
}
}

View File

@ -0,0 +1,90 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.audit.spi;
import org.aspectj.lang.JoinPoint;
import com.github.inspektr.common.spi.PrincipalResolver;
import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.ticket.ServiceTicket;
import org.jasig.cas.ticket.Ticket;
import org.jasig.cas.ticket.TicketGrantingTicket;
import org.jasig.cas.ticket.registry.TicketRegistry;
import org.jasig.cas.util.AopUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import javax.validation.constraints.NotNull;
/**
* PrincipalResolver that can retrieve the username from either the Ticket or from the Credentials.
*
* @author Scott Battaglia
* @version $Revision: 1.1 $ $Date: 2005/08/19 18:27:17 $
* @since 3.1.2
*
*/
public final class TicketOrCredentialPrincipalResolver implements PrincipalResolver {
@NotNull
private final TicketRegistry ticketRegistry;
public TicketOrCredentialPrincipalResolver(final TicketRegistry ticketRegistry) {
this.ticketRegistry = ticketRegistry;
}
public String resolveFrom(final JoinPoint joinPoint, final Object retVal) {
return resolveFromInternal(AopUtils.unWrapJoinPoint(joinPoint));
}
public String resolveFrom(final JoinPoint joinPoint, final Exception retVal) {
return resolveFromInternal(AopUtils.unWrapJoinPoint(joinPoint));
}
public String resolve() {
return UNKNOWN_USER;
}
protected String resolveFromInternal(final JoinPoint joinPoint) {
final Object arg1 = joinPoint.getArgs()[0];
if (arg1 instanceof Credentials) {
return arg1.toString();
} else if (arg1 instanceof String) {
final Ticket ticket = this.ticketRegistry.getTicket((String) arg1);
if (ticket instanceof ServiceTicket) {
final ServiceTicket serviceTicket = (ServiceTicket) ticket;
return serviceTicket.getGrantingTicket().getAuthentication().getPrincipal().getId();
} else if (ticket instanceof TicketGrantingTicket) {
final TicketGrantingTicket tgt = (TicketGrantingTicket) ticket;
return tgt.getAuthentication().getPrincipal().getId();
}
} else {
final SecurityContext securityContext = SecurityContextHolder.getContext();
if (securityContext != null) {
final Authentication authentication = securityContext.getAuthentication();
if (authentication != null) {
return ((UserDetails) authentication.getPrincipal()).getUsername();
}
}
}
return UNKNOWN_USER;
}
}

View File

@ -0,0 +1,77 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication;
import java.util.Map;
import org.jasig.cas.authentication.principal.Principal;
import org.springframework.util.Assert;
/**
* @author Scott Battaglia
* @version $Revision$ $Date: 2007-02-20 09:41:49 -0500 (Tue, 20 Feb
* 2007) $
* @since 3.0.3
*/
public abstract class AbstractAuthentication implements Authentication {
/** A Principal object representing the authenticated entity. */
private final Principal principal;
/** Associated authentication attributes. */
private final Map<String, Object> attributes;
public AbstractAuthentication(final Principal principal,
final Map<String, Object> attributes) {
Assert.notNull(principal, "principal cannot be null");
Assert.notNull(attributes, "attributes cannot be null");
this.principal = principal;
this.attributes = attributes;
}
public final Map<String, Object> getAttributes() {
return this.attributes;
}
public final Principal getPrincipal() {
return this.principal;
}
public final boolean equals(final Object o) {
if (o == null || !this.getClass().isAssignableFrom(o.getClass())) {
return false;
}
Authentication a = (Authentication) o;
return this.principal.equals(a.getPrincipal())
&& this.getAuthenticatedDate().equals(a.getAuthenticatedDate()) && this.attributes.equals(a.getAttributes());
}
public final int hashCode() {
return 49 * this.principal.hashCode()
^ this.getAuthenticatedDate().hashCode();
}
public final String toString() {
return "[Principal=" + this.principal.getId() + ", attributes="
+ this.attributes.toString() + "]";
}
}

View File

@ -0,0 +1,140 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication;
import java.util.ArrayList;
import java.util.List;
import javax.validation.constraints.NotNull;
import com.github.inspektr.audit.annotation.Audit;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.handler.AuthenticationHandler;
import org.jasig.cas.authentication.handler.NamedAuthenticationHandler;
import org.jasig.cas.authentication.handler.UncategorizedAuthenticationException;
import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.Principal;
import org.perf4j.aop.Profiled;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.3.5
*/
public abstract class AbstractAuthenticationManager implements AuthenticationManager {
/** Log instance for logging events, errors, warnings, etc. */
protected final Logger log = LoggerFactory.getLogger(AuthenticationManagerImpl.class);
/** An array of AuthenticationAttributesPopulators. */
@NotNull
private List<AuthenticationMetaDataPopulator> authenticationMetaDataPopulators = new ArrayList<AuthenticationMetaDataPopulator>();
@Audit(
action="AUTHENTICATION",
actionResolverName="AUTHENTICATION_RESOLVER",
resourceResolverName="AUTHENTICATION_RESOURCE_RESOLVER")
@Profiled(tag = "AUTHENTICATE", logFailuresSeparately = false)
public final Authentication authenticate(final Credentials credentials) throws AuthenticationException {
final Pair<AuthenticationHandler, Principal> pair = authenticateAndObtainPrincipal(credentials);
// we can only get here if the above method doesn't throw an exception. And if it doesn't, then the pair must not be null.
final Principal p = pair.getSecond();
log.info("{} authenticated {} with credential {}.", pair.getFirst(), p, credentials);
log.debug("Attribute map for {}: {}", p.getId(), p.getAttributes());
Authentication authentication = new MutableAuthentication(p);
if (pair.getFirst()instanceof NamedAuthenticationHandler) {
final NamedAuthenticationHandler a = (NamedAuthenticationHandler) pair.getFirst();
authentication.getAttributes().put(AuthenticationManager.AUTHENTICATION_METHOD_ATTRIBUTE, a.getName());
}
for (final AuthenticationMetaDataPopulator authenticationMetaDataPopulator : this.authenticationMetaDataPopulators) {
authentication = authenticationMetaDataPopulator
.populateAttributes(authentication, credentials);
}
return new ImmutableAuthentication(authentication.getPrincipal(),
authentication.getAttributes());
}
/**
* @param authenticationMetaDataPopulators the authenticationMetaDataPopulators to set.
*/
public final void setAuthenticationMetaDataPopulators(final List<AuthenticationMetaDataPopulator> authenticationMetaDataPopulators) {
this.authenticationMetaDataPopulators = authenticationMetaDataPopulators;
}
/**
* Follows the same rules as the "authenticate" method (i.e. should only return a fully populated object, or throw an exception)
*
* @param credentials the credentials to check
* @return the pair of authentication handler and principal. CANNOT be NULL.
* @throws AuthenticationException if there is an error authenticating.
*/
protected abstract Pair<AuthenticationHandler,Principal> authenticateAndObtainPrincipal(Credentials credentials) throws AuthenticationException;
/**
* Handles an authentication error raised by an {@link AuthenticationHandler}.
*
* @param handlerName The class name of the authentication handler.
* @param credentials Client credentials subject to authentication.
* @param e The exception that has occurred during authentication attempt.
*/
protected void handleError(final String handlerName, final Credentials credentials, final Exception e)
throws AuthenticationException {
if (e instanceof AuthenticationException) {
// CAS-1181 Log common authentication failures at INFO without stack trace
log.info("{} failed authenticating {}", handlerName, credentials);
throw (AuthenticationException) e;
}
log.error("{} threw error authenticating {}", handlerName, credentials, e);
throw new UncategorizedAuthenticationException(e.getClass().getName(), e) {
// Anonymous inner class allows us to throw uncategorized authentication error
// since base class is abstract.
};
}
protected static class Pair<A,B> {
private final A first;
private final B second;
public Pair(final A first, final B second) {
this.first = first;
this.second = second;
}
public A getFirst() {
return this.first;
}
public B getSecond() {
return this.second;
}
}
}

View File

@ -0,0 +1,74 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication;
import java.io.Serializable;
import java.util.Date;
import java.util.Map;
import org.jasig.cas.authentication.principal.Principal;
/**
* <p>
* The Authentication object represents a successful authentication request. It
* contains the principal that the authentication request was made for as well
* as the additional meta information such as the authenticated date and a map
* of attributes.
* </p>
* <p>
* An Authentication object must be serializable to permit persistance and
* clustering.
* </p>
* <p>
* Implementing classes must take care to ensure that the Map returned by
* getAttributes is serializable by using a Serializable map such as HashMap.
* </p>
*
* @author Dmitriy Kopylenko
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
* <p>
* This is a published and supported CAS Server 3 API.
* </p>
*/
public interface Authentication extends Serializable {
/**
* Method to obtain the Principal.
*
* @return a Principal implementation
*/
Principal getPrincipal();
/**
* Method to retrieve the timestamp of when this Authentication object was
* created.
*
* @return the date/time the authentication occurred.
*/
Date getAuthenticatedDate();
/**
* Attributes of the authentication (not the Principal).
*
* @return the map of attributes.
*/
Map<String, Object> getAttributes();
}

View File

@ -0,0 +1,56 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.principal.Credentials;
/**
* The AuthenticationManager class is the entity that determines the
* authenticity of the credentials provided. It (or a class it delegates to) is
* the sole authority on whether credentials are valid or not.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
* <p>
* This is a published and supported CAS Server 3 API.
* </p>
*/
public interface AuthenticationManager {
String AUTHENTICATION_METHOD_ATTRIBUTE = "authenticationMethod";
/**
* Method to validate the credentials provided. On successful validation, a
* fully populated Authentication object will be returned. Typically this
* will involve resolving a principal and providing any additional
* attributes, but specifics are left to the individual implementations to
* determine. Failure to authenticate is considered an exceptional case, and
* an AuthenticationException is thrown.
*
* @param credentials The credentials provided for authentication.
* @return fully populated Authentication object.
* @throws AuthenticationException if unable to determine validity of
* credentials or there is an extenuating circumstance related to
* credentials (i.e. Account locked).
*/
Authentication authenticate(final Credentials credentials)
throws AuthenticationException;
}

View File

@ -0,0 +1,155 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication;
import java.util.List;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.handler.AuthenticationHandler;
import org.jasig.cas.authentication.handler.BadCredentialsAuthenticationException;
import org.jasig.cas.authentication.handler.UnsupportedCredentialsException;
import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.CredentialsToPrincipalResolver;
import org.jasig.cas.authentication.principal.Principal;
/**
* <p>
* Default implementation of the AuthenticationManager. The
* AuthenticationManager follows the following algorithm. The manager loops
* through the array of AuthenticationHandlers searching for one that can
* attempt to determine the validity of the credentials. If it finds one, it
* tries that one. If that handler returns true, it continues on. If it returns
* false, it looks for another handler. If it throws an exception, it aborts the
* whole process and rethrows the exception. Next, it looks for a
* CredentialsToPrincipalResolver that can handle the credentials in order to
* create a Principal. Finally, it attempts to populate the Authentication
* object's attributes map using AuthenticationAttributesPopulators
* <p>
* Behavior is determined by external beans attached through three configuration
* properties. The Credentials are opaque to the manager. They are passed to the
* external beans to see if any can process the actual type represented by the
* Credentials marker.
* <p>
* AuthenticationManagerImpl requires the following properties to be set:
* </p>
* <ul>
* <li> <code>authenticationHandlers</code> - The array of
* AuthenticationHandlers that know how to process the credentials provided.
* <li> <code>credentialsToPrincipalResolvers</code> - The array of
* CredentialsToPrincipal resolvers that know how to process the credentials
* provided.
* </ul>
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
* @see org.jasig.cas.authentication.handler.AuthenticationHandler
* @see org.jasig.cas.authentication.principal.CredentialsToPrincipalResolver
* @see org.jasig.cas.authentication.AuthenticationMetaDataPopulator
*/
public final class AuthenticationManagerImpl extends AbstractAuthenticationManager {
/** An array of authentication handlers. */
@NotNull
@Size(min=1)
private List<AuthenticationHandler> authenticationHandlers;
/** An array of CredentialsToPrincipalResolvers. */
@NotNull
@Size(min=1)
private List<CredentialsToPrincipalResolver> credentialsToPrincipalResolvers;
@Override
protected Pair<AuthenticationHandler, Principal> authenticateAndObtainPrincipal(final Credentials credentials) throws AuthenticationException {
boolean foundSupported = false;
boolean authenticated = false;
AuthenticationHandler authenticatedClass = null;
String handlerName;
for (final AuthenticationHandler authenticationHandler : this.authenticationHandlers) {
if (authenticationHandler.supports(credentials)) {
foundSupported = true;
handlerName = authenticationHandler.getClass().getName();
try {
if (!authenticationHandler.authenticate(credentials)) {
log.info("{} failed to authenticate {}", handlerName, credentials);
} else {
log.info("{} successfully authenticated {}", handlerName, credentials);
authenticatedClass = authenticationHandler;
authenticated = true;
break;
}
} catch (final Exception e) {
handleError(handlerName, credentials, e);
}
}
}
if (!authenticated) {
if (foundSupported) {
throw BadCredentialsAuthenticationException.ERROR;
}
throw UnsupportedCredentialsException.ERROR;
}
foundSupported = false;
for (final CredentialsToPrincipalResolver credentialsToPrincipalResolver : this.credentialsToPrincipalResolvers) {
if (credentialsToPrincipalResolver.supports(credentials)) {
final Principal principal = credentialsToPrincipalResolver.resolvePrincipal(credentials);
log.info("Resolved principal " + principal);
foundSupported = true;
if (principal != null) {
return new Pair<AuthenticationHandler,Principal>(authenticatedClass, principal);
}
}
}
if (foundSupported) {
if (log.isDebugEnabled()) {
log.debug("CredentialsToPrincipalResolver found but no principal returned.");
}
throw BadCredentialsAuthenticationException.ERROR;
}
log.error("CredentialsToPrincipalResolver not found for " + credentials.getClass().getName());
throw UnsupportedCredentialsException.ERROR;
}
/**
* @param authenticationHandlers The authenticationHandlers to set.
*/
public void setAuthenticationHandlers(
final List<AuthenticationHandler> authenticationHandlers) {
this.authenticationHandlers = authenticationHandlers;
}
/**
* @param credentialsToPrincipalResolvers The
* credentialsToPrincipalResolvers to set.
*/
public void setCredentialsToPrincipalResolvers(
final List<CredentialsToPrincipalResolver> credentialsToPrincipalResolvers) {
this.credentialsToPrincipalResolvers = credentialsToPrincipalResolvers;
}
}

View File

@ -0,0 +1,53 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication;
import org.jasig.cas.authentication.principal.Credentials;
/**
* An extension point to the Authentication process that allows CAS to provide
* additional attributes related to the overall Authentication (such as
* authentication type) that are specific to the Authentication request versus
* the Principal itself. AuthenticationAttributePopulators are a new feature in
* CAS3. In order for an installation to be CAS2 compliant, deployers do not
* need an AuthenticationMetaDataPopulator.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
* <p>
* This is a published and supported CAS Server 3 API.
* </p>
*/
public interface AuthenticationMetaDataPopulator {
/**
* Provided with an Authentication object and the original credentials
* presented, provide any additional attributes to the Authentication
* object. Implementations have the option of returning the same
* Authentication object, or a new one.
*
* @param authentication The Authentication to potentially augment with
* additional attributes.
* @return the original Authentication object or a new Authentication
* object.
*/
Authentication populateAttributes(Authentication authentication,
Credentials credentials);
}

View File

@ -0,0 +1,112 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication;
import java.util.Map;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.handler.AuthenticationHandler;
import org.jasig.cas.authentication.handler.BadCredentialsAuthenticationException;
import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.CredentialsToPrincipalResolver;
import org.jasig.cas.authentication.principal.Principal;
import org.springframework.util.Assert;
/**
* Authentication Manager that provides a direct mapping between credentials
* provided and the authentication handler used to authenticate the user.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public final class DirectMappingAuthenticationManagerImpl extends AbstractAuthenticationManager {
@NotNull
@Size(min=1)
private Map<Class< ? extends Credentials>, DirectAuthenticationHandlerMappingHolder> credentialsMapping;
/**
* @throws IllegalArgumentException if a mapping cannot be found.
* @see org.jasig.cas.authentication.AuthenticationManager#authenticate(org.jasig.cas.authentication.principal.Credentials)
*/
@Override
protected Pair<AuthenticationHandler, Principal> authenticateAndObtainPrincipal(final Credentials credentials) throws AuthenticationException {
final Class< ? extends Credentials> credentialsClass = credentials.getClass();
final DirectAuthenticationHandlerMappingHolder d = this.credentialsMapping.get(credentialsClass);
Assert.notNull(d, "no mapping found for: " + credentialsClass.getName());
final String handlerName = d.getAuthenticationHandler().getClass().getSimpleName();
boolean authenticated = false;
try {
authenticated = d.getAuthenticationHandler().authenticate(credentials);
} catch (final Exception e) {
handleError(handlerName, credentials, e);
}
if (!authenticated) {
log.info("{} failed to authenticate {}", handlerName, credentials);
throw BadCredentialsAuthenticationException.ERROR;
}
log.info("{} successfully authenticated {}", handlerName, credentials);
final Principal p = d.getCredentialsToPrincipalResolver().resolvePrincipal(credentials);
return new Pair<AuthenticationHandler,Principal>(d.getAuthenticationHandler(), p);
}
public final void setCredentialsMapping(
final Map<Class< ? extends Credentials>, DirectAuthenticationHandlerMappingHolder> credentialsMapping) {
this.credentialsMapping = credentialsMapping;
}
public static final class DirectAuthenticationHandlerMappingHolder {
private AuthenticationHandler authenticationHandler;
private CredentialsToPrincipalResolver credentialsToPrincipalResolver;
public DirectAuthenticationHandlerMappingHolder() {
// nothing to do
}
public final AuthenticationHandler getAuthenticationHandler() {
return this.authenticationHandler;
}
public void setAuthenticationHandler(
final AuthenticationHandler authenticationHandler) {
this.authenticationHandler = authenticationHandler;
}
public CredentialsToPrincipalResolver getCredentialsToPrincipalResolver() {
return this.credentialsToPrincipalResolver;
}
public void setCredentialsToPrincipalResolver(
final CredentialsToPrincipalResolver credentialsToPrincipalResolver) {
this.credentialsToPrincipalResolver = credentialsToPrincipalResolver;
}
}
}

View File

@ -0,0 +1,78 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.jasig.cas.authentication.principal.Principal;
/**
* Default implementation of Authentication interface. ImmutableAuthentication
* is an immutable object and thus its attributes cannot be changed.
* <p>
* Instanciators of the ImmutableAuthentication class must take care that the
* map they provide is serializable (i.e. HashMap).
*
* @author Dmitriy Kopylenko
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
*/
public final class ImmutableAuthentication extends AbstractAuthentication {
/** UID for serializing. */
private static final long serialVersionUID = 3906647483978365235L;
private static final Map<String, Object> EMPTY_MAP = Collections.unmodifiableMap(new HashMap<String, Object>());
/** The date/time this authentication object became valid. */
final Date authenticatedDate;
/**
* Constructor that accepts both a principal and a map.
*
* @param principal Principal representing user
* @param attributes Authentication attributes map.
* @throws IllegalArgumentException if the principal is null.
*/
public ImmutableAuthentication(final Principal principal,
final Map<String, Object> attributes) {
super(principal, attributes == null || attributes.isEmpty()
? EMPTY_MAP : Collections.unmodifiableMap(attributes));
this.authenticatedDate = new Date();
}
/**
* Constructor that assumes there are no additional authentication
* attributes.
*
* @param principal the Principal representing the authenticated entity.
*/
public ImmutableAuthentication(final Principal principal) {
this(principal, null);
}
public Date getAuthenticatedDate() {
return new Date(this.authenticatedDate.getTime());
}
}

View File

@ -0,0 +1,84 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication;
import java.util.Map;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.handler.AuthenticationHandler;
import org.jasig.cas.authentication.handler.BadCredentialsAuthenticationException;
import org.jasig.cas.authentication.handler.UnsupportedCredentialsException;
import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.CredentialsToPrincipalResolver;
import org.jasig.cas.authentication.principal.Principal;
/**
* Ensures that all authentication handlers are tried, but if one is tried, the associated CredentialsToPrincipalResolver is used.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.3.5
*/
public class LinkedAuthenticationHandlerAndCredentialsToPrincipalResolverAuthenticationManager extends AbstractAuthenticationManager {
@NotNull
@Size(min = 1)
private final Map<AuthenticationHandler, CredentialsToPrincipalResolver> linkedHandlers;
public LinkedAuthenticationHandlerAndCredentialsToPrincipalResolverAuthenticationManager(final Map<AuthenticationHandler,CredentialsToPrincipalResolver> linkedHandlers) {
this.linkedHandlers = linkedHandlers;
}
@Override
protected Pair<AuthenticationHandler, Principal> authenticateAndObtainPrincipal(final Credentials credentials) throws AuthenticationException {
boolean foundOneThatWorks = false;
String handlerName;
for (final AuthenticationHandler authenticationHandler : this.linkedHandlers.keySet()) {
if (!authenticationHandler.supports(credentials)) {
continue;
}
foundOneThatWorks = true;
boolean authenticated = false;
handlerName = authenticationHandler.getClass().getName();
try {
authenticated = authenticationHandler.authenticate(credentials);
} catch (final Exception e) {
handleError(handlerName, credentials, e);
}
if (authenticated) {
log.info("{} successfully authenticated {}", handlerName, credentials);
final Principal p = this.linkedHandlers.get(authenticationHandler).resolvePrincipal(credentials);
return new Pair<AuthenticationHandler,Principal>(authenticationHandler, p);
}
log.info("{} failed to authenticate {}", handlerName, credentials);
}
if (foundOneThatWorks) {
throw BadCredentialsAuthenticationException.ERROR;
}
throw UnsupportedCredentialsException.ERROR;
}
}

View File

@ -0,0 +1,56 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication;
import java.util.Date;
import java.util.HashMap;
import org.jasig.cas.authentication.principal.Principal;
/**
* Mutable implementation of Authentication interface.
* <p>
* Instanciators of the MutableAuthentication class must take care that the map
* they provide is serializable (i.e. HashMap).
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0.3
*/
public final class MutableAuthentication extends AbstractAuthentication {
/** Unique Id for serialization. */
private static final long serialVersionUID = -4415875344376642246L;
/** The date/time this authentication object became valid. */
private final Date authenticatedDate;
public MutableAuthentication(final Principal principal) {
this(principal, new Date());
}
public MutableAuthentication(final Principal principal, final Date date) {
super(principal, new HashMap<String, Object>());
this.authenticatedDate = date;
}
public Date getAuthenticatedDate() {
return this.authenticatedDate;
}
}

View File

@ -0,0 +1,94 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication;
import java.util.HashMap;
import java.util.Map;
import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.HttpBasedServiceCredentials;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
/**
* AuthenticationMetaDataPopulator to retrieve the Authentication Type.
* <p>
* Note: Authentication Methods are exposed under the key:
* <code>samlAuthenticationStatement::authMethod</code> in the Authentication
* attributes map.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public class SamlAuthenticationMetaDataPopulator implements
AuthenticationMetaDataPopulator {
public static final String ATTRIBUTE_AUTHENTICATION_METHOD = "samlAuthenticationStatementAuthMethod";
public static final String AUTHN_METHOD_PASSWORD = "urn:oasis:names:tc:SAML:1.0:am:password";
public static final String AUTHN_METHOD_SSL_TLS_CLIENT = "urn:ietf:rfc:2246";
public static final String AUTHN_METHOD_X509_PUBLICKEY = "urn:oasis:names:tc:SAML:1.0:am:X509-PKI";
public static final String AUTHN_METHOD_UNSPECIFIED = "urn:oasis:names:tc:SAML:1.0:am:unspecified";
private final Map<String, String> authenticationMethods = new HashMap<String, String>();
public SamlAuthenticationMetaDataPopulator() {
this.authenticationMethods.put(
HttpBasedServiceCredentials.class.getName(),
AUTHN_METHOD_SSL_TLS_CLIENT);
this.authenticationMethods.put(
UsernamePasswordCredentials.class.getName(),
AUTHN_METHOD_PASSWORD);
// Next two classes are in other modules, so avoid using Class#getName() to prevent circular dependency
this.authenticationMethods.put(
"org.jasig.cas.adaptors.trusted.authentication.principal.PrincipalBearingCredentials",
AUTHN_METHOD_UNSPECIFIED);
this.authenticationMethods.put(
"org.jasig.cas.adaptors.x509.authentication.principal.X509CertificateCredentials",
AUTHN_METHOD_X509_PUBLICKEY);
}
public final Authentication populateAttributes(final Authentication authentication, final Credentials credentials) {
final String credentialsClass = credentials.getClass().getName();
final String authenticationMethod = this.authenticationMethods.get(credentialsClass);
authentication.getAttributes().put(ATTRIBUTE_AUTHENTICATION_METHOD, authenticationMethod);
return authentication;
}
/**
* Map of user-defined mappings. Note it is possible to over-ride the
* defaults. Mapping should be of the following type:
* <p>(<String version of Package/Class Name> <SAML Type>)
* <p>
* Example: (<"org.jasig.cas.authentication.principal.HttpBasedServiceCredentials">
* <SAMLAuthenticationStatement.AuthenticationMethod_SSL_TLS_Client>)
*
* @param userDefinedMappings map of user defined authentication types.
*/
public void setUserDefinedMappings(final Map<String, String> userDefinedMappings) {
this.authenticationMethods.putAll(userDefinedMappings);
}
}

View File

@ -0,0 +1,120 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler;
/**
* The most generic type of authentication exception that one can catch if not
* sure what specific implementation will be thrown. Top of the tree of all
* other AuthenticationExceptions.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
*/
public abstract class AuthenticationException extends Exception {
/** Serializable ID. */
private static final long serialVersionUID = 3906648604830611762L;
/** The code to return for resolving to a message description. */
private String code;
/** The error type that provides additional info about the nature of the exception cause **/
private String type = "error";
/**
* Constructor that takes a code description of the error. These codes
* normally have a corresponding entries in the messages file for the
* internationalization of error messages.
*
* @param code The short unique identifier for this error.
*/
public AuthenticationException(final String code) {
this.code = code;
}
/**
* Constructor that takes a <code>code</code> description of the error along with the exception
* <code>msg</code> generally for logging purposes. These codes normally have a corresponding
* entries in the messages file for the internationalization of error messages.
*
* @param code The short unique identifier for this error.
* @param msg The error message associated with this exception for additional logging purposes.
*/
public AuthenticationException(final String code, final String msg) {
super(msg);
this.code = code;
}
/**
* Constructor that takes a <code>code</code> description of the error along with the exception
* <code>msg</code> generally for logging purposes and the <code>type</code> of the error that originally caused the exception.
* These codes normally have a corresponding entries in the messages file for the internationalization of error messages.
*
* @param code The short unique identifier for this error.
* @param msg The error message associated with this exception for additional logging purposes.
* @param type The type of the error message that caused the exception to be thrown. By default,
* all errors are considered of <code>error</code>.
*/
public AuthenticationException(final String code, final String msg, final String type) {
super(msg);
this.code = code;
this.type = type;
}
/**
* Constructor that takes a code description of the error and the chained
* exception. These codes normally have a corresponding entries in the
* messages file for the internationalization of error messages.
*
* @param code The short unique identifier for this error.
* @param throwable The chained exception for this AuthenticationException
*/
public AuthenticationException(final String code, final Throwable throwable) {
super(throwable);
this.code = code;
}
/**
* Method to return the error type of this exception
*
* @return the String identifier for the cause of this error.
*/
public final String getType() {
return this.type;
}
/**
* Method to return the unique identifier for this error type.
*
* @return the String identifier for this error type.
*/
public final String getCode() {
return this.code;
}
@Override
public final String toString() {
String msg = getCode();
if (getMessage() != null && getMessage().trim().length() > 0)
msg = ":" + getMessage();
return msg;
}
}

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 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.authentication.handler;
import org.jasig.cas.authentication.principal.Credentials;
/**
* Validate Credentials support for AuthenticationManagerImpl.
* <p>
* Determines that Credentials are valid. Password-based credentials may be
* tested against an external LDAP, Kerberos, JDBC source. Certificates may be
* checked against a list of CA's and do the usual chain validation.
* Implementations must be parameterized with their sources of information.
* <p>
* Callers to this class should first call supports to determine if the
* AuthenticationHandler can authenticate the credentials provided.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
* <p>
* This is a published and supported CAS Server 3 API.
* </p>
*/
public interface AuthenticationHandler {
/**
* Method to determine if the credentials supplied are valid.
*
* @param credentials The credentials to validate.
* @return true if valid, return false otherwise.
* @throws AuthenticationException An AuthenticationException can contain
* details about why a particular authentication request failed.
*/
boolean authenticate(Credentials credentials)
throws AuthenticationException;
/**
* Method to check if the handler knows how to handle the credentials
* provided. It may be a simple check of the Credentials class or something
* more complicated such as scanning the information contained in the
* Credentials object.
*
* @param credentials The credentials to check.
* @return true if the handler supports the Credentials, false othewrise.
*/
boolean supports(Credentials credentials);
}

View File

@ -0,0 +1,88 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler;
/**
* Generic Bad Credentials Exception. This can be thrown when the system knows
* the credentials are not valid specificially because they are bad. Subclasses
* can be specific to a certain type of Credentials
* (BadUsernamePassowrdCredentials).
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
*/
public class BadCredentialsAuthenticationException extends
AuthenticationException {
/**
* Static instance of class to prevent cost incurred by creating new
* instance.
*/
public static final BadCredentialsAuthenticationException ERROR = new BadCredentialsAuthenticationException();
/** UID for serializable objects. */
private static final long serialVersionUID = 3256719585087797044L;
/**
* Default constructor that does not allow the chaining of exceptions and
* uses the default code as the error code for this exception.
*/
public static final String CODE = "error.authentication.credentials.bad";
/**
* Default constructor that does not allow the chaining of exceptions and
* uses the default code as the error code for this exception.
*/
public BadCredentialsAuthenticationException() {
super(CODE);
}
/**
* Constructor to allow for the chaining of exceptions. Constructor defaults
* to default code.
*
* @param throwable the chainable exception.
*/
public BadCredentialsAuthenticationException(final Throwable throwable) {
super(CODE, throwable);
}
/**
* Constructor method to allow for providing a custom code to associate with
* this exception.
*
* @param code the code to use.
*/
public BadCredentialsAuthenticationException(final String code) {
super(code);
}
/**
* Constructor to allow for the chaining of exceptions and use of a
* non-default code.
*
* @param code the user-specified code.
* @param throwable the chainable exception.
*/
public BadCredentialsAuthenticationException(final String code,
final Throwable throwable) {
super(code, throwable);
}
}

View File

@ -0,0 +1,81 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler;
/**
* The exception to throw when we know the username is correct but the password
* is not.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
*/
public class BadPasswordAuthenticationException extends
BadUsernameOrPasswordAuthenticationException {
/** Static instance of BadPasswordAuthenticationException. */
public static final BadPasswordAuthenticationException ERROR = new BadPasswordAuthenticationException();
/** Unique ID for serializing. */
private static final long serialVersionUID = 3977861752513837361L;
/** The default code for this exception used for message resolving. */
private static final String CODE = "error.authentication.credentials.bad.usernameorpassword.password";
/**
* Default constructor that does not allow the chaining of exceptions and
* uses the default code as the error code for this exception.
*/
public BadPasswordAuthenticationException() {
super(CODE);
}
/**
* Constructor that allows for the chaining of exceptions. Defaults to the
* default code provided for this exception.
*
* @param throwable the chained exception.
*/
public BadPasswordAuthenticationException(final Throwable throwable) {
super(CODE, throwable);
}
/**
* Constructor that allows for providing a custom error code for this class.
* Error codes are often used to resolve exceptions into messages. Providing
* a custom error code allows the use of a different message.
*
* @param code the custom code to use with this exception.
*/
public BadPasswordAuthenticationException(final String code) {
super(code);
}
/**
* Constructor that allows for chaining of exceptions and a custom error
* code.
*
* @param code the custom error code to use in message resolving.
* @param throwable the chained exception.
*/
public BadPasswordAuthenticationException(final String code,
final Throwable throwable) {
super(code, throwable);
}
}

View File

@ -0,0 +1,82 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler;
/**
* Exception to throw when we know the credentials provided were
* username/password and the combination is wrong.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
*/
public class BadUsernameOrPasswordAuthenticationException extends
BadCredentialsAuthenticationException {
/** Static instance of BadUsernameOrPasswordAuthenticationException. */
public static final BadUsernameOrPasswordAuthenticationException ERROR = new BadUsernameOrPasswordAuthenticationException();
/** Unique ID for serializing. */
private static final long serialVersionUID = 3977861752513837361L;
/** The default code for this exception used for message resolving. */
private static final String CODE = "error.authentication.credentials.bad.usernameorpassword";
/**
* Default constructor that does not allow the chaining of exceptions and
* uses the default code as the error code for this exception.
*/
public BadUsernameOrPasswordAuthenticationException() {
super(CODE);
}
/**
* Constructor that allows for the chaining of exceptions. Defaults to the
* default code provided for this exception.
*
* @param throwable the chained exception.
*/
public BadUsernameOrPasswordAuthenticationException(
final Throwable throwable) {
super(CODE, throwable);
}
/**
* Constructor that allows for providing a custom error code for this class.
* Error codes are often used to resolve exceptions into messages. Providing
* a custom error code allows the use of a different message.
*
* @param code the custom code to use with this exception.
*/
public BadUsernameOrPasswordAuthenticationException(final String code) {
super(code);
}
/**
* Constructor that allows for chaining of exceptions and a custom error
* code.
*
* @param code the custom error code to use in message resolving.
* @param throwable the chained exception.
*/
public BadUsernameOrPasswordAuthenticationException(final String code,
final Throwable throwable) {
super(code, throwable);
}
}

View File

@ -0,0 +1,81 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler;
/**
* Exception to represent credentials that have been blocked for a reason such
* as Locked account.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
*/
public class BlockedCredentialsAuthenticationException extends
AuthenticationException {
/** Static instance of BlockedCredentialsAuthenticationException. */
public static final BlockedCredentialsAuthenticationException ERROR = new BlockedCredentialsAuthenticationException();
/** Unique ID for serialization. */
private static final long serialVersionUID = 3544669598642420017L;
/** The default code for this exception used for message resolving. */
private static final String CODE = "error.authentication.credentials.blocked";
/**
* Default constructor that does not allow the chaining of exceptions and
* uses the default code as the error code for this exception.
*/
public BlockedCredentialsAuthenticationException() {
super(CODE);
}
/**
* Constructor that allows for the chaining of exceptions. Defaults to the
* default code provided for this exception.
*
* @param throwable the chained exception.
*/
public BlockedCredentialsAuthenticationException(final Throwable throwable) {
super(CODE, throwable);
}
/**
* Constructor that allows for providing a custom error code for this class.
* Error codes are often used to resolve exceptions into messages. Providing
* a custom error code allows the use of a different message.
*
* @param code the custom code to use with this exception.
*/
public BlockedCredentialsAuthenticationException(final String code) {
super(code);
}
/**
* Constructor that allows for chaining of exceptions and a custom error
* code.
*
* @param code the custom error code to use in message resolving.
* @param throwable the chained exception.
*/
public BlockedCredentialsAuthenticationException(final String code,
final Throwable throwable) {
super(code, throwable);
}
}

View File

@ -0,0 +1,96 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler;
import org.springframework.util.StringUtils;
import javax.validation.constraints.NotNull;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* Implementation of PasswordEncoder using message digest. Can accept any
* message digest that the JDK can accept, including MD5 and SHA1. Returns the
* equivalent Hash you would get from a Perl digest.
*
* @author Scott Battaglia
* @author Stephen More
* @version $Revision$ $Date$
* @since 3.1
*/
public final class DefaultPasswordEncoder implements PasswordEncoder {
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
@NotNull
private final String encodingAlgorithm;
private String characterEncoding;
public DefaultPasswordEncoder(final String encodingAlgorithm) {
this.encodingAlgorithm = encodingAlgorithm;
}
public String encode(final String password) {
if (password == null) {
return null;
}
try {
MessageDigest messageDigest = MessageDigest
.getInstance(this.encodingAlgorithm);
if (StringUtils.hasText(this.characterEncoding)) {
messageDigest.update(password.getBytes(this.characterEncoding));
} else {
messageDigest.update(password.getBytes());
}
final byte[] digest = messageDigest.digest();
return getFormattedText(digest);
} catch (final NoSuchAlgorithmException e) {
throw new SecurityException(e);
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* Takes the raw bytes from the digest and formats them correct.
*
* @param bytes the raw bytes from the digest.
* @return the formatted bytes.
*/
private String getFormattedText(byte[] bytes) {
final StringBuilder buf = new StringBuilder(bytes.length * 2);
for (int j = 0; j < bytes.length; j++) {
buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
}
return buf.toString();
}
public final void setCharacterEncoding(final String characterEncoding) {
this.characterEncoding = characterEncoding;
}
}

View File

@ -0,0 +1,33 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at 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.authentication.handler;
/**
* Offers AuthenticationHandlers a way to identify themselves by a
* user-configured name.
*
* @author Scott Battaglia
* @version $Revision: 1.1 $ $Date: 2005/08/19 18:27:17 $
* @since 3.2.1
*
*/
public interface NamedAuthenticationHandler extends AuthenticationHandler {
String getName();
}

View File

@ -0,0 +1,33 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at 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.authentication.handler;
/**
* Simple implementation that actually does NO transformation.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.3.6
*/
public final class NoOpPrincipalNameTransformer implements PrincipalNameTransformer {
public String transform(final String formUserId) {
return formUserId;
}
}

View File

@ -0,0 +1,47 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler;
/**
* Interface to provide a standard way to translate a plaintext password into a
* different representation of that password so that the password may be
* compared with the stored encrypted password without having to decode the
* encrypted password.
* <p>
* PasswordEncoders are useful because often the stored passwords are encoded
* with a one way hash function which makes them almost impossible to decode.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
* <p>
* This is a published and supported CAS Server 3 API.
* </p>
*/
public interface PasswordEncoder {
/**
* Method that actually performs the transformation of the plaintext
* password into the encrypted password.
*
* @param password the password to translate
* @return the transformed version of the password
*/
String encode(String password);
}

View File

@ -0,0 +1,34 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler;
/**
* Default password encoder for the case where no password encoder is needed.
* Encoding results in the same password that was passed in.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
*/
public final class PlainTextPasswordEncoder implements PasswordEncoder {
public String encode(final String password) {
return password;
}
}

View File

@ -0,0 +1,60 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler;
/**
* Transform the user id by adding a prefix or suffix.
*
* @author Howard Gilbert
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.3.6
*/
public final class PrefixSuffixPrincipalNameTransformer implements PrincipalNameTransformer {
private String prefix;
private String suffix;
public String transform(final String formUserId) {
final StringBuilder stringBuilder = new StringBuilder();
if (this.prefix != null) {
stringBuilder.append(this.prefix);
}
stringBuilder.append(formUserId);
if (this.suffix != null) {
stringBuilder.append(this.suffix);
}
return stringBuilder.toString();
}
public void setPrefix(final String prefix) {
this.prefix = prefix;
}
public void setSuffix(final String suffix) {
this.suffix = suffix;
}
}

View File

@ -0,0 +1,45 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler;
/**
* @author Howard Gilbert
* @version $Revision$ $Date$
* @since 3.3.6
*/
public interface PrincipalNameTransformer {
/**
* Transform the string typed into the login form into a tentative Principal Name to be
* validated by a specific type of Authentication Handler.
*
* <p>The Principal Name eventually assigned by the CredentialsToPrincipalResolver may
* be unqualified ("AENewman"). However, validation of the Principal name against a
* particular backend source represented by a particular Authentication Handler may
* require transformation to a temporary fully qualified format such as
* AENewman@MAD.DCCOMICS.COM or MAD\AENewman. After validation, this form of the
* Principal name is discarded in favor of the choice made by the Resolver.
*
* @param formUserId The raw userid typed into the login form
* @return the string that the Authentication Handler should lookup in the backend system
*/
public String transform(String formUserId);
}

View File

@ -0,0 +1,54 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at 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.authentication.handler;
/**
* Generic abstract exception to extend when you don't know what the heck is
* going on.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
*/
public abstract class UncategorizedAuthenticationException extends
AuthenticationException {
/**
* Constructor that allows for providing a custom error code for this class.
* Error codes are often used to resolve exceptions into messages. Providing
* a custom error code allows the use of a different message.
*
* @param code the custom code to use with this exception.
*/
public UncategorizedAuthenticationException(final String code) {
super(code);
}
/**
* Constructor that allows for chaining of exceptions and a custom error
* code.
*
* @param code the custom error code to use in message resolving.
* @param throwable the chained exception.
*/
public UncategorizedAuthenticationException(final String code,
final Throwable throwable) {
super(code, throwable);
}
}

View File

@ -0,0 +1,81 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler;
/**
* The exception to throw when we explicitly don't know anything about the
* username.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
*/
public class UnknownUsernameAuthenticationException extends
BadUsernameOrPasswordAuthenticationException {
/** Static instance of UnknownUsernameAuthenticationException. */
public static final UnknownUsernameAuthenticationException ERROR = new UnknownUsernameAuthenticationException();
/** Unique ID for serializing. */
private static final long serialVersionUID = 3977861752513837361L;
/** The code description of this exception. */
private static final String CODE = "error.authentication.credentials.bad.usernameorpassword.username";
/**
* Default constructor that does not allow the chaining of exceptions and
* uses the default code as the error code for this exception.
*/
public UnknownUsernameAuthenticationException() {
super(CODE);
}
/**
* Constructor that allows for the chaining of exceptions. Defaults to the
* default code provided for this exception.
*
* @param throwable the chained exception.
*/
public UnknownUsernameAuthenticationException(final Throwable throwable) {
super(CODE, throwable);
}
/**
* Constructor that allows for providing a custom error code for this class.
* Error codes are often used to resolve exceptions into messages. Providing
* a custom error code allows the use of a different message.
*
* @param code the custom code to use with this exception.
*/
public UnknownUsernameAuthenticationException(final String code) {
super(code);
}
/**
* Constructor that allows for chaining of exceptions and a custom error
* code.
*
* @param code the custom error code to use in message resolving.
* @param throwable the chained exception.
*/
public UnknownUsernameAuthenticationException(final String code,
final Throwable throwable) {
super(code, throwable);
}
}

View File

@ -0,0 +1,59 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler;
/**
* The exception thrown when a Handler does not know how to determine the
* validity of the credentials based on the fact that it does not know what to
* do with the credentials presented.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
*/
public final class UnsupportedCredentialsException extends
AuthenticationException {
/** Static instance of UnsupportedCredentialsException. */
public static final UnsupportedCredentialsException ERROR = new UnsupportedCredentialsException();
/** Unique ID for serializing. */
private static final long serialVersionUID = 3977861752513837361L;
/** The code description of this exception. */
private static final String CODE = "error.authentication.credentials.unsupported";
/**
* Default constructor that does not allow the chaining of exceptions and
* uses the default code as the error code for this exception.
*/
public UnsupportedCredentialsException() {
super(CODE);
}
/**
* Constructor that allows for the chaining of exceptions. Defaults to the
* default code provided for this exception.
*
* @param throwable the chained exception.
*/
public UnsupportedCredentialsException(final Throwable throwable) {
super(CODE, throwable);
}
}

View File

@ -0,0 +1,46 @@
<!--
Licensed to Jasig under one or more contributor license
agreements. See the NOTICE file distributed with this work
for additional information regarding copyright ownership.
Jasig licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a
copy of the License at the following location:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html>
<body>
The handler package contains the classes used to authenticate a user. It contains
the AuthenticationHandler interface which is used to validate credentials. It also
contains the PasswordEncoders which are used by implementations of the AuthenticationHandler
to provide conversion from plain text to whatever the password is encoded as in the data
store.
<p>
The package also contains a well-defined exception heirarchy to allow fine-grained error
messages to be displayed.
<p>
Examples of AuthenticationHandlers implementations:
<ul>
<li>If the credentials are a Userid and Password, then it submits them to an
external Kerberos, LDAP, or JDBC authority for validation.</li>
<li>If the credentials are a Certificate, then it verifies the Issuer chain
against some list of reliable CAs, checks the date to make sure it hasn't
expired, and checks the CRL to make sure it wasn't revoked.</li>
<li>If authentication has been done by the Servlet Container or by a Filter, then
the Credentials have been extracted from the HttpRequest object. Notably, this
will include the REMOTE_USER. Such Credentials are implicitly trusted and self
validating, so an AuthenticationHandler recognizing such an object will indicate
that it is valid without inspecting its contents.</li>
</ul>
</body>
</html>

View File

@ -0,0 +1,92 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler.support;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.handler.NamedAuthenticationHandler;
import org.jasig.cas.authentication.principal.Credentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.constraints.NotNull;
/**
* Abstract authentication handler that allows deployers to utilize the bundled
* AuthenticationHandlers while providing a mechanism to perform tasks before
* and after authentication.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public abstract class AbstractPreAndPostProcessingAuthenticationHandler
implements NamedAuthenticationHandler {
/** Instance of logging for subclasses. */
protected Logger log = LoggerFactory.getLogger(this.getClass());
/** The name of the authentication handler. */
@NotNull
private String name = getClass().getName();
/**
* Method to execute before authentication occurs.
*
* @param credentials the Credentials supplied
* @return true if authentication should continue, false otherwise.
*/
protected boolean preAuthenticate(final Credentials credentials) {
return true;
}
/**
* Method to execute after authentication occurs.
*
* @param credentials the supplied credentials
* @param authenticated the result of the authentication attempt.
* @return true if the handler should return true, false otherwise.
*/
protected boolean postAuthenticate(final Credentials credentials,
final boolean authenticated) {
return authenticated;
}
public final void setName(final String name) {
this.name = name;
}
public final String getName() {
return this.name;
}
public final boolean authenticate(final Credentials credentials)
throws AuthenticationException {
if (!preAuthenticate(credentials)) {
return false;
}
final boolean authenticated = doAuthentication(credentials);
return postAuthenticate(credentials, authenticated);
}
protected abstract boolean doAuthentication(final Credentials credentials)
throws AuthenticationException;
}

View File

@ -0,0 +1,144 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler.support;
import org.jasig.cas.authentication.handler.*;
import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
import javax.validation.constraints.NotNull;
/**
* Abstract class to override supports so that we don't need to duplicate the
* check for UsernamePasswordCredentials.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
* <p>
* This is a published and supported CAS Server 3 API.
* </p>
*/
public abstract class AbstractUsernamePasswordAuthenticationHandler extends
AbstractPreAndPostProcessingAuthenticationHandler {
/** Default class to support if one is not supplied. */
private static final Class<UsernamePasswordCredentials> DEFAULT_CLASS = UsernamePasswordCredentials.class;
/** Class that this instance will support. */
@NotNull
private Class< ? > classToSupport = DEFAULT_CLASS;
/**
* Boolean to determine whether to support subclasses of the class to
* support.
*/
private boolean supportSubClasses = true;
/**
* PasswordEncoder to be used by subclasses to encode passwords for
* comparing against a resource.
*/
@NotNull
private PasswordEncoder passwordEncoder = new PlainTextPasswordEncoder();
@NotNull
private PrincipalNameTransformer principalNameTransformer = new NoOpPrincipalNameTransformer();
/**
* Method automatically handles conversion to UsernamePasswordCredentials
* and delegates to abstract authenticateUsernamePasswordInternal so
* subclasses do not need to cast.
*/
protected final boolean doAuthentication(final Credentials credentials)
throws AuthenticationException {
return authenticateUsernamePasswordInternal((UsernamePasswordCredentials) credentials);
}
/**
* Abstract convenience method that assumes the credentials passed in are a
* subclass of UsernamePasswordCredentials.
*
* @param credentials the credentials representing the Username and Password
* presented to CAS
* @return true if the credentials are authentic, false otherwise.
* @throws AuthenticationException if authenticity cannot be determined.
*/
protected abstract boolean authenticateUsernamePasswordInternal(
final UsernamePasswordCredentials credentials)
throws AuthenticationException;
/**
* Method to return the PasswordEncoder to be used to encode passwords.
*
* @return the PasswordEncoder associated with this class.
*/
protected final PasswordEncoder getPasswordEncoder() {
return this.passwordEncoder;
}
protected final PrincipalNameTransformer getPrincipalNameTransformer() {
return this.principalNameTransformer;
}
/**
* Method to set the class to support.
*
* @param classToSupport the class we want this handler to support
* explicitly.
*/
public final void setClassToSupport(final Class< ? > classToSupport) {
this.classToSupport = classToSupport;
}
/**
* Method to set whether this handler will support subclasses of the
* supported class.
*
* @param supportSubClasses boolean of whether to support subclasses or not.
*/
public final void setSupportSubClasses(final boolean supportSubClasses) {
this.supportSubClasses = supportSubClasses;
}
/**
* Sets the PasswordEncoder to be used with this class.
*
* @param passwordEncoder the PasswordEncoder to use when encoding
* passwords.
*/
public final void setPasswordEncoder(final PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
public final void setPrincipalNameTransformer(final PrincipalNameTransformer principalNameTransformer) {
this.principalNameTransformer = principalNameTransformer;
}
/**
* @return true if the credentials are not null and the credentials class is
* equal to the class defined in classToSupport.
*/
public final boolean supports(final Credentials credentials) {
return credentials != null
&& (this.classToSupport.equals(credentials.getClass()) || (this.classToSupport
.isAssignableFrom(credentials.getClass()))
&& this.supportSubClasses);
}
}

View File

@ -0,0 +1,99 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler.support;
import org.jasig.cas.authentication.handler.AuthenticationHandler;
import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.HttpBasedServiceCredentials;
import org.jasig.cas.util.HttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.constraints.NotNull;
/**
* Class to validate the credentials presented by communicating with the web
* server and checking the certificate that is returned against the hostname,
* etc.
* <p>
* This class is concerned with ensuring that the protocol is HTTPS and that a
* response is returned. The SSL handshake that occurs automatically by opening
* a connection does the heavy process of authenticating.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
*/
public final class HttpBasedServiceCredentialsAuthenticationHandler implements AuthenticationHandler {
/** The string representing the HTTPS protocol. */
private static final String PROTOCOL_HTTPS = "https";
/** Boolean variable denoting whether secure connection is required or not. */
private boolean requireSecure = true;
/** Log instance. */
private final Logger log = LoggerFactory.getLogger(getClass());
/** Instance of Apache Commons HttpClient */
@NotNull
private HttpClient httpClient;
public boolean authenticate(final Credentials credentials) {
final HttpBasedServiceCredentials serviceCredentials = (HttpBasedServiceCredentials) credentials;
if (this.requireSecure
&& !serviceCredentials.getCallbackUrl().getProtocol().equals(
PROTOCOL_HTTPS)) {
if (log.isDebugEnabled()) {
log.debug("Authentication failed because url was not secure.");
}
return false;
}
log
.debug("Attempting to resolve credentials for "
+ serviceCredentials);
return this.httpClient.isValidEndPoint(serviceCredentials
.getCallbackUrl());
}
/**
* @return true if the credentials provided are not null and the credentials
* are a subclass of (or equal to) HttpBasedServiceCredentials.
*/
public boolean supports(final Credentials credentials) {
return credentials != null
&& HttpBasedServiceCredentials.class.isAssignableFrom(credentials
.getClass());
}
/** Sets the HttpClient which will do all of the connection stuff. */
public void setHttpClient(final HttpClient httpClient) {
this.httpClient = httpClient;
}
/**
* Set whether a secure url is required or not.
*
* @param requireSecure true if its required, false if not. Default is true.
*/
public void setRequireSecure(final boolean requireSecure) {
this.requireSecure = requireSecure;
}
}

View File

@ -0,0 +1,151 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler.support;
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.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.validation.constraints.NotNull;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
import org.springframework.util.Assert;
/**
* JAAS Authentication Handler for CAAS. This is a simple bridge from CAS'
* authentication to JAAS.
* <p>
* Using the JAAS Authentication Handler requires you to configure the
* appropriate JAAS modules. You can specify the location of a jass.conf file
* using the VM parameter
* -Djava.security.auth.login.config=$PATH_TO_JAAS_CONF/jaas.conf.
* <p>
* This example jaas.conf would try Kerberos based authentication, then try LDAP
* authentication CAS { com.sun.security.auth.module.Krb5LoginModule sufficient
* client=TRUE debug=FALSE useTicketCache=FALSE;
* edu.uconn.netid.jaas.LDAPLoginModule sufficient<br />
* java.naming.provider.url="ldap://ldapserver.my.edu:389/dc=my,dc=edu"<br />
* java.naming.security.principal="uid=jaasauth,dc=my,dc=edu"<br />
* java.naming.security.credentials="password" Attribute="uid" startTLS="true"; };<br />
*
* @author <a href="mailto:dotmatt@uconn.edu">Matthew J. Smith</a>
* @version $Revision$ $Date$
* @since 3.0.5
* @see javax.security.auth.callback.CallbackHandler
* @see javax.security.auth.callback.PasswordCallback
* @see javax.security.auth.callback.NameCallback
*/
public class JaasAuthenticationHandler extends
AbstractUsernamePasswordAuthenticationHandler {
/** If no realm is specified, we default to CAS. */
private static final String DEFAULT_REALM = "CAS";
/** The realm that contains the login module information. */
@NotNull
private String realm = DEFAULT_REALM;
public JaasAuthenticationHandler() {
Assert.notNull(Configuration.getConfiguration(), "Static Configuration cannot be null. Did you remember to specify \"java.security.auth.login.config\"?");
}
protected final boolean authenticateUsernamePasswordInternal(
final UsernamePasswordCredentials credentials)
throws AuthenticationException {
final String transformedUsername = getPrincipalNameTransformer().transform(credentials.getUsername());
try {
if (log.isDebugEnabled()) {
log.debug("Attempting authentication for: "
+ transformedUsername);
}
final LoginContext lc = new LoginContext(this.realm,
new UsernamePasswordCallbackHandler(transformedUsername,
credentials.getPassword()));
lc.login();
lc.logout();
} catch (final LoginException fle) {
if (log.isDebugEnabled()) {
log.debug("Authentication failed for: "
+ transformedUsername);
}
return false;
}
if (log.isDebugEnabled()) {
log.debug("Authentication succeeded for: "
+ transformedUsername);
}
return true;
}
public void setRealm(final String realm) {
this.realm = realm;
}
/**
* A simple JAAS CallbackHandler which accepts a Name String and Password
* String in the constructor. Only NameCallbacks and PasswordCallbacks are
* accepted in the callback array. This code based loosely on example given
* in Sun's javadoc for CallbackHandler interface.
*/
protected static final class UsernamePasswordCallbackHandler implements CallbackHandler {
/** The username of the principal we are trying to authenticate. */
private final String userName;
/** The password of the principal we are trying to authenticate. */
private final String password;
/**
* Constuctor accepts name and password to be used for authentication.
*
* @param userName name to be used for authentication
* @param password Password to be used for authentication
*/
protected UsernamePasswordCallbackHandler(final String userName,
final String password) {
this.userName = userName;
this.password = password;
}
public void handle(final Callback[] callbacks)
throws UnsupportedCallbackException {
for (final Callback callback : callbacks ) {
if (callback.getClass().equals(NameCallback.class)) {
((NameCallback) callback).setName(this.userName);
} else if (callback.getClass().equals(PasswordCallback.class)) {
((PasswordCallback) callback).setPassword(this.password
.toCharArray());
} else {
throw new UnsupportedCallbackException(callback,
"Unrecognized Callback");
}
}
}
}
}

View File

@ -0,0 +1,59 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.handler.support;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
import org.springframework.util.StringUtils;
/**
* Simple test implementation of a AuthenticationHandler that returns true if
* the username and password match. This class should never be enabled in a
* production environment and is only designed to facilitate unit testing and
* load testing.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
*/
public final class SimpleTestUsernamePasswordAuthenticationHandler extends
AbstractUsernamePasswordAuthenticationHandler {
public SimpleTestUsernamePasswordAuthenticationHandler() {
log
.warn(this.getClass().getName()
+ " is only to be used in a testing environment. NEVER enable this in a production environment.");
}
public boolean authenticateUsernamePasswordInternal(final UsernamePasswordCredentials credentials) {
final String username = credentials.getUsername();
final String password = credentials.getPassword();
if (StringUtils.hasText(username) && StringUtils.hasText(password)
&& username.equals(getPasswordEncoder().encode(password))) {
log
.debug("User [" + username
+ "] was successfully authenticated.");
return true;
}
log.debug("User [" + username + "] failed authentication");
return false;
}
}

View File

@ -0,0 +1,32 @@
<!--
Licensed to Jasig under one or more contributor license
agreements. See the NOTICE file distributed with this work
for additional information regarding copyright ownership.
Jasig licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a
copy of the License at the following location:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html>
<body>
<p>
Authentication.support contains the specific implementations of
the AuthenticationHandler interface. These implementations are designed
to authenticate a specific type of Credential.
<p>
AuthenticationHandlers are normally associated with the provided
AuthenticationManagerImpl which handles all aspects of the request for
Authentication.
</body>
</html>

View File

@ -0,0 +1,39 @@
<!--
Licensed to Jasig under one or more contributor license
agreements. See the NOTICE file distributed with this work
for additional information regarding copyright ownership.
Jasig licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a
copy of the License at the following location:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html>
<body>
<p>Authentication validates the Credentials provided during a /login
request. In this context, "Credentials" are an opaque object declared
with the Credentials marker interface. The AuthenticationManager
typically passes the Credentials to a sequence of plug-in elements
to see if any of them can recognize and process the concrete implementing
type.</p>
<p>Successful authentication generates a Principal object wrapped in an
Authentication object. All these objects must be serializable, and the
Authentication becomes part of the TGT in the ticket cache.</p>
<p>Unsucessful authentication must throw an AuthenticationException. The
AuthenticationManager may not return null to signal a failure.</p>
</body>
</html>

View File

@ -0,0 +1,110 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jasig.services.persondir.IPersonAttributeDao;
import org.jasig.services.persondir.IPersonAttributes;
import org.jasig.services.persondir.support.StubPersonAttributeDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.validation.constraints.NotNull;
/**
*
* @author Scott Battaglia
* @version $Revision: 1.1 $ $Date: 2005/08/19 18:27:17 $
* @since 3.1
*
*/
public abstract class AbstractPersonDirectoryCredentialsToPrincipalResolver
implements CredentialsToPrincipalResolver {
/** Log instance. */
protected final Logger log = LoggerFactory.getLogger(this.getClass());
private boolean returnNullIfNoAttributes = false;
/** Repository of principal attributes to be retrieved */
@NotNull
private IPersonAttributeDao attributeRepository = new StubPersonAttributeDao(new HashMap<String, List<Object>>());
public final Principal resolvePrincipal(final Credentials credentials) {
if (log.isDebugEnabled()) {
log.debug("Attempting to resolve a principal...");
}
final String principalId = extractPrincipalId(credentials);
if (principalId == null) {
return null;
}
if (log.isDebugEnabled()) {
log.debug("Creating SimplePrincipal for ["
+ principalId + "]");
}
final IPersonAttributes personAttributes = this.attributeRepository.getPerson(principalId);
final Map<String, List<Object>> attributes;
if (personAttributes == null) {
attributes = null;
} else {
attributes = personAttributes.getAttributes();
}
if (attributes == null & !this.returnNullIfNoAttributes) {
return new SimplePrincipal(principalId);
}
if (attributes == null) {
return null;
}
final Map<String, Object> convertedAttributes = new HashMap<String, Object>();
for (final Map.Entry<String, List<Object>> entry : attributes.entrySet()) {
final String key = entry.getKey();
final Object value = entry.getValue().size() == 1 ? entry.getValue().get(0) : entry.getValue();
convertedAttributes.put(key, value);
}
return new SimplePrincipal(principalId, convertedAttributes);
}
/**
* Extracts the id of the user from the provided credentials.
*
* @param credentials the credentials provided by the user.
* @return the username, or null if it could not be resolved.
*/
protected abstract String extractPrincipalId(Credentials credentials);
public final void setAttributeRepository(final IPersonAttributeDao attributeRepository) {
this.attributeRepository = attributeRepository;
}
public void setReturnNullIfNoAttributes(final boolean returnNullIfNoAttributes) {
this.returnNullIfNoAttributes = returnNullIfNoAttributes;
}
}

View File

@ -0,0 +1,169 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.jasig.cas.util.DefaultUniqueTicketIdGenerator;
import org.jasig.cas.util.HttpClient;
import org.jasig.cas.util.SamlUtils;
import org.jasig.cas.util.UniqueTicketIdGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract implementation of a WebApplicationService.
*
* @author Scott Battaglia
* @version $Revision: 1.3 $ $Date: 2007/04/19 20:13:01 $
* @since 3.1
*
*/
public abstract class AbstractWebApplicationService implements WebApplicationService {
protected static final Logger LOG = LoggerFactory.getLogger(SamlService.class);
private static final Map<String, Object> EMPTY_MAP = Collections.unmodifiableMap(new HashMap<String, Object>());
private static final UniqueTicketIdGenerator GENERATOR = new DefaultUniqueTicketIdGenerator();
/** The id of the service. */
private final String id;
/** The original url provided, used to reconstruct the redirect url. */
private final String originalUrl;
private final String artifactId;
private Principal principal;
private boolean loggedOutAlready = false;
private final HttpClient httpClient;
protected AbstractWebApplicationService(final String id, final String originalUrl, final String artifactId, final HttpClient httpClient) {
this.id = id;
this.originalUrl = originalUrl;
this.artifactId = artifactId;
this.httpClient = httpClient;
}
public final String toString() {
return this.id;
}
public final String getId() {
return this.id;
}
public final String getArtifactId() {
return this.artifactId;
}
public final Map<String, Object> getAttributes() {
return EMPTY_MAP;
}
protected static String cleanupUrl(final String url) {
if (url == null) {
return null;
}
final int jsessionPosition = url.indexOf(";jsession");
if (jsessionPosition == -1) {
return url;
}
final int questionMarkPosition = url.indexOf("?");
if (questionMarkPosition < jsessionPosition) {
return url.substring(0, url.indexOf(";jsession"));
}
return url.substring(0, jsessionPosition)
+ url.substring(questionMarkPosition);
}
protected final String getOriginalUrl() {
return this.originalUrl;
}
protected final HttpClient getHttpClient() {
return this.httpClient;
}
public boolean equals(final Object object) {
if (object == null) {
return false;
}
if (object instanceof Service) {
final Service service = (Service) object;
return getId().equals(service.getId());
}
return false;
}
public int hashCode() {
final int prime = 41;
int result = 1;
result = prime * result
+ ((this.id == null) ? 0 : this.id.hashCode());
return result;
}
protected Principal getPrincipal() {
return this.principal;
}
public void setPrincipal(final Principal principal) {
this.principal = principal;
}
public boolean matches(final Service service) {
return this.id.equals(service.getId());
}
public synchronized boolean logOutOfService(final String sessionIdentifier) {
if (this.loggedOutAlready) {
return true;
}
LOG.debug("Sending logout request for: " + getId());
final String logoutRequest = "<samlp:LogoutRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\""
+ GENERATOR.getNewTicketId("LR")
+ "\" Version=\"2.0\" IssueInstant=\"" + SamlUtils.getCurrentDateAndTime()
+ "\"><saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">@NOT_USED@</saml:NameID><samlp:SessionIndex>"
+ sessionIdentifier + "</samlp:SessionIndex></samlp:LogoutRequest>";
this.loggedOutAlready = true;
if (this.httpClient != null) {
return this.httpClient.sendMessageToEndPoint(getOriginalUrl(), logoutRequest, true);
}
return false;
}
}

View File

@ -0,0 +1,51 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import java.io.Serializable;
/**
* Marker interface for credentials required to authenticate a principal.
* <p>
* The Credentials is an opaque object that represents the information a user
* asserts proves that the user is who it says it is. In CAS, any information
* that is to be presented for authentication must be wrapped (or implement) the
* Credentials interface. Credentials can contain a userid and password, or a
* Certificate, or an IP address, or a cookie value. Some credentials require
* validation, while others (such as container based or Filter based validation)
* are inherently trustworthy.
* <p>
* People who choose to implement their own Credentials object should take care that
* any toString() they implement does not accidentally expose confidential information.
* toString() can be called from various portions of the CAS code base, including logging
* statements, and thus toString should never contain anything confidential or anything
* that should not be logged.
* <p>
* Credentials objects that are included in CAS do NOT expose any confidential information.
*
* @author William G. Thompson, Jr.
* @version $Revision: 1.2 $ $Date: 2007/01/22 20:35:26 $
* @since 3.0
* <p>
* This is a published and supported CAS Server 3 API.
* </p>
*/
public interface Credentials extends Serializable {
// marker interface contains no methods
}

View File

@ -0,0 +1,75 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
/**
* CredentialsToPrincipalResolvers extract information from the Credentials
* provided and determine the Principal represented by those credentials.
* <p>
* A minimal Principal object just has one ID value. This can be extended with
* richer objects containing more properties. The SimplePrincipal class
* implementing this interface just stores a userid.
* </p>
* <p>
* The Credentials typically contains a userid typed by the user or a
* Certificate presented by the browser. In the simplest case the userid is
* stored as the Principal ID. The Certificate is a more complicated case
* because the ID may have to be extracted from the Subject DN or from one of
* the alternate subject names. In a few cases, the institution may prefer the
* ID to be a student or employee ID number that can only be obtained by
* database lookup using information supplied in the Credentials.
* </p>
* <p>
* The Resolver is free to obtain additional information about the user and
* place it in the fields of a class that extends Principal. Such extended
* information will be stored like other Principal objects in the TGT, persisted
* as needed, and will be available to the View layer, but it is transparent to
* most CAS processing.
* </p>
*
* @author Scott Battaglia
* @version $Revision: 1.2 $ $Date: 2007/01/22 20:35:27 $
* @since 3.0
* <p>
* This is a published and supported CAS Server 3 API.
* </p>
* @see org.jasig.cas.authentication.principal.Principal
* @see org.jasig.cas.authentication.principal.Credentials
*/
public interface CredentialsToPrincipalResolver {
/**
* Turn Credentials into a Principal object by analyzing the information
* provided in the Credentials and constructing a Principal object based on
* that information or information derived from the Credentials object.
*
* @param credentials from which to resolve Principal
* @return resolved Principal, or null if the principal could not be resolved.
*/
Principal resolvePrincipal(Credentials credentials);
/**
* Determine if a credentials type is supported by this resolver. This is
* checked before calling resolve principal.
*
* @param credentials The credentials to check if we support.
* @return true if we support these credentials, false otherwise.
*/
boolean supports(Credentials credentials);
}

View File

@ -0,0 +1,304 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import org.jasig.cas.util.SamlUtils;
import org.jdom.Document;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import org.apache.commons.codec.binary.Base64;
/**
* Implementation of a Service that supports Google Accounts (eventually a more
* generic SAML2 support will come).
*
* @author Scott Battaglia
* @version $Revision: 1.1 $ $Date: 2005/08/19 18:27:17 $
* @since 3.1
*/
public class GoogleAccountsService extends AbstractWebApplicationService {
/**
* Comment for <code>serialVersionUID</code>
*/
private static final long serialVersionUID = 6678711809842282833L;
private static Random random = new Random();
private static final char[] charMapping = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p'};
private static final String CONST_PARAM_SERVICE = "SAMLRequest";
private static final String CONST_RELAY_STATE = "RelayState";
private static final String TEMPLATE_SAML_RESPONSE = "<samlp:Response ID=\"<RESPONSE_ID>\" IssueInstant=\"<ISSUE_INSTANT>\" Version=\"2.0\""
+ " xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\""
+ " xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\""
+ " xmlns:xenc=\"http://www.w3.org/2001/04/xmlenc#\">"
+ "<samlp:Status>"
+ "<samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\" />"
+ "</samlp:Status>"
+ "<Assertion ID=\"<ASSERTION_ID>\""
+ " IssueInstant=\"2003-04-17T00:46:02Z\" Version=\"2.0\""
+ " xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\">"
+ "<Issuer>https://www.opensaml.org/IDP</Issuer>"
+ "<Subject>"
+ "<NameID Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress\">"
+ "<USERNAME_STRING>"
+ "</NameID>"
+ "<SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\">"
+ "<SubjectConfirmationData Recipient=\"<ACS_URL>\" NotOnOrAfter=\"<NOT_ON_OR_AFTER>\" InResponseTo=\"<REQUEST_ID>\" />"
+ "</SubjectConfirmation>"
+ "</Subject>"
+ "<Conditions NotBefore=\"2003-04-17T00:46:02Z\""
+ " NotOnOrAfter=\"<NOT_ON_OR_AFTER>\">"
+ "<AudienceRestriction>"
+ "<Audience><ACS_URL></Audience>"
+ "</AudienceRestriction>"
+ "</Conditions>"
+ "<AuthnStatement AuthnInstant=\"<AUTHN_INSTANT>\">"
+ "<AuthnContext>"
+ "<AuthnContextClassRef>"
+ "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
+ "</AuthnContextClassRef>"
+ "</AuthnContext>"
+ "</AuthnStatement>"
+ "</Assertion></samlp:Response>";
private final String relayState;
private final PublicKey publicKey;
private final PrivateKey privateKey;
private final String requestId;
private final String alternateUserName;
protected GoogleAccountsService(final String id, final String relayState, final String requestId,
final PrivateKey privateKey, final PublicKey publicKey, final String alternateUserName) {
this(id, id, null, relayState, requestId, privateKey, publicKey, alternateUserName);
}
protected GoogleAccountsService(final String id, final String originalUrl,
final String artifactId, final String relayState, final String requestId,
final PrivateKey privateKey, final PublicKey publicKey, final String alternateUserName) {
super(id, originalUrl, artifactId, null);
this.relayState = relayState;
this.privateKey = privateKey;
this.publicKey = publicKey;
this.requestId = requestId;
this.alternateUserName = alternateUserName;
}
public static GoogleAccountsService createServiceFrom(
final HttpServletRequest request, final PrivateKey privateKey,
final PublicKey publicKey, final String alternateUserName) {
final String relayState = request.getParameter(CONST_RELAY_STATE);
final String xmlRequest = decodeAuthnRequestXML(request
.getParameter(CONST_PARAM_SERVICE));
if (!StringUtils.hasText(xmlRequest)) {
return null;
}
final Document document = SamlUtils
.constructDocumentFromXmlString(xmlRequest);
if (document == null) {
return null;
}
final String assertionConsumerServiceUrl = document.getRootElement().getAttributeValue("AssertionConsumerServiceURL");
final String requestId = document.getRootElement().getAttributeValue("ID");
return new GoogleAccountsService(assertionConsumerServiceUrl,
relayState, requestId, privateKey, publicKey, alternateUserName);
}
public Response getResponse(final String ticketId) {
final Map<String, String> parameters = new HashMap<String, String>();
final String samlResponse = constructSamlResponse();
final String signedResponse = SamlUtils.signSamlResponse(samlResponse,
this.privateKey, this.publicKey);
parameters.put("SAMLResponse", signedResponse);
parameters.put("RelayState", this.relayState);
return Response.getPostResponse(getOriginalUrl(), parameters);
}
/**
* Service does not support Single Log Out
*
* @see org.jasig.cas.authentication.principal.WebApplicationService#logOutOfService(java.lang.String)
*/
public boolean logOutOfService(final String sessionIdentifier) {
return false;
}
private String constructSamlResponse() {
String samlResponse = TEMPLATE_SAML_RESPONSE;
final Calendar c = Calendar.getInstance();
c.setTime(new Date());
c.add(Calendar.YEAR, 1);
final String userId;
if (this.alternateUserName == null) {
userId = getPrincipal().getId();
} else {
final String attributeValue = (String) getPrincipal().getAttributes().get(this.alternateUserName);
if (attributeValue == null) {
userId = getPrincipal().getId();
} else {
userId = attributeValue;
}
}
samlResponse = samlResponse.replace("<USERNAME_STRING>", userId);
samlResponse = samlResponse.replace("<RESPONSE_ID>", createID());
samlResponse = samlResponse.replace("<ISSUE_INSTANT>", SamlUtils
.getCurrentDateAndTime());
samlResponse = samlResponse.replace("<AUTHN_INSTANT>", SamlUtils
.getCurrentDateAndTime());
samlResponse = samlResponse.replaceAll("<NOT_ON_OR_AFTER>", SamlUtils
.getFormattedDateAndTime(c.getTime()));
samlResponse = samlResponse.replace("<ASSERTION_ID>", createID());
samlResponse = samlResponse.replaceAll("<ACS_URL>", getId());
samlResponse = samlResponse.replace("<REQUEST_ID>", this.requestId);
return samlResponse;
}
private static String createID() {
final byte[] bytes = new byte[20]; // 160 bits
random.nextBytes(bytes);
final char[] chars = new char[40];
for (int i = 0; i < bytes.length; i++) {
int left = (bytes[i] >> 4) & 0x0f;
int right = bytes[i] & 0x0f;
chars[i * 2] = charMapping[left];
chars[i * 2 + 1] = charMapping[right];
}
return String.valueOf(chars);
}
private static String decodeAuthnRequestXML(
final String encodedRequestXmlString) {
if (encodedRequestXmlString == null) {
return null;
}
final byte[] decodedBytes = base64Decode(encodedRequestXmlString);
if (decodedBytes == null) {
return null;
}
final String inflated = inflate(decodedBytes);
if (inflated != null) {
return inflated;
}
return zlibDeflate(decodedBytes);
}
private static String zlibDeflate(final byte[] bytes) {
final ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final InflaterInputStream iis = new InflaterInputStream(bais);
final byte[] buf = new byte[1024];
try {
int count = iis.read(buf);
while (count != -1) {
baos.write(buf, 0, count);
count = iis.read(buf);
}
return new String(baos.toByteArray());
} catch (final Exception e) {
return null;
} finally {
try {
iis.close();
} catch (final Exception e) {
// nothing to do
}
}
}
private static byte[] base64Decode(final String xml) {
try {
final byte[] xmlBytes = xml.getBytes("UTF-8");
return Base64.decodeBase64(xmlBytes);
} catch (final Exception e) {
return null;
}
}
private static String inflate(final byte[] bytes) {
final Inflater inflater = new Inflater(true);
final byte[] xmlMessageBytes = new byte[10000];
final byte[] extendedBytes = new byte[bytes.length + 1];
System.arraycopy(bytes, 0, extendedBytes, 0, bytes.length);
extendedBytes[bytes.length] = 0;
inflater.setInput(extendedBytes);
try {
final int resultLength = inflater.inflate(xmlMessageBytes);
inflater.end();
if (!inflater.finished()) {
throw new RuntimeException("buffer not large enough.");
}
inflater.end();
return new String(xmlMessageBytes, 0, resultLength, "UTF-8");
} catch (final DataFormatException e) {
return null;
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException("Cannot find encoding: UTF-8", e);
}
}
}

View File

@ -0,0 +1,101 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import java.net.URL;
import org.springframework.util.Assert;
/**
* The Credentials representing an HTTP-based service. HTTP-based services (such
* as web applications) are often represented by the URL entry point of the
* application.
*
* @author Scott Battaglia
* @version $Revision: 1.3 $ $Date: 2007/04/24 13:01:51 $
* @since 3.0
*/
public class HttpBasedServiceCredentials implements Credentials {
/** Unique Serializable ID. */
private static final long serialVersionUID = 3904681574350991665L;
/** The callbackURL to check that identifies the application. */
private final URL callbackUrl;
/** String form of callbackUrl; */
private final String callbackUrlAsString;
/**
* Constructor that takes the URL of the HTTP-based service and creates the
* Credentials object. Caches the value of URL.toExternalForm so updates to
* the URL will not be reflected in a call to toString().
*
* @param callbackUrl the URL representing the service
* @throws IllegalArgumentException if the callbackUrl is null.
*/
public HttpBasedServiceCredentials(final URL callbackUrl) {
Assert.notNull(callbackUrl, "callbackUrl cannot be null");
this.callbackUrl = callbackUrl;
this.callbackUrlAsString = callbackUrl.toExternalForm();
}
/**
* @return Returns the callbackUrl.
*/
public final URL getCallbackUrl() {
return this.callbackUrl;
}
/**
* Returns the String version of the URL, based on the original URL
* provided. i.e. this caches the value of URL.toExternalForm()
*/
public final String toString() {
return "[callbackUrl: " + this.callbackUrlAsString + "]";
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime
* result
+ ((this.callbackUrlAsString == null) ? 0 : this.callbackUrlAsString
.hashCode());
return result;
}
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final HttpBasedServiceCredentials other = (HttpBasedServiceCredentials) obj;
if (this.callbackUrlAsString == null) {
if (other.callbackUrlAsString != null)
return false;
} else if (!this.callbackUrlAsString.equals(other.callbackUrlAsString))
return false;
return true;
}
}

View File

@ -0,0 +1,51 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
/**
* HttpBasedServiceCredentialsToPrincipalResolver extracts the callbackUrl from
* the HttpBasedServiceCredentials and constructs a SimpleService with the
* callbackUrl as the unique Id.
*
* @author Scott Battaglia
* @version $Revision: 1.5 $ $Date: 2007/02/27 19:31:58 $
* @since 3.0
*/
public final class HttpBasedServiceCredentialsToPrincipalResolver implements
CredentialsToPrincipalResolver {
/**
* Method to return a simple Service Principal with the identifier set to be
* the callback url.
*/
public Principal resolvePrincipal(final Credentials credentials) {
final HttpBasedServiceCredentials serviceCredentials = (HttpBasedServiceCredentials) credentials;
return new SimpleWebApplicationServiceImpl(serviceCredentials.getCallbackUrl().toExternalForm());
}
/**
* @return true if the credentials provided are not null and are assignable
* from HttpBasedServiceCredentials, otherwise returns false.
*/
public boolean supports(final Credentials credentials) {
return credentials != null
&& HttpBasedServiceCredentials.class.isAssignableFrom(credentials
.getClass());
}
}

View File

@ -0,0 +1,40 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
/**
* Generates a unique consistant Id based on the principal, a service, and some
* algorithm.
*
* @author Scott Battaglia
* @version $Revision: 1.1 $ $Date: 2007/04/20 19:39:31 $
* @since 3.1
*/
public interface PersistentIdGenerator {
/**
* Generates a PersistentId based on some algorithm plus the principal and
* service.
*
* @param principal the principal to generate the id for.
* @param service the service to generate the id for.
* @return the generated persistent id.
*/
String generate(Principal principal, Service service);
}

View File

@ -0,0 +1,54 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at 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.authentication.principal;
import java.io.Serializable;
import java.util.Map;
/**
* Generic concept of an authenticated thing. Examples include a person or a
* service.
* <p>
* The implementation SimplePrincipal just contains the Id property. More
* complex Principal objects may contain additional information that are
* meaningful to the View layer but are generally transparent to the rest of
* CAS.
* </p>
*
* @author Scott Battaglia
* @version $Revision: 1.3 $ $Date: 2007/04/19 20:13:01 $
* @since 3.0
* <p>
* This is a published and supported CAS Server 3 API.
* </p>
*/
public interface Principal extends Serializable {
/**
* Returns the unique id for the Principal
* @return the unique id for the Principal.
*/
String getId();
/**
*
* @return
*/
Map<String, Object> getAttributes();
}

View File

@ -0,0 +1,47 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import org.jasig.cas.authentication.Authentication;
import org.jasig.cas.authentication.AuthenticationMetaDataPopulator;
/**
* Determines if the credentials provided are for Remember Me Services and then sets the appropriate
* Authentication attribute if remember me services have been requested.
*
* @author Scott Battaglia
* @version $Revision: 1.1 $ $Date: 2005/08/19 18:27:17 $
* @since 3.2.1
*
*/
public final class RememberMeAuthenticationMetaDataPopulator implements
AuthenticationMetaDataPopulator {
public Authentication populateAttributes(final Authentication authentication,
final Credentials credentials) {
if (credentials instanceof RememberMeCredentials) {
final RememberMeCredentials r = (RememberMeCredentials) credentials;
if (r.isRememberMe()) {
authentication.getAttributes().put(RememberMeCredentials.AUTHENTICATION_ATTRIBUTE_REMEMBER_ME, Boolean.TRUE);
}
}
return authentication;
}
}

View File

@ -0,0 +1,39 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
/**
* Credentials that wish to handle remember me scenarios need
* to implement this class.
*
* @author Scott Battaglia
* @version $Revision: 1.1 $ $Date: 2005/08/19 18:27:17 $
* @since 3.2.1
*
*/
public interface RememberMeCredentials extends Credentials {
String AUTHENTICATION_ATTRIBUTE_REMEMBER_ME = "org.jasig.cas.authentication.principal.REMEMBER_ME";
String REQUEST_PARAMETER_REMEMBER_ME = "rememberMe";
boolean isRememberMe();
void setRememberMe(boolean rememberMe);
}

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 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.authentication.principal;
/**
* Handles both remember me services and username and password.
*
* @author Scott Battaglia
* @version $Revision: 1.1 $ $Date: 2005/08/19 18:27:17 $
* @since 3.2.1
*
*/
public class RememberMeUsernamePasswordCredentials extends
UsernamePasswordCredentials implements RememberMeCredentials {
/** Unique Id for serialization. */
private static final long serialVersionUID = -9178853167397038282L;
private boolean rememberMe;
public final boolean isRememberMe() {
return this.rememberMe;
}
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + (this.rememberMe ? 1231 : 1237);
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
final RememberMeUsernamePasswordCredentials other = (RememberMeUsernamePasswordCredentials) obj;
if (this.rememberMe != other.rememberMe)
return false;
return true;
}
public final void setRememberMe(boolean rememberMe) {
this.rememberMe = rememberMe;
}
}

View File

@ -0,0 +1,135 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import java.net.URLEncoder;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Encapsulates a Response to send back for a particular service.
*
* @author Scott Battaglia
* @author Arnaud Lesueur
* @version $Revision: 1.1 $ $Date: 2005/08/19 18:27:17 $
* @since 3.1
*/
public final class Response {
/** Pattern to detect unprintable ASCII characters. */
private static final Pattern NON_PRINTABLE =
Pattern.compile("[\\x00-\\x19\\x7F]+");
/** Log instance. */
protected static final Logger LOG = LoggerFactory.getLogger(Response.class);
public static enum ResponseType {
POST, REDIRECT
}
private final ResponseType responseType;
private final String url;
private final Map<String, String> attributes;
protected Response(ResponseType responseType, final String url, final Map<String, String> attributes) {
this.responseType = responseType;
this.url = url;
this.attributes = attributes;
}
public static Response getPostResponse(final String url, final Map<String, String> attributes) {
return new Response(ResponseType.POST, url, attributes);
}
public static Response getRedirectResponse(final String url, final Map<String, String> parameters) {
final StringBuilder builder = new StringBuilder(parameters.size() * 40 + 100);
boolean isFirst = true;
final String[] fragmentSplit = sanitizeUrl(url).split("#");
builder.append(fragmentSplit[0]);
for (final Map.Entry<String, String> entry : parameters.entrySet()) {
if (entry.getValue() != null) {
if (isFirst) {
builder.append(url.contains("?") ? "&" : "?");
isFirst = false;
} else {
builder.append("&");
}
builder.append(entry.getKey());
builder.append("=");
try {
builder.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
} catch (final Exception e) {
builder.append(entry.getValue());
}
}
}
if (fragmentSplit.length > 1) {
builder.append("#");
builder.append(fragmentSplit[1]);
}
return new Response(ResponseType.REDIRECT, builder.toString(), parameters);
}
public Map<String, String> getAttributes() {
return this.attributes;
}
public ResponseType getResponseType() {
return this.responseType;
}
public String getUrl() {
return this.url;
}
/**
* Sanitize a URL provided by a relying party by normalizing non-printable
* ASCII character sequences into spaces. This functionality protects
* against CRLF attacks and other similar attacks using invisible characters
* that could be abused to trick user agents.
*
* @param url URL to sanitize.
*
* @return Sanitized URL string.
*/
private static String sanitizeUrl(final String url) {
final Matcher m = NON_PRINTABLE.matcher(url);
final StringBuffer sb = new StringBuffer(url.length());
boolean hasNonPrintable = false;
while (m.find()) {
m.appendReplacement(sb, " ");
hasNonPrintable = true;
}
m.appendTail(sb);
if (hasNonPrintable) {
LOG.warn("The following redirect URL has been sanitized and may be sign of attack:\n" + url);
}
return sb.toString();
}
}

View File

@ -0,0 +1,172 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import java.io.BufferedReader;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.cas.util.HttpClient;
import org.springframework.util.StringUtils;
/**
* Class to represent that this service wants to use SAML. We use this in
* combination with the CentralAuthenticationServiceImpl to choose the right
* UniqueTicketIdGenerator.
*
* @author Scott Battaglia
* @version $Revision: 1.6 $ $Date: 2007/02/27 19:31:58 $
* @since 3.1
*/
public final class SamlService extends AbstractWebApplicationService {
private static final Log log = LogFactory.getLog(SamlService.class);
/** Constant representing service. */
private static final String CONST_PARAM_SERVICE = "TARGET";
/** Constant representing artifact. */
private static final String CONST_PARAM_TICKET = "SAMLart";
private static final String CONST_START_ARTIFACT_XML_TAG_NO_NAMESPACE = "<AssertionArtifact>";
private static final String CONST_END_ARTIFACT_XML_TAG_NO_NAMESPACE = "</AssertionArtifact>";
private static final String CONST_START_ARTIFACT_XML_TAG = "<samlp:AssertionArtifact>";
private static final String CONST_END_ARTIFACT_XML_TAG = "</samlp:AssertionArtifact>";
private String requestId;
/**
* Unique Id for serialization.
*/
private static final long serialVersionUID = -6867572626767140223L;
protected SamlService(final String id) {
super(id, id, null, new HttpClient());
}
protected SamlService(final String id, final String originalUrl, final String artifactId, final HttpClient httpClient, final String requestId) {
super(id, originalUrl, artifactId, httpClient);
this.requestId = requestId;
}
/**
* This always returns true because a SAML Service does not receive the TARGET value on validation.
*/
public boolean matches(final Service service) {
return true;
}
public String getRequestID() {
return this.requestId;
}
public static SamlService createServiceFrom(
final HttpServletRequest request, final HttpClient httpClient) {
final String service = request.getParameter(CONST_PARAM_SERVICE);
final String artifactId;
final String requestBody = getRequestBody(request);
final String requestId;
if (!StringUtils.hasText(service) && !StringUtils.hasText(requestBody)) {
return null;
}
final String id = cleanupUrl(service);
if (StringUtils.hasText(requestBody)) {
final String tagStart;
final String tagEnd;
if (requestBody.contains(CONST_START_ARTIFACT_XML_TAG)) {
tagStart = CONST_START_ARTIFACT_XML_TAG;
tagEnd = CONST_END_ARTIFACT_XML_TAG;
} else {
tagStart = CONST_START_ARTIFACT_XML_TAG_NO_NAMESPACE;
tagEnd = CONST_END_ARTIFACT_XML_TAG_NO_NAMESPACE;
}
final int startTagLocation = requestBody.indexOf(tagStart);
final int artifactStartLocation = startTagLocation + tagStart.length();
final int endTagLocation = requestBody.indexOf(tagEnd);
artifactId = requestBody.substring(artifactStartLocation, endTagLocation).trim();
// is there a request id?
requestId = extractRequestId(requestBody);
} else {
artifactId = null;
requestId = null;
}
if (log.isDebugEnabled()) {
log.debug("Attempted to extract Request from HttpServletRequest. Results:");
log.debug(String.format("Request Body: %s", requestBody));
log.debug(String.format("Extracted ArtifactId: %s", artifactId));
log.debug(String.format("Extracted Request Id: %s", requestId));
}
return new SamlService(id, service, artifactId, httpClient, requestId);
}
public Response getResponse(final String ticketId) {
final Map<String, String> parameters = new HashMap<String, String>();
parameters.put(CONST_PARAM_TICKET, ticketId);
parameters.put(CONST_PARAM_SERVICE, getOriginalUrl());
return Response.getRedirectResponse(getOriginalUrl(), parameters);
}
protected static String extractRequestId(final String requestBody) {
if (!requestBody.contains("RequestID")) {
return null;
}
try {
final int position = requestBody.indexOf("RequestID=\"") + 11;
final int nextPosition = requestBody.indexOf("\"", position);
return requestBody.substring(position, nextPosition);
} catch (final Exception e) {
log.debug("Exception parsing RequestID from request." ,e);
return null;
}
}
protected static String getRequestBody(final HttpServletRequest request) {
final StringBuilder builder = new StringBuilder();
try {
final BufferedReader reader = request.getReader();
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
return builder.toString();
} catch (final Exception e) {
return null;
}
}
}

View File

@ -0,0 +1,41 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
/**
* Marker interface for Services. Services are generally either remote
* applications utilizing CAS or applications that principals wish to gain
* access to. In most cases this will be some form of web application.
*
* @author William G. Thompson, Jr.
* @author Scott Battaglia
* @version $Revision: 1.2 $ $Date: 2007/01/22 20:35:26 $
* @since 3.0
* <p>
* This is a published and supported CAS Server 3 API.
* </p>
*/
public interface Service extends Principal {
void setPrincipal(Principal principal);
boolean logOutOfService(String sessionIdentifier);
boolean matches(Service service);
}

View File

@ -0,0 +1,61 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Base64;
import javax.validation.constraints.NotNull;
/**
* Generates PersistentIds based on the Shibboleth algorithm.
*
* @author Scott Battaglia
* @version $Revision: 1.1 $ $Date: 2007/04/20 19:39:31 $
* @since 3.1
*/
public final class ShibbolethCompatiblePersistentIdGenerator implements
PersistentIdGenerator {
private static final byte CONST_SEPARATOR = (byte) '!';
@NotNull
private byte[] salt;
public String generate(final Principal principal, final Service service) {
try {
final MessageDigest md = MessageDigest.getInstance("SHA");
md.update(service.getId().getBytes());
md.update(CONST_SEPARATOR);
md.update(principal.getId().getBytes());
md.update(CONST_SEPARATOR);
return Base64.encodeBase64String(md.digest(this.salt)).replaceAll(
System.getProperty("line.separator"), "");
} catch (final NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public void setSalt(final String salt) {
this.salt = salt.getBytes();
}
}

View File

@ -0,0 +1,91 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.util.Assert;
/**
* Simple implementation of a AttributePrincipal that exposes an unmodifiable
* map of attributes.
*
* @author Scott Battaglia
* @version $Revision: 1.3 $ $Date: 2007/04/19 20:13:01 $
* @since 3.1
*/
public class SimplePrincipal implements Principal {
private static final Map<String, Object> EMPTY_MAP = Collections
.unmodifiableMap(new HashMap<String, Object>());
/**
* Unique Id for Serialization.
*/
private static final long serialVersionUID = -5265620187476296219L;
/** The unique identifier for the principal. */
private final String id;
/** Map of attributes for the Principal. */
private Map<String, Object> attributes;
public SimplePrincipal(final String id) {
this(id, null);
}
public SimplePrincipal(final String id, final Map<String, Object> attributes) {
Assert.notNull(id, "id cannot be null");
this.id = id;
this.attributes = attributes == null || attributes.isEmpty()
? EMPTY_MAP : Collections.unmodifiableMap(attributes);
}
/**
* Returns an immutable map.
*/
public Map<String, Object> getAttributes() {
return this.attributes;
}
public String toString() {
return this.id;
}
public int hashCode() {
return super.hashCode() ^ this.id.hashCode();
}
public final String getId() {
return this.id;
}
public boolean equals(final Object o) {
if (o == null || !this.getClass().equals(o.getClass())) {
return false;
}
final SimplePrincipal p = (SimplePrincipal) o;
return this.id.equals(p.getId());
}
}

View File

@ -0,0 +1,106 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.jasig.cas.authentication.principal.Response.ResponseType;
import org.jasig.cas.util.HttpClient;
import org.springframework.util.StringUtils;
/**
* Represents a service which wishes to use the CAS protocol.
*
* @author Scott Battaglia
* @version $Revision: 1.3 $ $Date: 2007/04/24 18:19:22 $
* @since 3.1
*/
public final class SimpleWebApplicationServiceImpl extends
AbstractWebApplicationService {
private static final String CONST_PARAM_SERVICE = "service";
private static final String CONST_PARAM_TARGET_SERVICE = "targetService";
private static final String CONST_PARAM_TICKET = "ticket";
private static final String CONST_PARAM_METHOD = "method";
private final ResponseType responseType;
/**
* Unique Id for Serialization
*/
private static final long serialVersionUID = 8334068957483758042L;
public SimpleWebApplicationServiceImpl(final String id) {
this(id, id, null, null, null);
}
public SimpleWebApplicationServiceImpl(final String id, final HttpClient httpClient) {
this(id, id, null, null, httpClient);
}
private SimpleWebApplicationServiceImpl(final String id,
final String originalUrl, final String artifactId,
final ResponseType responseType, final HttpClient httpClient) {
super(id, originalUrl, artifactId, httpClient);
this.responseType = responseType;
}
public static SimpleWebApplicationServiceImpl createServiceFrom(final HttpServletRequest request) {
return createServiceFrom(request, null);
}
public static SimpleWebApplicationServiceImpl createServiceFrom(
final HttpServletRequest request, final HttpClient httpClient) {
final String targetService = request
.getParameter(CONST_PARAM_TARGET_SERVICE);
final String method = request.getParameter(CONST_PARAM_METHOD);
final String serviceToUse = StringUtils.hasText(targetService)
? targetService : request.getParameter(CONST_PARAM_SERVICE);
if (!StringUtils.hasText(serviceToUse)) {
return null;
}
final String id = cleanupUrl(serviceToUse);
final String artifactId = request.getParameter(CONST_PARAM_TICKET);
return new SimpleWebApplicationServiceImpl(id, serviceToUse,
artifactId, "POST".equals(method) ? ResponseType.POST
: ResponseType.REDIRECT, httpClient);
}
public Response getResponse(final String ticketId) {
final Map<String, String> parameters = new HashMap<String, String>();
if (StringUtils.hasText(ticketId)) {
parameters.put(CONST_PARAM_TICKET, ticketId);
}
if (ResponseType.POST == this.responseType) {
return Response.getPostResponse(getOriginalUrl(), parameters);
}
return Response.getRedirectResponse(getOriginalUrl(), parameters);
}
}

View File

@ -0,0 +1,101 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
* UsernamePasswordCredentials respresents the username and password that a user
* may provide in order to prove the authenticity of who they say they are.
*
* @author Scott Battaglia
* @version $Revision: 1.2 $ $Date: 2007/01/22 20:35:26 $
* @since 3.0
* <p>
* This is a published and supported CAS Server 3 API.
* </p>
*/
public class UsernamePasswordCredentials implements Credentials {
/** Unique ID for serialization. */
private static final long serialVersionUID = -8343864967200862794L;
/** The username. */
@NotNull
@Size(min=1,message = "required.username")
private String username;
/** The password. */
@NotNull
@Size(min=1, message = "required.password")
private String password;
/**
* @return Returns the password.
*/
public final String getPassword() {
return this.password;
}
/**
* @param password The password to set.
*/
public final void setPassword(final String password) {
this.password = password;
}
/**
* @return Returns the userName.
*/
public final String getUsername() {
return this.username;
}
/**
* @param userName The userName to set.
*/
public final void setUsername(final String userName) {
this.username = userName;
}
public String toString() {
return "[username: " + this.username + "]";
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UsernamePasswordCredentials that = (UsernamePasswordCredentials) o;
if (password != null ? !password.equals(that.password) : that.password != null) return false;
if (username != null ? !username.equals(that.username) : that.username != null) return false;
return true;
}
@Override
public int hashCode() {
int result = username != null ? username.hashCode() : 0;
result = 31 * result + (password != null ? password.hashCode() : 0);
return result;
}
}

View File

@ -0,0 +1,52 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication.principal;
/**
* Implementation of CredentialsToPrincipalResolver for Credentials based on
* UsernamePasswordCredentials when a SimplePrincipal (username only) is
* sufficient.
* <p>
* Implementation extracts the username from the Credentials provided and
* constructs a new SimplePrincipal with the unique id set to the username.
* </p>
*
* @author Scott Battaglia
* @version $Revision: 1.2 $ $Date: 2007/01/22 20:35:26 $
* @since 3.0
* @see org.jasig.cas.authentication.principal.SimplePrincipal
*/
public final class UsernamePasswordCredentialsToPrincipalResolver extends
AbstractPersonDirectoryCredentialsToPrincipalResolver {
protected String extractPrincipalId(final Credentials credentials) {
final UsernamePasswordCredentials usernamePasswordCredentials = (UsernamePasswordCredentials) credentials;
return usernamePasswordCredentials.getUsername();
}
/**
* Return true if Credentials are UsernamePasswordCredentials, false
* otherwise.
*/
public boolean supports(final Credentials credentials) {
return credentials != null
&& UsernamePasswordCredentials.class.isAssignableFrom(credentials
.getClass());
}
}

View File

@ -0,0 +1,44 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at 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.authentication.principal;
/**
* Represents a service using CAS that comes from the web.
*
* @author Scott Battaglia
* @version $Revision: 1.3 $ $Date: 2007/02/27 19:31:58 $
* @since 3.1
*/
public interface WebApplicationService extends Service {
/**
* Constructs the url to redirect the service back to.
*
* @param ticketId the service ticket to provide to the service.
* @return the redirect url.
*/
Response getResponse(String ticketId);
/**
* Retrieves the artifact supplied with the service. May be null.
*
* @return the artifact if it exists, null otherwise.
*/
String getArtifactId();
}

View File

@ -0,0 +1,42 @@
<!--
Licensed to Jasig under one or more contributor license
agreements. See the NOTICE file distributed with this work
for additional information regarding copyright ownership.
Jasig licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a
copy of the License at the following location:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html>
<body>
<p>Credentials is a marker interface for an opaque object that may be recognized by
Handlers and Resolvers. Credentials may be a Userid/Password, Certificate,
RemoteUser, IP address, etc.</p>
<p>When the simple AuthenticationManagerImpl is
used, that bean is configured with a list of AuthenticationHandlers that
validate Credentials and CredentialsToPrincipalResolvers that turn Credentials
into Principal objects.</p>
<p>The Authentication Handler validates Credentials but does not extract
information. This seems curious in the simple case when the credentials are a
Userid/Password. It becomes clearer for a Certificate. A Certificate is valid if
you trust the CA, if it hasn't expired, and if it isn't revoked. You can decide
all this, and still not have the foggiest idea what ID to give to the person (if
it is a person) reprepsented by the Certificate.</p>
<p>The CredentialsToPrincipalResolver looks into previously validated
Credentials to construct a Principal object containing an ID (and in more
complex cases some attributes). The DefaultCredentialsToPrincipalResolver takes
UsernamePasswordCredentials and creates a SimplePrincipal containing the Userid.</p>
</body>
</html>

View File

@ -0,0 +1,109 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
/**
* Abstract base class for monitors that observe cache storage systems.
*
* @author Marvin S. Addison
* @since 3.5.1
*/
public abstract class AbstractCacheMonitor extends AbstractNamedMonitor<CacheStatus> {
/** Default free capacity threshold is 10%. */
public static final int DEFAULT_WARN_FREE_THRESHOLD = 10;
/** Default eviction threshold is 0. */
public static final long DEFAULT_EVICTION_THRESHOLD = 0;
/** Percent free capacity threshold below which a warning is issued.*/
private int warnFreeThreshold = DEFAULT_WARN_FREE_THRESHOLD;
/** Threshold for number of acceptable evictions above which an error is issued. */
private long evictionThreshold = DEFAULT_EVICTION_THRESHOLD;
/**
* Sets the percent free capacity threshold below which a warning is issued.
*
* @param percent Warning threshold percent.
*/
public void setWarnFreeThreshold(final int percent) {
this.warnFreeThreshold = percent;
}
/**
* Sets the eviction threshold count above which an error is issued.
*
* @param count Threshold for number of cache evictions.
*/
public void setEvictionThreshold(final long count) {
this.evictionThreshold = count;
}
public CacheStatus observe() {
CacheStatus status;
try {
final CacheStatistics[] statistics = getStatistics();
if (statistics == null || statistics.length == 0) {
return new CacheStatus(StatusCode.ERROR, "Cache statistics not available.");
}
StatusCode overall = StatusCode.OK;
StatusCode code;
for (final CacheStatistics stats : statistics) {
code = status(stats);
// Record highest status which is equivalent to worst case
if (code.value() > overall.value()) {
overall = code;
}
}
status = new CacheStatus(overall, null, statistics);
} catch (final Exception e) {
status = new CacheStatus(e);
}
return status;
}
protected abstract CacheStatistics[] getStatistics();
/**
* Computes the status code for a given set of cache statistics.
*
* @param statistics Cache statistics.
*
* @return {@link StatusCode#WARN} if eviction count is above threshold or if
* percent free space is below threshold, otherwise {@link StatusCode#OK}.
*/
protected StatusCode status(final CacheStatistics statistics) {
final StatusCode code;
if (statistics.getEvictions() > this.evictionThreshold) {
code = StatusCode.WARN;
} else if (statistics.getPercentFree() < this.warnFreeThreshold) {
code = StatusCode.WARN;
} else {
code = StatusCode.OK;
}
return code;
}
}

View File

@ -0,0 +1,49 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at 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.monitor;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.Assert;
/**
* Base class for all monitors that support configurable naming.
*
* @author Marvin S. Addison
* @since 3.5.0
*/
public abstract class AbstractNamedMonitor<S extends Status> implements Monitor<S> {
/** Monitor name. */
protected String name;
/**
* @return Monitor name.
*/
public String getName() {
return StringUtils.defaultIfEmpty(this.name, getClass().getSimpleName());
}
/**
* @param n Monitor name.
*/
public void setName(final String n) {
Assert.hasText(n, "Monitor name cannot be null or empty.");
this.name = n;
}
}

View File

@ -0,0 +1,122 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.validation.constraints.NotNull;
/**
* Describes a monitor that observes a pool of resources.
*
* @author Marvin S. Addison
* @since 3.5.0
*/
public abstract class AbstractPoolMonitor extends AbstractNamedMonitor<PoolStatus> {
/** Default maximum wait time for asynchronous pool validation. */
public static final int DEFAULT_MAX_WAIT = 3000;
/** Maximum amount of time in ms to wait while validating pool resources. */
private int maxWait = DEFAULT_MAX_WAIT;
/** Executor that performs pool resource validation. */
@NotNull
private ExecutorService executor;
/**
* Sets the executor service responsible for pool resource validation.
*
* @param executorService Executor of pool validation operations.
*/
public void setExecutor(final ExecutorService executorService) {
this.executor = executorService;
}
/**
* Set the maximum amount of time wait while validating pool resources.
* If the pool defines a minumum time to wait for a resource, this property
* should be set less than that value.
*
* @param time Wait time in milliseconds.
*/
public void setMaxWait(final int time) {
this.maxWait = time;
}
/** {@inheritDoc} */
public PoolStatus observe() {
final Future<StatusCode> result = this.executor.submit(new Validator());
StatusCode code;
String description = null;
try {
code = result.get(this.maxWait, TimeUnit.MILLISECONDS);
} catch (final InterruptedException e) {
code = StatusCode.UNKNOWN;
description = "Validator thread interrupted during pool validation.";
} catch (final TimeoutException e) {
code = StatusCode.WARN;
description = String.format("Pool validation timed out. Max wait is %s ms.", this.maxWait);
} catch (final Exception e) {
code = StatusCode.ERROR;
description = e.getMessage();
}
return new PoolStatus(code, description, getActiveCount(), getIdleCount());
}
/**
* Performs a health check on a the pool. The recommended implementation is to
* obtain a pool resource, validate it, and return it to the pool.
*
* @return Status code describing pool health.
*
* @throws Exception Thrown to indicate a serious problem with pool validation.
*/
protected abstract StatusCode checkPool() throws Exception;
/**
* Gets the number of pool resources idle at present.
*
* @return Number of idle pool resources.
*/
protected abstract int getIdleCount();
/**
* Gets the number of pool resources active at present.
*
* @return Number of active pool resources.
*/
protected abstract int getActiveCount();
private class Validator implements Callable<StatusCode> {
public StatusCode call() throws Exception {
return checkPool();
}
}
}

View File

@ -0,0 +1,76 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
/**
* Describes the simplest set of cache statistics that are meaningful for health monitoring.
*
* @author Marvin S. Addison
* @since 3.5.1
*/
public interface CacheStatistics {
/**
* Gets the current size of the cache in a unit specific to the cache being monitored (e.g. bytes, items, etc).
*
* @return Current cache size.
*/
long getSize();
/**
* Gets the current capacity of the cache in a unit specific to the cache being monitored (e.g. bytes, items, etc).
*
* @return Current cache capacity.
*/
long getCapacity();
/**
* Gets the number of items evicted from the cache in order to make space for new items.
*
* @return Eviction count.
*/
long getEvictions();
/**
* Gets the percent free capacity remaining in the cache.
*
* @return Percent of space/capacity free.
*/
int getPercentFree();
/**
* Gets a descriptive name of the cache instance for which statistics apply.
*
* @return Name of cache instance/host to which statistics apply.
*/
String getName();
/**
* Writes a string representation of cache statistics to the given string builder.
*
* @param builder String builder to which string representation is appended.
*/
void toString(StringBuilder builder);
}

View File

@ -0,0 +1,90 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
/**
* Describes meaningful health metrics on the status of a cache.
*
* @author Marvin S. Addison
* @since 3.5.1
*/
public class CacheStatus extends Status {
private final CacheStatistics[] statistics;
/**
* Creates a new instance describing cache status.
*
* @param code Status code.
* @param description Optional status description.
* @param statistics One or more sets of cache statistics.
*/
public CacheStatus(final StatusCode code, final String description, final CacheStatistics... statistics) {
super(code, buildDescription(description, statistics));
this.statistics = statistics;
}
/**
* Creates a new instance when cache statistics are unavailable due to given exception.
*
* @param e Cause of unavailable statistics.
*/
public CacheStatus(final Exception e) {
super(StatusCode.ERROR,
String.format("Error fetching cache status: %s::%s", e.getClass().getSimpleName(), e.getMessage()));
this.statistics = null;
}
/**
* Gets the current cache statistics.
*
* @return Cache statistics.
*/
public CacheStatistics[] getStatistics() {
return this.statistics;
}
private static String buildDescription(final String desc, final CacheStatistics... statistics) {
if (statistics == null || statistics.length == 0) {
return desc;
}
final StringBuilder sb = new StringBuilder();
if (desc != null) {
sb.append(desc);
if (!desc.endsWith(".")) {
sb.append('.');
}
sb.append(' ');
}
sb.append("Cache statistics: [");
int i = 0;
for (final CacheStatistics stats : statistics) {
if (i++ > 0) {
sb.append('|');
}
stats.toString(sb);
}
sb.append(']');
return sb.toString();
}
}

View File

@ -0,0 +1,89 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import javax.validation.constraints.NotNull;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
/**
* Monitors a data source that describes a single connection or connection pool to a database.
*
* @author Marvin S. Addison
* @since 3.5.1
*/
public class DataSourceMonitor extends AbstractPoolMonitor {
@NotNull
private final JdbcTemplate jdbcTemplate;
@NotNull
private String validationQuery;
/**
* Creates a new instance that monitors the given data source.
*
* @param dataSource Data source to monitor.
*/
public DataSourceMonitor(final DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
/**
* Sets the validation query used to monitor the data source. The validation query should return
* at least one result; otherwise results are ignored.
*
* @param query Validation query that should be as efficient as possible.
*/
public void setValidationQuery(final String query) {
this.validationQuery = query;
}
@Override
protected StatusCode checkPool() throws Exception {
return this.jdbcTemplate.query(this.validationQuery, new ResultSetExtractor<StatusCode>() {
public StatusCode extractData(final ResultSet rs) throws SQLException, DataAccessException {
if (rs.next()) {
return StatusCode.OK;
}
return StatusCode.WARN;
}
});
}
@Override
protected int getIdleCount() {
return PoolStatus.UNKNOWN_COUNT;
}
@Override
protected int getActiveCount() {
return PoolStatus.UNKNOWN_COUNT;
}
}

View File

@ -0,0 +1,74 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.validation.constraints.NotNull;
/**
* Simple health check monitor that reports the overall health as the greatest reported
* {@link StatusCode} of an arbitrary number of individual checks.
*
* @author Marvin S. Addison
* @since 3.5.0
*/
public class HealthCheckMonitor implements Monitor<HealthStatus> {
/** Individual monitors that comprise health check. */
@NotNull
private Collection<Monitor> monitors = Collections.emptySet();
/**
* Sets the monitors that comprise the health check.
*
* @param monitors Collection of monitors responsible for observing various aspects of CAS.
*/
public void setMonitors(final Collection<Monitor> monitors) {
this.monitors = monitors;
}
/** {@inheritDoc} */
public String getName() {
return HealthCheckMonitor.class.getSimpleName();
}
/** {@inheritDoc} */
public HealthStatus observe() {
final Map<String, Status> results = new LinkedHashMap<String, Status>(this.monitors.size());
StatusCode code = StatusCode.UNKNOWN;
Status result;
for (final Monitor monitor : this.monitors) {
try {
result = monitor.observe();
if (result.getCode().value() > code.value()) {
code = result.getCode();
}
} catch (final Exception e) {
code = StatusCode.ERROR;
result = new Status(code, e.getClass().getSimpleName() + ": " + e.getMessage());
}
results.put(monitor.getName(), result);
}
return new HealthStatus(code, results);
}
}

View File

@ -0,0 +1,57 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
import java.util.Collections;
import java.util.Map;
/**
* Describes the overall health status of the CAS server as determined by composite status values.
*
* @author Marvin S. Addison
* @since 3.5.0
*/
public class HealthStatus extends Status {
/** Map of names (e.g. monitor that produced it) to status information. */
private final Map<String, Status> details;
/**
* Creates a new status object with the given code.
*
* @param code Status code.
* @param detailMap Map of names to status information. A reasonable name would be, for example, the name of
* the monitor that produced it.
* @see #getCode()
*/
public HealthStatus(final StatusCode code, final Map<String, Status> detailMap) {
super(code);
this.details = Collections.unmodifiableMap(detailMap);
}
/**
* Gets the status details comprising the individual health checks performed for overall health status.
*
* @return Map of named status items to status information for each check performed.
*/
public Map<String, Status> getDetails() {
return this.details;
}
}

View File

@ -0,0 +1,67 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
/**
* Monitors JVM memory usage.
*
* @author Marvin S. Addison
* @since 3.5.0
*/
public class MemoryMonitor implements Monitor<MemoryStatus> {
/** Default percent free memory warning threshold. */
public static final int DEFAULT_FREE_MEMORY_WARN_THRESHOLD = 10;
/** Percent free memory warning threshold. */
private long freeMemoryWarnThreshold = DEFAULT_FREE_MEMORY_WARN_THRESHOLD;
/**
* Sets the percent of free memory below which a warning is reported.
*
* @param threshold Percent free memory warning threshold.
*/
public void setFreeMemoryWarnThreshold(final long threshold) {
if (threshold < 0) {
throw new IllegalArgumentException("Warning threshold must be non-negative.");
}
this.freeMemoryWarnThreshold = threshold;
}
/** {@inheritDoc} */
public String getName() {
return MemoryMonitor.class.getSimpleName();
}
/** {@inheritDoc} */
public MemoryStatus observe() {
final StatusCode code;
final long free = Runtime.getRuntime().freeMemory();
final long total = Runtime.getRuntime().totalMemory();
if (free * 100 / total < this.freeMemoryWarnThreshold) {
code = StatusCode.WARN;
} else {
code = StatusCode.OK;
}
return new MemoryStatus(code, free, total);
}
}

View File

@ -0,0 +1,71 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
/**
* Describes the memory status of the JVM.
*
* @author Marvin S. Addison
* @since 3.5.0
*/
public class MemoryStatus extends Status {
private static final double BYTES_PER_MB = 1048510.0;
/** JVM free memory. */
private final long freeMemory;
/** JVM total memory. */
private final long totalMemory;
/**
* Creates a new status object with the given code.
*
* @param code Status code.
* @param free JVM free memory in bytes.
* @param total JVM total memory in bytes.
*
* @see #getCode()
*/
public MemoryStatus(final StatusCode code, final long free, final long total) {
super(code, String.format("%.2fMB free, %.2fMB total.", free / BYTES_PER_MB, total / BYTES_PER_MB));
this.freeMemory = free;
this.totalMemory = total;
}
/**
* Gets JVM free memory.
*
* @return Free memory in bytes.
*/
public long getFreeMemory() {
return this.freeMemory;
}
/**
* Gets JVM total memory.
*
* @return Max memory in bytes.
*/
public long getTotalMemory() {
return this.totalMemory;
}
}

View File

@ -0,0 +1,43 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at 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.monitor;
/**
* A monitor observes a resource and reports its status.
*
* @author Marvin S. Addison
* @since 3.5.0
*/
public interface Monitor<S extends Status> {
/**
* Gets the name of the monitor.
*
* @return Monitor name.
*/
String getName();
/**
* Observes the monitored resource and reports the status.
*
* @return Status of monitored resource.
*/
S observe();
}

View File

@ -0,0 +1,96 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
/**
* Describes the status of a resource pool.
*
* @author Marvin S. Addison
* @since 3.5.0
*/
public class PoolStatus extends Status {
/**
* Return value for {@link #getActiveCount()} and {@link #getIdleCount()}
* when pool metrics are unknown or unknowable.
*/
public static final int UNKNOWN_COUNT = -1;
/** Number of idle pool resources. */
private final int idleCount;
/** Number of active pool resources. */
private final int activeCount;
/**
* Creates a new status object with the given code.
*
* @param code Status code.
* @param desc Human-readable status description.
*
* @see #getCode()
*/
public PoolStatus(final StatusCode code, final String desc, final int active, final int idle) {
super(code, buildDescription(desc, active, idle));
this.activeCount = active;
this.idleCount = idle;
}
/**
* Gets the number of idle pool resources.
*
* @return Number of idle pool members.
*/
public int getIdleCount() {
return this.idleCount;
}
/**
* Gets the number of active pool resources.
*
* @return Number of active pool members.
*/
public int getActiveCount() {
return this.activeCount;
}
private static String buildDescription(final String desc, final int active, final int idle) {
final StringBuilder sb = new StringBuilder();
if (desc != null) {
sb.append(desc);
if (!desc.endsWith(".")) {
sb.append('.');
}
sb.append(' ');
}
if (active != UNKNOWN_COUNT) {
sb.append(active).append(" active");
}
if (idle != UNKNOWN_COUNT) {
sb.append(", ").append(idle).append(" idle.");
}
if (sb.length() > 0) {
return sb.toString();
}
return null;
}
}

View File

@ -0,0 +1,114 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
import javax.validation.constraints.NotNull;
/**
* Monitors the status of a {@link org.jasig.cas.ticket.registry.TicketRegistry}
* that supports the {@link TicketRegistryState} interface for exposing internal
* state information used in status reports.
*
* @author Marvin S. Addison
* @since 3.5.0
*/
public class SessionMonitor implements Monitor<SessionStatus> {
/** Ticket registry instance that exposes state info. */
@NotNull
private TicketRegistryState registryState;
/** Threshold above which warnings are issued for session count. */
private int sessionCountWarnThreshold = -1;
/** Threshold above which warnings are issued for service ticket count. */
private int serviceTicketCountWarnThreshold = -1;
/**
* Sets the ticket registry that exposes state information that may be queried by this monitor.
* @param state
*/
public void setTicketRegistry(final TicketRegistryState state) {
this.registryState = state;
}
/**
* Sets the threshold above which warnings are issued for session counts in excess of value.
*
* @param threshold Warn threshold if non-negative value, otherwise warnings are disabled.
*/
public void setSessionCountWarnThreshold(final int threshold) {
this.sessionCountWarnThreshold = threshold;
}
/**
* Sets the threshold above which warnings are issued for service ticket counts in excess of value.
*
* @param threshold Warn threshold if non-negative value, otherwise warnings are disabled.
*/
public void setServiceTicketCountWarnThreshold(final int threshold) {
this.serviceTicketCountWarnThreshold = threshold;
}
/** {@inheritDoc} */
public String getName() {
return SessionMonitor.class.getSimpleName();
}
/** {@inheritDoc} */
public SessionStatus observe() {
try {
final int sessionCount = this.registryState.sessionCount();
final int ticketCount = this.registryState.serviceTicketCount();
if (sessionCount == Integer.MIN_VALUE || ticketCount == Integer.MIN_VALUE) {
return new SessionStatus(StatusCode.UNKNOWN,
String.format("Ticket registry %s reports unknown session and/or ticket counts.",
this.registryState.getClass().getName()),
sessionCount, ticketCount);
}
final StringBuilder msg = new StringBuilder();
StatusCode code = StatusCode.OK;
if (this.sessionCountWarnThreshold > -1 && sessionCount > this.sessionCountWarnThreshold) {
code = StatusCode.WARN;
msg.append(String.format(
"Session count (%s) is above threshold %s. ", sessionCount, this.sessionCountWarnThreshold));
} else {
msg.append(sessionCount).append(" sessions. ");
}
if (this.serviceTicketCountWarnThreshold > -1 && ticketCount > this.serviceTicketCountWarnThreshold) {
code = StatusCode.WARN;
msg.append(String.format(
"Service ticket count (%s) is above threshold %s.",
ticketCount,
this.serviceTicketCountWarnThreshold));
} else {
msg.append(ticketCount).append(" service tickets.");
}
return new SessionStatus(code, msg.toString(), sessionCount, ticketCount);
} catch (final Exception e) {
return new SessionStatus(StatusCode.ERROR, e.getMessage());
}
}
}

View File

@ -0,0 +1,82 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
/**
* Provides status information about the number of SSO sessions established in CAS.
*
* @author Marvin S. Addison
* @since 3.5.0
*/
public class SessionStatus extends Status {
/** Total number of SSO sessions maintained by CAS. */
private final int sessionCount;
/** Total number of service tickets in CAS ticket registry. */
private final int serviceTicketCount;
/**
* Creates a new status object with the given code.
*
* @param code Status code.
* @param desc Human-readable status description.
*
* @see #getCode()
*/
public SessionStatus(final StatusCode code, final String desc) {
this(code, desc, 0, 0);
}
/**
* Creates a new status object with the given code.
*
* @param code Status code.
* @param desc Human-readable status description.
* @param sessions Number of established SSO sessions in ticket registry.
* @param serviceTickets Number of service tickets in ticket registry.
*
* @see #getCode()
*/
public SessionStatus(final StatusCode code, final String desc, final int sessions, final int serviceTickets) {
super(code, desc);
this.sessionCount = sessions;
this.serviceTicketCount = serviceTickets;
}
/**
* Gets total number of SSO sessions maintained by CAS.
*
* @return Total number of SSO sessions.
*/
public int getSessionCount() {
return this.sessionCount;
}
/**
* Gets the total number of service tickets in the CAS ticket registry.
*
* @return Total number of service tickets.
*/
public int getServiceTicketCount() {
return this.serviceTicketCount;
}
}

View File

@ -0,0 +1,117 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
import java.util.Formatter;
/**
* Simple implementation of cache statistics.
*
* @author Marvin S. Addison
* @since 3.5.1
*/
public class SimpleCacheStatistics implements CacheStatistics {
private static final double BYTES_PER_MB = 1048510.0;
private final long size;
private final long capacity;
private final long evictions;
private String name;
/**
* Creates a new instance with given parameters.
*
* @param size Current cache size (e.g. items, bytes, etc).
* @param capacity Current cache capacity (e.g. items, bytes, etc). The units of capacity must be equal to size
* in order to produce a meaningful value for {@link #getPercentFree}.
* @param evictions Number of evictions reported by cache.
*/
public SimpleCacheStatistics(final long size, final long capacity, final long evictions) {
this.size = size;
this.capacity = capacity;
this.evictions = evictions;
}
/**
* Creates a new named instance with given parameters.
*
* @param size Current cache size (e.g. items, bytes, etc).
* @param capacity Current cache capacity (e.g. items, bytes, etc). The units of capacity must be equal to size
* in order to produce a meaningful value for {@link #getPercentFree}.
* @param evictions Number of evictions reported by cache.
* @param name Name of cache instance to which statistics apply.
*/
public SimpleCacheStatistics(final long size, final long capacity, final long evictions, final String name) {
this.size = size;
this.capacity = capacity;
this.evictions = evictions;
this.name = name;
}
public long getSize() {
return this.size;
}
public long getCapacity() {
return this.capacity;
}
public long getEvictions() {
return this.evictions;
}
public int getPercentFree() {
if (this.capacity == 0) {
return 0;
}
return (int) ((this.capacity - this.size) * 100 / this.capacity);
}
public void toString(final StringBuilder builder) {
if (this.name != null) {
builder.append(this.name).append(':');
}
final Formatter formatter = new Formatter(builder);
formatter.format("%.2f", this.size / BYTES_PER_MB);
builder.append("MB used, ");
builder.append(getPercentFree()).append("% free, ");
builder.append(this.evictions).append(" evictions");
}
/**
* Gets a descriptive name of the cache instance for which statistics apply.
*
* @return Name of cache instance/host to which statistics apply.
*/
public String getName() {
return this.name;
}
}

View File

@ -0,0 +1,92 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
/**
* Describes a generic status condition.
*
* @author Marvin S. Addison
* @since 3.5.0
*/
public class Status {
/** Generic UNKNOWN status. */
public static final Status UNKNOWN = new Status(StatusCode.UNKNOWN);
/** Generic OK status. */
public static final Status OK = new Status(StatusCode.OK);
/** Generic INFO status. */
public static final Status INFO = new Status(StatusCode.INFO);
/** Generic WARN status. */
public static final Status WARN = new Status(StatusCode.WARN);
/** Generic ERROR status. */
public static final Status ERROR = new Status(StatusCode.ERROR);
/** Status code. */
private final StatusCode code;
/** Human-readable status description. */
private final String description;
/**
* Creates a new status object with the given code.
*
* @param code Status code.
*
* @see #getCode()
*/
public Status(final StatusCode code) {
this(code, null);
}
/**
* Creates a new status object with the given code.
*
* @param code Status code.
* @param desc Human-readable status description.
*
* @see #getCode()
*/
public Status(final StatusCode code, final String desc) {
this.code = code;
this.description = desc;
}
/**
* Gets the status code.
*
* @return Status code.
*/
public StatusCode getCode() {
return this.code;
}
/**
* @return Human-readable description of status.
*/
public String getDescription() {
return this.description;
}
}

View File

@ -0,0 +1,56 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
/**
* Monitor status code inspired by HTTP status codes.
*
* @author Marvin S. Addison
* @since 3.5.0
*/
public enum StatusCode {
ERROR(500),
WARN(400),
INFO(300),
OK(200),
UNKNOWN(100);
/** Status code numerical value */
private final int value;
/**
* Creates a new instance with the given numeric value.
*
* @param numericValue Numeric status code value.
*/
StatusCode(final int numericValue) {
this.value = numericValue;
}
/**
* Gets the numeric value of the status code. Higher values describe more severe conditions.
*
* @return Numeric status code value.
*/
public int value() {
return this.value;
}
}

View File

@ -0,0 +1,46 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.monitor;
/**
* Describes important state information that may be optionally exposed by
* {@link org.jasig.cas.ticket.registry.TicketRegistry} components that might
* be of interest to monitors.
*
* @author Marvin S. Addison
* @since 3.5.0
*/
public interface TicketRegistryState {
/**
* Computes the number of SSO sessions stored in the ticket registry.
*
* @return Number of ticket-granting tickets in the registry at time of invocation
* or {@link Integer#MIN_VALUE} if unknown.
*/
int sessionCount();
/**
* Computes the number of service tickets stored in the ticket registry.
*
* @return Number of service tickets in the registry at time of invocation
* or {@link Integer#MIN_VALUE} if unknown.
*/
int serviceTicketCount();
}

View File

@ -0,0 +1,60 @@
<!--
Licensed to Jasig under one or more contributor license
agreements. See the NOTICE file distributed with this work
for additional information regarding copyright ownership.
Jasig licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a
copy of the License at the following location:
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html>
<body>
<p>This is the entry point to the part of the CAS processing that is independent
of the user/program interface. This layer is mostly a service to create, manage,
validate, query, and destroy tickets. The caller of this layer may be a Web
application running in a Servlet container, but it may also be a Web Service
client, RMI client, or even a program that finds these services compelling.</p>
<p>One first enters this layer with opaque Credentials requesting creation of a
TGT. The Credentials are &quot;opaque&quot; because they are an object whose nature is
irrelevant to CAS. The Credentials are carried through the layers until they can
be presented to plugin configuration beans that may recognize the underlying
type and process it. Simple Credentials might be an object with a userid and
password, but they may also be an X.509 Certificate, Kerberos Ticket, Shibboleth
artifact, XML SOAP header, or any other object.</p>
<p>The Credentials are presented to a set of objects plugged into the
Authentication process by the system administrators. If one of these plugin
elements recognizes the Credentials, validates their integrity, and maps them to
the identity of a user in the local system, then CAS has logged someone on and
creates a TGT.</p>
<p>The results of the login are somewhat opaque. The TGT references an
Authentication object that references a Principal. Minimally the Principal
contains a simple ID string. What else the Principal or Authentication object
contain are transparent to CAS. These objects must be Serializable, because the
Tickets and everything they reference may need to be checkpointed to disk or
shared with multiple machines in a clustering configuration. CAS is managing the
TGT and, as a result, it also saves everything in the concrete classes
referenced by it.</p>
<p>Any additional information about the User fetched at login is of no direct
interest to CAS. It may, however, be meaningful to the caller of this layer. In
the case of an HTTP Servlet interface, this would be the View layer that
generates, among other things, the response to the Ticket Validation query.</p>
<p>Having created a TGT, CAS then proceeds to create Service Tickets which are
chained off the TGT, and in the case of Proxy authentication creates chains of
TGTs for the Service Ticket. TGTs and STs are stored in a cache until they
expire or are deleted. Various technologies can be plugged into the back end so
that the Ticket cache is shared among machines or persisted across a reboot.</p>
</body>
</html>

View File

@ -0,0 +1,130 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.remoting.server;
import org.jasig.cas.CentralAuthenticationService;
import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.ticket.TicketException;
import org.jasig.cas.validation.Assertion;
import org.springframework.util.Assert;
import javax.validation.*;
import javax.validation.constraints.NotNull;
import java.util.Set;
/**
* Wrapper implementation around a CentralAuthenticationService that does
* completes the marshalling of parameters from the web-service layer to the
* service layer. Typically the only thing that is done is to validate the
* parameters (as you would in the web tier) and then delegate to the service
* layer.
* <p>
* The following properties are required:
* </p>
* <ul>
* <li>centralAuthenticationService - the service layer we are delegating to.</li>
* </ul>
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.0
*/
public final class RemoteCentralAuthenticationService implements CentralAuthenticationService {
/** The CORE to delegate to. */
@NotNull
private CentralAuthenticationService centralAuthenticationService;
/** The validators to check the Credentials. */
@NotNull
private Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
/**
* @throws IllegalArgumentException if the Credentials are null or if given
* invalid credentials.
*/
public String createTicketGrantingTicket(final Credentials credentials) throws TicketException {
Assert.notNull(credentials, "credentials cannot be null");
checkForErrors(credentials);
return this.centralAuthenticationService.createTicketGrantingTicket(credentials);
}
public String grantServiceTicket(final String ticketGrantingTicketId, final Service service) throws TicketException {
return this.centralAuthenticationService.grantServiceTicket(ticketGrantingTicketId, service);
}
/**
* @throws IllegalArgumentException if given invalid credentials
*/
public String grantServiceTicket(final String ticketGrantingTicketId, final Service service, final Credentials credentials) throws TicketException {
checkForErrors(credentials);
return this.centralAuthenticationService.grantServiceTicket(ticketGrantingTicketId, service, credentials);
}
public Assertion validateServiceTicket(final String serviceTicketId, final Service service) throws TicketException {
return this.centralAuthenticationService.validateServiceTicket(serviceTicketId, service);
}
public void destroyTicketGrantingTicket(final String ticketGrantingTicketId) {
this.centralAuthenticationService.destroyTicketGrantingTicket(ticketGrantingTicketId);
}
/**
* @throws IllegalArgumentException if the credentials are invalid.
*/
public String delegateTicketGrantingTicket(final String serviceTicketId, final Credentials credentials) throws TicketException {
checkForErrors(credentials);
return this.centralAuthenticationService.delegateTicketGrantingTicket(serviceTicketId, credentials);
}
private void checkForErrors(final Credentials credentials) {
if (credentials == null) {
return;
}
final Set<ConstraintViolation<Credentials>> errors = this.validator.validate(credentials);
if (!errors.isEmpty()) {
throw new IllegalArgumentException("Error validating credentials: " + errors.toString());
}
}
/**
* Set the CentralAuthenticationService.
*
* @param centralAuthenticationService The CentralAuthenticationService to
* set.
*/
public void setCentralAuthenticationService(
final CentralAuthenticationService centralAuthenticationService) {
this.centralAuthenticationService = centralAuthenticationService;
}
/**
* Set the list of validators.
*
* @param validator The array of validators to use.
*/
public void setValidator(final Validator validator) {
this.validator = validator;
}
}

View File

@ -0,0 +1,27 @@
<!--
Licensed to Jasig under one or more contributor license
agreements. See the NOTICE file distributed with this work
for additional information regarding copyright ownership.
Jasig licenses this file to you under the Apache License,
Version 2.0 (the "License"); you may not use this file
except in compliance with the License. You may obtain a
copy of the License at 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.
-->
<html>
<body>
Classes to allow CAS to be exposed as a server.
</body>
</html>

View File

@ -0,0 +1,328 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.services;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.hibernate.annotations.IndexColumn;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.Table;
import javax.persistence.DiscriminatorType;
import javax.persistence.GeneratedValue;
import javax.persistence.JoinTable;
import javax.persistence.JoinColumn;
import javax.persistence.Column;
import javax.persistence.FetchType;
/**
* Base class for mutable, persistable registered services.
*
* @author Marvin S. Addison
* @author Scott Battaglia
*/
@Entity
@Inheritance
@DiscriminatorColumn(
name = "expression_type",
length = 15,
discriminatorType = DiscriminatorType.STRING,
columnDefinition = "VARCHAR(15) DEFAULT 'ant'")
@Table(name = "RegisteredServiceImpl")
public abstract class AbstractRegisteredService
implements RegisteredService, Comparable<RegisteredService>, Serializable {
/** Serialization version marker */
private static final long serialVersionUID = 7645279151115635245L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id = -1;
@ElementCollection(targetClass = String.class, fetch = FetchType.EAGER)
@JoinTable(name = "rs_attributes", joinColumns = @JoinColumn(name = "RegisteredServiceImpl_id"))
@Column(name = "a_name", nullable = false)
@IndexColumn(name = "a_id")
private List<String> allowedAttributes = new ArrayList<String>();
private String description;
protected String serviceId;
private String name;
private String theme;
private boolean allowedToProxy = true;
private boolean enabled = true;
private boolean ssoEnabled = true;
private boolean anonymousAccess = false;
private boolean ignoreAttributes = false;
@Column(name = "evaluation_order", nullable = false)
private int evaluationOrder;
/**
* Name of the user attribute that this service expects as the value of the username payload in the
* validate responses.
*/
@Column(name = "username_attr", nullable = true, length = 256)
private String usernameAttribute = null;
public boolean isAnonymousAccess() {
return this.anonymousAccess;
}
public void setAnonymousAccess(final boolean anonymousAccess) {
this.anonymousAccess = anonymousAccess;
}
public List<String> getAllowedAttributes() {
return this.allowedAttributes;
}
public long getId() {
return this.id;
}
public String getDescription() {
return this.description;
}
public String getServiceId() {
return this.serviceId;
}
public String getName() {
return this.name;
}
public String getTheme() {
return this.theme;
}
public boolean isAllowedToProxy() {
return this.allowedToProxy;
}
public boolean isEnabled() {
return this.enabled;
}
public boolean isSsoEnabled() {
return this.ssoEnabled;
}
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (this == o) {
return true;
}
if (!(o instanceof AbstractRegisteredService)) {
return false;
}
final AbstractRegisteredService that = (AbstractRegisteredService) o;
return new EqualsBuilder()
.append(this.allowedToProxy, that.allowedToProxy)
.append(this.anonymousAccess, that.anonymousAccess)
.append(this.enabled, that.enabled)
.append(this.evaluationOrder, that.evaluationOrder)
.append(this.ignoreAttributes, that.ignoreAttributes)
.append(this.ssoEnabled, that.ssoEnabled)
.append(this.allowedAttributes, that.allowedAttributes)
.append(this.description, that.description)
.append(this.name, that.name)
.append(this.serviceId, that.serviceId)
.append(this.theme, that.theme)
.append(this.usernameAttribute, that.usernameAttribute)
.isEquals();
}
public int hashCode() {
return new HashCodeBuilder(7, 31)
.append(this.allowedAttributes)
.append(this.description)
.append(this.serviceId)
.append(this.name)
.append(this.theme)
.append(this.enabled)
.append(this.ssoEnabled)
.append(this.anonymousAccess)
.append(this.ignoreAttributes)
.append(this.evaluationOrder)
.append(this.usernameAttribute)
.toHashCode();
}
public void setAllowedAttributes(final List<String> allowedAttributes) {
if (allowedAttributes == null) {
this.allowedAttributes = new ArrayList<String>();
} else {
this.allowedAttributes = allowedAttributes;
}
}
public void setAllowedToProxy(final boolean allowedToProxy) {
this.allowedToProxy = allowedToProxy;
}
public void setDescription(final String description) {
this.description = description;
}
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
public abstract void setServiceId(final String id);
public void setId(final long id) {
this.id = id;
}
public void setName(final String name) {
this.name = name;
}
public void setSsoEnabled(final boolean ssoEnabled) {
this.ssoEnabled = ssoEnabled;
}
public void setTheme(final String theme) {
this.theme = theme;
}
public boolean isIgnoreAttributes() {
return this.ignoreAttributes;
}
public void setIgnoreAttributes(final boolean ignoreAttributes) {
this.ignoreAttributes = ignoreAttributes;
}
public void setEvaluationOrder(final int evaluationOrder) {
this.evaluationOrder = evaluationOrder;
}
public int getEvaluationOrder() {
return this.evaluationOrder;
}
public String getUsernameAttribute() {
return this.usernameAttribute;
}
/**
* Sets the name of the user attribute to use as the username when providing usernames to this registered service.
*
* <p>Note: The username attribute will have no affect on services that are marked for anonymous access.
*
* @param username attribute to release for this service that may be one of the following values:
* <ul>
* <li>name of the attribute this service prefers to consume as username</li>.
* <li><code>null</code> to enforce default CAS behavior</li>
* </ul>
* @see #isAnonymousAccess()
*/
public void setUsernameAttribute(final String username) {
if (StringUtils.isBlank(username)) {
this.usernameAttribute = null;
} else {
this.usernameAttribute = username;
}
}
public Object clone() throws CloneNotSupportedException {
final AbstractRegisteredService clone = newInstance();
clone.copyFrom(this);
return clone;
}
/**
* Copies the properties of the source service into this instance.
*
* @param source Source service from which to copy properties.
*/
public void copyFrom(final RegisteredService source) {
this.setId(source.getId());
this.setAllowedAttributes(new ArrayList<String>(source.getAllowedAttributes()));
this.setAllowedToProxy(source.isAllowedToProxy());
this.setDescription(source.getDescription());
this.setEnabled(source.isEnabled());
this.setName(source.getName());
this.setServiceId(source.getServiceId());
this.setSsoEnabled(source.isSsoEnabled());
this.setTheme(source.getTheme());
this.setAnonymousAccess(source.isAnonymousAccess());
this.setIgnoreAttributes(source.isIgnoreAttributes());
this.setEvaluationOrder(source.getEvaluationOrder());
this.setUsernameAttribute(source.getUsernameAttribute());
}
/**
* Compares this instance with the <code>other</code> registered service based on
* evaluation order, name. The name comparison is case insensitive.
*
* @see #getEvaluationOrder()
*/
public int compareTo(final RegisteredService other) {
return new CompareToBuilder()
.append(this.getEvaluationOrder(), other.getEvaluationOrder())
.append(this.getName().toLowerCase(), other.getName().toLowerCase())
.toComparison();
}
public String toString() {
final ToStringBuilder toStringBuilder = new ToStringBuilder(null, ToStringStyle.SHORT_PREFIX_STYLE);
toStringBuilder.append("id", this.id);
toStringBuilder.append("name", this.name);
toStringBuilder.append("description", this.description);
toStringBuilder.append("serviceId", this.serviceId);
toStringBuilder.append("usernameAttribute", this.usernameAttribute);
toStringBuilder.append("attributes", this.allowedAttributes.toArray());
return toStringBuilder.toString();
}
protected abstract AbstractRegisteredService newInstance();
}

View File

@ -0,0 +1,170 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.services;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import com.github.inspektr.audit.annotation.Audit;
import org.jasig.cas.authentication.principal.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import javax.validation.constraints.NotNull;
/**
* Default implementation of the {@link ServicesManager} interface. If there are
* no services registered with the server, it considers the ServicecsManager
* disabled and will not prevent any service from using CAS.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public final class DefaultServicesManagerImpl implements ReloadableServicesManager {
private final Logger log = LoggerFactory.getLogger(getClass());
/** Instance of ServiceRegistryDao. */
@NotNull
private ServiceRegistryDao serviceRegistryDao;
/** Map to store all services. */
private ConcurrentHashMap<Long, RegisteredService> services = new ConcurrentHashMap<Long, RegisteredService>();
/** Default service to return if none have been registered. */
private RegisteredService disabledRegisteredService;
public DefaultServicesManagerImpl(
final ServiceRegistryDao serviceRegistryDao) {
this(serviceRegistryDao, new ArrayList<String>());
}
/**
* Constructs an instance of the {@link DefaultServicesManagerImpl} where the default RegisteredService
* can include a set of default attributes to use if no services are defined in the registry.
*
* @param serviceRegistryDao the Service Registry Dao.
* @param defaultAttributes the list of default attributes to use.
*/
public DefaultServicesManagerImpl(final ServiceRegistryDao serviceRegistryDao, final List<String> defaultAttributes) {
this.serviceRegistryDao = serviceRegistryDao;
this.disabledRegisteredService = constructDefaultRegisteredService(defaultAttributes);
load();
}
@Transactional(readOnly = false)
@Audit(action = "DELETE_SERVICE", actionResolverName = "DELETE_SERVICE_ACTION_RESOLVER", resourceResolverName = "DELETE_SERVICE_RESOURCE_RESOLVER")
public synchronized RegisteredService delete(final long id) {
final RegisteredService r = findServiceBy(id);
if (r == null) {
return null;
}
this.serviceRegistryDao.delete(r);
this.services.remove(id);
return r;
}
/**
* Note, if the repository is empty, this implementation will return a default service to grant all access.
* <p>
* This preserves default CAS behavior.
*/
public RegisteredService findServiceBy(final Service service) {
final Collection<RegisteredService> c = convertToTreeSet();
if (c.isEmpty()) {
return this.disabledRegisteredService;
}
for (final RegisteredService r : c) {
if (r.matches(service)) {
return r;
}
}
return null;
}
public RegisteredService findServiceBy(final long id) {
final RegisteredService r = this.services.get(id);
try {
return r == null ? null : (RegisteredService) r.clone();
} catch (final CloneNotSupportedException e) {
return r;
}
}
protected TreeSet<RegisteredService> convertToTreeSet() {
return new TreeSet<RegisteredService>(this.services.values());
}
public Collection<RegisteredService> getAllServices() {
return Collections.unmodifiableCollection(convertToTreeSet());
}
public boolean matchesExistingService(final Service service) {
return findServiceBy(service) != null;
}
@Transactional(readOnly = false)
@Audit(action = "SAVE_SERVICE", actionResolverName = "SAVE_SERVICE_ACTION_RESOLVER", resourceResolverName = "SAVE_SERVICE_RESOURCE_RESOLVER")
public synchronized RegisteredService save(final RegisteredService registeredService) {
final RegisteredService r = this.serviceRegistryDao.save(registeredService);
this.services.put(r.getId(), r);
return r;
}
public void reload() {
log.info("Reloading registered services.");
load();
}
private void load() {
final ConcurrentHashMap<Long, RegisteredService> localServices = new ConcurrentHashMap<Long, RegisteredService>();
for (final RegisteredService r : this.serviceRegistryDao.load()) {
log.debug("Adding registered service " + r.getServiceId());
localServices.put(r.getId(), r);
}
this.services = localServices;
log.info(String.format("Loaded %s services.", this.services.size()));
}
private RegisteredService constructDefaultRegisteredService(final List<String> attributes) {
final RegisteredServiceImpl r = new RegisteredServiceImpl();
r.setAllowedToProxy(true);
r.setAnonymousAccess(false);
r.setEnabled(true);
r.setSsoEnabled(true);
r.setAllowedAttributes(attributes);
if (attributes == null || attributes.isEmpty()) {
r.setIgnoreAttributes(true);
}
return r;
}
}

View File

@ -0,0 +1,87 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.services;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* Default In Memory Service Registry Dao for test/demonstration purposes.
*
* @author Scott Battaglia
* @version $Revision: 1.1 $ $Date: 2005/08/19 18:27:17 $
* @since 3.1
*
*/
public final class InMemoryServiceRegistryDaoImpl implements ServiceRegistryDao {
@NotNull
private List<RegisteredService> registeredServices = new ArrayList<RegisteredService>();
public boolean delete(RegisteredService registeredService) {
return this.registeredServices.remove(registeredService);
}
public RegisteredService findServiceById(final long id) {
for (final RegisteredService r : this.registeredServices) {
if (r.getId() == id) {
return r;
}
}
return null;
}
public List<RegisteredService> load() {
return this.registeredServices;
}
public RegisteredService save(final RegisteredService registeredService) {
if (registeredService.getId() == -1) {
((AbstractRegisteredService) registeredService).setId(findHighestId()+1);
}
this.registeredServices.remove(registeredService);
this.registeredServices.add(registeredService);
return registeredService;
}
public void setRegisteredServices(final List<RegisteredService> registeredServices) {
this.registeredServices = registeredServices;
}
/**
* This isn't super-fast but I don't expect thousands of services.
*
* @return
*/
private long findHighestId() {
long id = 0;
for (final RegisteredService r : this.registeredServices) {
if (r.getId() > id) {
id = r.getId();
}
}
return id;
}
}

View File

@ -0,0 +1,61 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.services;
import org.springframework.orm.jpa.support.JpaDaoSupport;
import java.util.List;
/**
* Implementation of the ServiceRegistryDao based on JPA.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public final class JpaServiceRegistryDaoImpl extends JpaDaoSupport implements
ServiceRegistryDao {
public boolean delete(final RegisteredService registeredService) {
getJpaTemplate().remove(
getJpaTemplate().contains(registeredService) ? registeredService
: getJpaTemplate().merge(registeredService));
return true;
}
public List<RegisteredService> load() {
return getJpaTemplate().find("select r from AbstractRegisteredService r");
}
public RegisteredService save(final RegisteredService registeredService) {
final boolean isNew = registeredService.getId() == -1;
final RegisteredService r = getJpaTemplate().merge(registeredService);
if (!isNew) {
getJpaTemplate().persist(r);
}
return r;
}
public RegisteredService findServiceById(final long id) {
return getJpaTemplate().find(AbstractRegisteredService.class, id);
}
}

View File

@ -0,0 +1,63 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.services;
import org.jasig.cas.authentication.principal.Service;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import java.util.regex.Pattern;
/**
* Mutable registered service that uses Java regular expressions for service matching.
*
* @author Marvin S. Addison
* @version $Revision: $
*/
@Entity
@DiscriminatorValue("regex")
public class RegexRegisteredService extends AbstractRegisteredService {
/** Serialization version marker */
private static final long serialVersionUID = -8258660210826975771L;
private transient Pattern servicePattern;
public void setServiceId(final String id) {
servicePattern = createPattern(id);
serviceId = id;
}
public boolean matches(final Service service) {
if (servicePattern == null) {
servicePattern = createPattern(serviceId);
}
return service != null && servicePattern.matcher(service.getId()).matches();
}
protected AbstractRegisteredService newInstance() {
return new RegexRegisteredService();
}
private Pattern createPattern(final String pattern) {
if (pattern == null) {
throw new IllegalArgumentException("Pattern cannot be null.");
}
return Pattern.compile(pattern);
}
}

View File

@ -0,0 +1,151 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.services;
import java.io.Serializable;
import java.util.List;
import org.jasig.cas.authentication.principal.Service;
/**
* Interface for a service that can be registered by the Services Management
* interface.
*
* @author Scott Battaglia
* @version $Revision$ $Date$
* @since 3.1
*/
public interface RegisteredService extends Cloneable, Serializable {
/**
* Is this application currently allowed to use CAS?
*
* @return true if it can use CAS, false otherwise.
*/
boolean isEnabled();
/**
* Determines whether the service is allowed anonymous or privileged access
* to user information. Anonymous access should not return any identifying
* information such as user id.
*
* @return if we should use a pseudo random identifier instead of their real id
*/
boolean isAnonymousAccess();
/**
* Sets whether we should bother to read the attribute list or not.
*
* @return true if we should read it, false otherwise.
*/
boolean isIgnoreAttributes();
/**
* Returns the list of allowed attributes.
*
* @return the list of attributes
*/
List<String> getAllowedAttributes();
/**
* Is this application allowed to take part in the proxying capabilities of
* CAS?
*
* @return true if it can, false otherwise.
*/
boolean isAllowedToProxy();
/**
* The unique identifier for this service.
*
* @return the unique identifier for this service.
*/
String getServiceId();
/**
* The numeric identifier for this service.
*
* @return the numeric identifier for this service.
*/
long getId();
/**
* Returns the name of the service.
*
* @return the name of the service.
*/
String getName();
/**
* Returns a short theme name. Services do not need to have unique theme
* names.
*
* @return the theme name associated with this service.
*/
String getTheme();
/**
* Does this application participate in the SSO session?
*
* @return true if it does, false otherwise.
*/
boolean isSsoEnabled();
/**
* Returns the description of the service.
*
* @return the description of the service.
*/
String getDescription();
/**
* Gets the relative evaluation order of this service when determining
* matches.
* @return Evaluation order relative to other registered services.
* Services with lower values will be evaluated for a match before others.
*/
int getEvaluationOrder();
/**
* Sets the relative evaluation order of this service when determining
* matches.
*/
void setEvaluationOrder(final int evaluationOrder);
/**
* Get the name of the attribute this service prefers to consume as username.
*
* @return Either of the following values:
* <ul>
* <li><code>String</code> representing the name of the attribute to consume as username</li>
* <li><code>null</code> indicating the default username</li>
* </ul>
*/
public String getUsernameAttribute();
/**
* Returns whether the service matches the registered service.
* <p>Note, as of 3.1.2, matches are case insensitive.
*
* @param service the service to match.
* @return true if they match, false otherwise.
*/
boolean matches(final Service service);
Object clone() throws CloneNotSupportedException;
}

View File

@ -0,0 +1,57 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.services;
import org.jasig.cas.authentication.principal.Service;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
/**
* Mutable registered service that uses Ant path patterns for service matching.
*
* @author Scott Battaglia
* @author Marvin S. Addison
* @version $Revision$ $Date$
* @since 3.1
*/
@Entity
@DiscriminatorValue("ant")
public class RegisteredServiceImpl extends AbstractRegisteredService {
/** Unique Id for serialization. */
private static final long serialVersionUID = -5906102762271197627L;
private static final PathMatcher PATH_MATCHER = new AntPathMatcher();
public void setServiceId(final String id) {
this.serviceId = id;
}
public boolean matches(final Service service) {
return service != null && PATH_MATCHER.match(serviceId.toLowerCase(), service.getId().toLowerCase());
}
protected AbstractRegisteredService newInstance() {
return new RegisteredServiceImpl();
}
}

View File

@ -0,0 +1,38 @@
/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.services;
/**
* Interface to allow for ServicesManagers to attempt to reload their list of
* services.
*
* @author Scott Battaglia
* @version $Revision: 1.1 $ $Date: 2005/08/19 18:27:17 $
* @since 3.1
*/
public interface ReloadableServicesManager extends ServicesManager {
/**
* Inform the ServicesManager to reload its list of services if its cached
* them. Note that this is a suggestion and that ServicesManagers are free
* to reload whenever they want.
*/
void reload();
}

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