CASC-108
commit before we move to the new repository. We cannot release this yet because we're missing a contributor license agreement.
This commit is contained in:
parent
b9208f9077
commit
23711eb6a1
|
|
@ -2,7 +2,7 @@
|
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.jasig.cas</groupId>
|
||||
<version>3.1.10</version>
|
||||
<version>3.1.11-SNAPSHOT</version>
|
||||
<artifactId>cas-client</artifactId>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
|
@ -10,50 +10,6 @@
|
|||
<artifactId>cas-client-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>JA-SIG CAS Client for Java - Core</name>
|
||||
<build>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<testSourceDirectory>src/test/java</testSourceDirectory>
|
||||
<testResources>
|
||||
<testResource>
|
||||
<directory>src/test/resources</directory>
|
||||
<filtering>false</filtering>
|
||||
</testResource>
|
||||
</testResources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.4</source>
|
||||
<target>1.4</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Tests*</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-clover-plugin</artifactId>
|
||||
<configuration>
|
||||
<licenseLocation>${basedir}/src/test/clover/clover.license</licenseLocation>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>pre-site</phase>
|
||||
<goals>
|
||||
<goal>instrument</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
|
|
|||
|
|
@ -15,15 +15,12 @@ import java.util.Map;
|
|||
* @version $Revision$ $Date$
|
||||
* @since 3.1
|
||||
*/
|
||||
public class AttributePrincipalImpl implements AttributePrincipal {
|
||||
public class AttributePrincipalImpl extends SimplePrincipal implements AttributePrincipal {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(AttributePrincipalImpl.class);
|
||||
private static final Log LOG = LogFactory.getLog(AttributePrincipalImpl.class);
|
||||
|
||||
/** Unique Id for Serialization */
|
||||
private static final long serialVersionUID = -8810123156070148535L;
|
||||
|
||||
/** The unique identifier for this principal. */
|
||||
private final String name;
|
||||
private static final long serialVersionUID = -1443182634624927187L;
|
||||
|
||||
/** Map of key/value pairs about this principal. */
|
||||
private final Map attributes;
|
||||
|
|
@ -73,12 +70,11 @@ public class AttributePrincipalImpl implements AttributePrincipal {
|
|||
* @param proxyRetriever the ProxyRetriever implementation to call back to the CAS server.
|
||||
*/
|
||||
public AttributePrincipalImpl(final String name, final Map attributes, final String proxyGrantingTicket, final ProxyRetriever proxyRetriever) {
|
||||
this.name = name;
|
||||
super(name);
|
||||
this.attributes = attributes;
|
||||
this.proxyGrantingTicket = proxyGrantingTicket;
|
||||
this.proxyRetriever = proxyRetriever;
|
||||
|
||||
CommonUtils.assertNotNull(this.name, "name cannot be null.");
|
||||
CommonUtils.assertNotNull(this.attributes, "attributes cannot be null.");
|
||||
}
|
||||
|
||||
|
|
@ -94,8 +90,4 @@ public class AttributePrincipalImpl implements AttributePrincipal {
|
|||
LOG.debug("No ProxyGrantingTicket was supplied, so no Proxy Ticket can be retrieved.");
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2010 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.security.acl.Group;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Simple security group implementation
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$
|
||||
* @since 3.1.11
|
||||
*
|
||||
*/
|
||||
public final class SimpleGroup extends SimplePrincipal implements Group {
|
||||
|
||||
private static final long serialVersionUID = 1541943977571896383L;
|
||||
|
||||
private final Set members = new HashSet();
|
||||
|
||||
/**
|
||||
* Creates a new group with the given name.
|
||||
* @param name Group name.
|
||||
*/
|
||||
public SimpleGroup(final String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public boolean addMember(final Principal user) {
|
||||
return this.members.add(user);
|
||||
}
|
||||
|
||||
public boolean isMember(final Principal member) {
|
||||
return this.members.contains(member);
|
||||
}
|
||||
|
||||
public Enumeration members() {
|
||||
return new EnumerationAdapter(this.members.iterator());
|
||||
}
|
||||
|
||||
public boolean removeMember(final Principal user) {
|
||||
return this.members.remove(user);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return super.toString() + ": " + members.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts a {@link java.util.Iterator} onto an {@link java.util.Enumeration}.
|
||||
*/
|
||||
private static class EnumerationAdapter implements Enumeration {
|
||||
|
||||
/** Iterator backing enumeration operations */
|
||||
private Iterator iterator;
|
||||
|
||||
/**
|
||||
* Creates a new instance backed by the given iterator.
|
||||
* @param i Iterator backing enumeration operations.
|
||||
*/
|
||||
public EnumerationAdapter(final Iterator i) {
|
||||
this.iterator = i;
|
||||
}
|
||||
|
||||
public boolean hasMoreElements() {
|
||||
return this.iterator.hasNext();
|
||||
}
|
||||
|
||||
public Object nextElement() {
|
||||
return this.iterator.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2010 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
*/
|
||||
package org.jasig.cas.client.authentication;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.security.Principal;
|
||||
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
/**
|
||||
* Simple security principal implementation.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$
|
||||
* @since 3.1.11
|
||||
*
|
||||
*/
|
||||
public class SimplePrincipal implements Principal, Serializable {
|
||||
|
||||
/** SimplePrincipal.java */
|
||||
private static final long serialVersionUID = -5645357206342793145L;
|
||||
|
||||
/** The unique identifier for this principal. */
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Creates a new principal with the given name.
|
||||
* @param name Principal name.
|
||||
*/
|
||||
public SimplePrincipal(final String name) {
|
||||
this.name = name;
|
||||
CommonUtils.assertNotNull(this.name, "name cannot be null.");
|
||||
}
|
||||
|
||||
public final String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
public boolean equals(final Object o) {
|
||||
if (o == null) {
|
||||
return false;
|
||||
} else if (!(o instanceof SimplePrincipal)) {
|
||||
return false;
|
||||
} else {
|
||||
return getName().equals(((SimplePrincipal)o).getName());
|
||||
}
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return 37 * getName().hashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2010 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
*/
|
||||
package org.jasig.cas.client.jaas;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.jasig.cas.client.authentication.SimplePrincipal;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
|
||||
/**
|
||||
* Principal implementation that contains the CAS ticket validation assertion.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$
|
||||
* @since 3.1.11
|
||||
*
|
||||
*/
|
||||
public class AssertionPrincipal extends SimplePrincipal implements Serializable {
|
||||
|
||||
/** AssertionPrincipal.java */
|
||||
private static final long serialVersionUID = 2288520214366461693L;
|
||||
|
||||
private Assertion assertion;
|
||||
|
||||
/**
|
||||
* Creates a new principal containing the CAS assertion.
|
||||
*
|
||||
* @param name Principal name.
|
||||
* @param assertion CAS assertion.
|
||||
*/
|
||||
public AssertionPrincipal(final String name, final Assertion assertion) {
|
||||
super(name);
|
||||
this.assertion = assertion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CAS ticket validation assertion.
|
||||
*/
|
||||
public Assertion getAssertion() {
|
||||
return this.assertion;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,365 @@
|
|||
/*
|
||||
* Copyright 2010 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
*/
|
||||
package org.jasig.cas.client.jaas;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.security.acl.Group;
|
||||
import java.util.*;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.NameCallback;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.spi.LoginModule;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jasig.cas.client.authentication.SimpleGroup;
|
||||
import org.jasig.cas.client.authentication.SimplePrincipal;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.ReflectUtils;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
import org.jasig.cas.client.validation.TicketValidator;
|
||||
|
||||
/**
|
||||
* JAAS login module that delegates to a CAS {@link TicketValidator} component
|
||||
* for authentication, and on success populates a {@link Subject} with principal
|
||||
* data including NetID and principal attributes. The module expects to be provided
|
||||
* with the CAS ticket (required) and service (optional) parameters via
|
||||
* {@link PasswordCallback} and {@link NameCallback}, respectively, by the
|
||||
* {@link CallbackHandler} that is part of the JAAS framework in which the servlet
|
||||
* resides.
|
||||
*
|
||||
* <p>
|
||||
* Module configuration options:
|
||||
* <ul>
|
||||
* <li>ticketValidatorClass - Fully-qualified class name of CAS ticket validator class.</li>
|
||||
* <li>casServerUrlPrefix - URL to root of CAS Web application context.</li>
|
||||
* <li>service (optional) - CAS service parameter that may be overridden by callback handler.
|
||||
* NOTE: service must be specified by at least one component such that it is available at
|
||||
* service ticket validation time</li>
|
||||
* <li>defaultRoles (optional) - Comma-delimited list of static roles applied to all
|
||||
* authenticated principals.</li>
|
||||
* <li>roleAttributeNames (optional) - Comma-delimited list of attribute names that describe
|
||||
* role data delivered to CAS in the service-ticket validation response that should be
|
||||
* applied to the current authenticated principal.</li>
|
||||
* <li>principalGroupName (optional) - The name of a group principal containing the
|
||||
* primary principal name of the current JAAS subject. The default value is "CallerPrincipal",
|
||||
* which is suitable for JBoss.</li>
|
||||
* <li>roleGroupName (optional) - The name of a group principal containing all role data.
|
||||
* The default value is "Roles", which is suitable for JBoss.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Module options not explicitly listed above are treated as attributes of the
|
||||
* given ticket validator class, e.g. <code>tolerance</code> in the following example.
|
||||
*
|
||||
* <p>
|
||||
* Sample jaas.config file entry for this module:
|
||||
* <pre>
|
||||
* cas {
|
||||
* org.jasig.cas.client.jaas.CasLoginModule required
|
||||
* ticketValidatorClass="org.jasig.cas.client.validation.Saml11TicketValidator"
|
||||
* casServerUrlPrefix="https://cas.example.com/cas"
|
||||
* tolerance="20000"
|
||||
* service="https://webapp.example.com/webapp"
|
||||
* defaultRoles="admin,operator"
|
||||
* roleAttributeNames="memberOf,eduPersonAffiliation"
|
||||
* principalGroupName="CallerPrincipal"
|
||||
* roleGroupName="Roles";
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$
|
||||
* @since 3.1.11
|
||||
*
|
||||
*/
|
||||
public class CasLoginModule implements LoginModule {
|
||||
/** Constant for login name stored in shared state. */
|
||||
public static final String LOGIN_NAME = "javax.security.auth.login.name";
|
||||
|
||||
/**
|
||||
* Default group name for storing caller principal.
|
||||
* The default value supports JBoss, but is configurable to hopefully
|
||||
* support other JEE containers.
|
||||
*/
|
||||
public static final String DEFAULT_PRINCIPAL_GROUP_NAME = "CallerPrincipal";
|
||||
|
||||
/**
|
||||
* Default group name for storing role membership data.
|
||||
* The default value supports JBoss, but is configurable to hopefully
|
||||
* support other JEE containers.
|
||||
*/
|
||||
public static final String DEFAULT_ROLE_GROUP_NAME = "Roles";
|
||||
|
||||
/** Logger instance */
|
||||
protected final Log log = LogFactory.getLog(getClass());
|
||||
|
||||
/** JAAS authentication subject */
|
||||
protected Subject subject;
|
||||
|
||||
/** JAAS callback handler */
|
||||
protected CallbackHandler callbackHandler;
|
||||
|
||||
/** CAS ticket validator */
|
||||
protected TicketValidator ticketValidator;
|
||||
|
||||
/** CAS service parameter used if no service is provided via TextCallback on login */
|
||||
protected String service;
|
||||
|
||||
/** CAS assertion */
|
||||
protected Assertion assertion;
|
||||
|
||||
/** Login module shared state */
|
||||
protected Map sharedState;
|
||||
|
||||
/** Roles to be added to all authenticated principals by default */
|
||||
protected String[] defaultRoles;
|
||||
|
||||
/** Names of attributes in the CAS assertion that should be used for role data */
|
||||
protected Set roleAttributeNames = new HashSet();
|
||||
|
||||
/** Name of JAAS Group containing caller principal */
|
||||
protected String principalGroupName = DEFAULT_PRINCIPAL_GROUP_NAME;
|
||||
|
||||
/** Name of JAAS Group containing role data */
|
||||
protected String roleGroupName = DEFAULT_ROLE_GROUP_NAME;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the CAS login module.
|
||||
* @param subject Authentication subject.
|
||||
* @param handler Callback handler.
|
||||
* @param state Shared state map.
|
||||
* @param options Login module options. The following are supported:
|
||||
* <ul>
|
||||
* <li>service - CAS service URL used for service ticket validation</li>
|
||||
* <li>ticketValidatorClass - fully-qualified class name of service ticket validator component</li>
|
||||
* <li>defaultRoles (optional) - comma-delimited list of roles to be added to all authenticated principals</li>
|
||||
* <li>roleAttributeNames (optional) - comma-delimited list of attributes in the CAS assertion that contain role data</li>
|
||||
* <li>principalGroupName (optional) - name of JAAS Group containing caller principal</li>
|
||||
* <li>roleGroupName (optional) - name of JAAS Group containing role data</li>
|
||||
* </ul>
|
||||
*/
|
||||
public void initialize(final Subject subject, final CallbackHandler handler, final Map state, final Map options) {
|
||||
this.callbackHandler = handler;
|
||||
this.subject = subject;
|
||||
this.sharedState = state;
|
||||
|
||||
String ticketValidatorClass = null;
|
||||
final Iterator iter = options.keySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
final Object key = iter.next();
|
||||
log.trace("Processing option " + key);
|
||||
if ("service".equals(key)) {
|
||||
this.service = (String) options.get(key);
|
||||
log.debug("Set service=" + this.service);
|
||||
} else if ("ticketValidatorClass".equals(key)) {
|
||||
ticketValidatorClass = (String) options.get(key);
|
||||
log.debug("Set ticketValidatorClass=" + ticketValidatorClass);
|
||||
} else if ("defaultRoles".equals(key)) {
|
||||
final String roles = (String) options.get(key);
|
||||
log.trace("Got defaultRoles value " + roles);
|
||||
this.defaultRoles = roles.split(",\\s*");
|
||||
log.debug("Set defaultRoles=" + this.defaultRoles);
|
||||
} else if ("roleAttributeNames".equals(key)) {
|
||||
final String attrNames = (String) options.get(key);
|
||||
log.trace("Got roleAttributeNames value " + attrNames);
|
||||
final String[] attributes = attrNames.split(",\\s*");
|
||||
this.roleAttributeNames.addAll(Arrays.asList(attributes));
|
||||
log.debug("Set roleAttributeNames=" + this.roleAttributeNames);
|
||||
} else if ("principalGroupName".equals(key)) {
|
||||
this.principalGroupName = (String) options.get(key);
|
||||
log.debug("Set principalGroupName=" + this.principalGroupName);
|
||||
} else if ("roleGroupName".equals(key)) {
|
||||
this.roleGroupName = (String) options.get(key);
|
||||
log.debug("Set roleGroupName=" + this.principalGroupName);
|
||||
}
|
||||
}
|
||||
|
||||
CommonUtils.assertNotNull(ticketValidatorClass, "ticketValidatorClass is required.");
|
||||
this.ticketValidator = createTicketValidator(ticketValidatorClass, options);
|
||||
}
|
||||
|
||||
public boolean login() throws LoginException {
|
||||
log.debug("Performing login.");
|
||||
final NameCallback serviceCallback = new NameCallback("service");
|
||||
final PasswordCallback ticketCallback = new PasswordCallback("ticket", false);
|
||||
try {
|
||||
this.callbackHandler.handle(new Callback[] { ticketCallback, serviceCallback });
|
||||
} catch (final IOException e) {
|
||||
log.info("Login failed due to IO exception in callback handler: " + e);
|
||||
throw new LoginException("IO exception in callback handler: " + e);
|
||||
} catch (final UnsupportedCallbackException e) {
|
||||
log.info("Login failed due to unsupported callback: " + e);
|
||||
throw new LoginException("Callback handler does not support PasswordCallback and TextInputCallback.");
|
||||
}
|
||||
if (ticketCallback.getPassword() != null) {
|
||||
final String ticket = new String(ticketCallback.getPassword());
|
||||
final String service = CommonUtils.isNotBlank(serviceCallback.getName()) ? serviceCallback.getName() : this.service;
|
||||
|
||||
if (CommonUtils.isBlank(service)) {
|
||||
log.info("Login failed because required CAS service parameter not provided.");
|
||||
throw new LoginException("Neither login module nor callback handler provided required service parameter.");
|
||||
}
|
||||
try {
|
||||
log.debug("Attempting ticket validation with service=" + service + " and ticket=" + ticket);
|
||||
this.assertion = this.ticketValidator.validate(ticket, service);
|
||||
} catch (final Exception e) {
|
||||
log.info("Login failed due to CAS ticket validation failure: " + e);
|
||||
throw new LoginException("CAS ticket validation failed: " + e);
|
||||
}
|
||||
} else {
|
||||
log.info("Login failed because callback handler did not provide CAS ticket.");
|
||||
throw new LoginException("Callback handler did not provide CAS ticket.");
|
||||
}
|
||||
log.info("Login succeeded.");
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean abort() throws LoginException {
|
||||
if (this.assertion != null) {
|
||||
this.assertion = null;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean commit() throws LoginException {
|
||||
if (this.assertion != null) {
|
||||
final AssertionPrincipal casPrincipal = new AssertionPrincipal(this.assertion.getPrincipal().getName(), this.assertion);
|
||||
this.subject.getPrincipals().add(casPrincipal);
|
||||
|
||||
// Add group containing principal as sole member
|
||||
// Supports JBoss JAAS use case
|
||||
final Group principalGroup = new SimpleGroup(this.principalGroupName);
|
||||
principalGroup.addMember(casPrincipal);
|
||||
this.subject.getPrincipals().add(principalGroup);
|
||||
|
||||
// Add group principal containing role data
|
||||
final Group roleGroup = new SimpleGroup(this.roleGroupName);
|
||||
for (int i = 0; i < defaultRoles.length; i++) {
|
||||
roleGroup.addMember(new SimplePrincipal(defaultRoles[i]));
|
||||
}
|
||||
final Map attributes = this.assertion.getPrincipal().getAttributes();
|
||||
final Iterator nameIterator = attributes.keySet().iterator();
|
||||
while (nameIterator.hasNext()) {
|
||||
final Object key = nameIterator.next();
|
||||
if (this.roleAttributeNames.contains(key)) {
|
||||
// Attribute value is Object if singular or Collection if plural
|
||||
final Object value = attributes.get(key);
|
||||
if (value instanceof Collection) {
|
||||
final Iterator valueIterator = ((Collection) value).iterator();
|
||||
while (valueIterator.hasNext()) {
|
||||
roleGroup.addMember(new SimplePrincipal(valueIterator.next().toString()));
|
||||
}
|
||||
} else {
|
||||
roleGroup.addMember(new SimplePrincipal(value.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.subject.getPrincipals().add(roleGroup);
|
||||
|
||||
// Place principal name in shared state for downstream JAAS modules (module chaining use case)
|
||||
this.sharedState.put(LOGIN_NAME, casPrincipal.getName());
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Created JAAS subject with principals: " + subject.getPrincipals());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean logout() throws LoginException {
|
||||
if (this.assertion != null) {
|
||||
log.debug("Performing logout.");
|
||||
this.subject.getPrincipals().remove(this.assertion.getPrincipal());
|
||||
// Remove all SimpleGroup principals
|
||||
final Iterator iter = this.subject.getPrincipals().iterator();
|
||||
while (iter.hasNext()) {
|
||||
if (iter.next() instanceof SimpleGroup) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
this.assertion = null;
|
||||
log.info("Logout succeeded.");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a {@link TicketValidator} instance from a class name and map of property name/value pairs.
|
||||
* @param className Fully-qualified name of {@link TicketValidator} concrete class.
|
||||
* @param propertyMap Map of property name/value pairs to set on validator instance.
|
||||
* @return Ticket validator with properties set.
|
||||
*/
|
||||
private TicketValidator createTicketValidator(final String className, final Map propertyMap) {
|
||||
CommonUtils.assertTrue(propertyMap.containsKey("casServerUrlPrefix"), "Required property casServerUrlPrefix not found.");
|
||||
|
||||
final Class validatorClass = ReflectUtils.loadClass(className);
|
||||
final TicketValidator validator = (TicketValidator) ReflectUtils.newInstance(validatorClass, new Object[] {propertyMap.get("casServerUrlPrefix")});
|
||||
|
||||
try {
|
||||
final BeanInfo info = Introspector.getBeanInfo(validatorClass);
|
||||
final Iterator iter = propertyMap.keySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
final String property = (String) iter.next();
|
||||
if (!"casServerUrlPrefix".equals(property)) {
|
||||
log.debug("Attempting to set TicketValidator property " + property);
|
||||
final String value = (String) propertyMap.get(property);
|
||||
final PropertyDescriptor pd = ReflectUtils.getPropertyDescriptor(info, property);
|
||||
if (pd != null) {
|
||||
ReflectUtils.setProperty(property, convertIfNecessary(pd, value), validator, info);
|
||||
log.debug("Set " + property + "=" + value);
|
||||
} else {
|
||||
log.warn("Cannot find property " + property + " on " + className);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (final IntrospectionException e) {
|
||||
throw new RuntimeException("Error getting bean info for " + validatorClass);
|
||||
}
|
||||
|
||||
return validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to do simple type conversion from a string value to the type expected
|
||||
* by the given property.
|
||||
*
|
||||
* Currently only conversion to int, long, and boolean are supported.
|
||||
*
|
||||
* @param pd Property descriptor of target property to set.
|
||||
* @param value Property value as a string.
|
||||
* @return Value converted to type expected by property if a conversion strategy exists.
|
||||
*/
|
||||
private static Object convertIfNecessary(final PropertyDescriptor pd, final String value) {
|
||||
if (String.class.equals(pd.getPropertyType())) {
|
||||
return value;
|
||||
} else if (boolean.class.equals(pd.getPropertyType())) {
|
||||
return Boolean.valueOf(value);
|
||||
} else if (int.class.equals(pd.getPropertyType())) {
|
||||
return new Integer(value);
|
||||
} else if (long.class.equals(pd.getPropertyType())) {
|
||||
return new Long(value);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"No conversion strategy exists for property " + pd.getName()
|
||||
+ " of type " + pd.getPropertyType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2010 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
*/
|
||||
package org.jasig.cas.client.jaas;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.callback.CallbackHandler;
|
||||
import javax.security.auth.callback.NameCallback;
|
||||
import javax.security.auth.callback.PasswordCallback;
|
||||
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||
|
||||
/**
|
||||
* Callback handler that provides the CAS service and ticket to a
|
||||
* {@link NameCallback} and {@link PasswordCallback} respectively,
|
||||
* which meets the requirements of the {@link CasLoginModule} JAAS module.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$
|
||||
* @since 3.1.11
|
||||
*
|
||||
*/
|
||||
public class ServiceAndTicketCallbackHandler implements CallbackHandler {
|
||||
|
||||
/** CAS service URL */
|
||||
private final String service;
|
||||
|
||||
/** CAS service ticket */
|
||||
private final String ticket;
|
||||
|
||||
/**
|
||||
* Creates a new instance with the given service and ticket.
|
||||
*
|
||||
* @param service CAS service URL.
|
||||
* @param ticket CAS service ticket.
|
||||
*/
|
||||
public ServiceAndTicketCallbackHandler(final String service, final String ticket) {
|
||||
this.service = service;
|
||||
this.ticket = ticket;
|
||||
}
|
||||
|
||||
public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException {
|
||||
for (int i = 0; i < callbacks.length; i++) {
|
||||
if (callbacks[i] instanceof NameCallback) {
|
||||
((NameCallback) callbacks[i]).setName(this.service);
|
||||
} else if (callbacks[i] instanceof PasswordCallback) {
|
||||
((PasswordCallback) callbacks[i]).setPassword(this.ticket.toCharArray());
|
||||
} else {
|
||||
throw new UnsupportedCallbackException(callbacks[i], "Callback not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright 2010 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Helper class with reflection utility methods.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$
|
||||
* @since 3.1.11
|
||||
*
|
||||
*/
|
||||
public final class ReflectUtils {
|
||||
|
||||
private ReflectUtils() {
|
||||
// private constructor to prevent instanciation.
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to create a class from a String.
|
||||
* @param className the name of the class to create.
|
||||
* @return the class. CANNOT be NULL.
|
||||
* @throws IllegalArgumentException if the className does not exist.
|
||||
*/
|
||||
public static Class loadClass(final String className) throws IllegalArgumentException {
|
||||
try {
|
||||
return Class.forName(className);
|
||||
} catch (final ClassNotFoundException e) {
|
||||
throw new IllegalArgumentException(className + " class not found.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of the given class by passing the given arguments
|
||||
* to the constructor.
|
||||
* @param className Name of class to be created.
|
||||
* @param args Constructor arguments.
|
||||
* @return New instance of given class.
|
||||
*/
|
||||
public static Object newInstance(final String className, final Object[] args) {
|
||||
try {
|
||||
return newInstance(Class.forName(className), args);
|
||||
} catch (final ClassNotFoundException e) {
|
||||
throw new IllegalArgumentException(className + " not found");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the given class by passing the given arguments
|
||||
* to the constructor.
|
||||
* @param clazz Class of instance to be created.
|
||||
* @param args Constructor arguments.
|
||||
* @return New instance of given class.
|
||||
*/
|
||||
public static Object newInstance(final Class clazz, final Object[] args) {
|
||||
final Class[] argClasses = new Class[args.length];
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
argClasses[i] = args[i].getClass();
|
||||
}
|
||||
try {
|
||||
return clazz.getConstructor(argClasses).newInstance(args);
|
||||
} catch (final Exception e) {
|
||||
throw new IllegalArgumentException("Error creating new instance of " + clazz, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property descriptor for the named property on the given class.
|
||||
* @param clazz Class to which property belongs.
|
||||
* @param propertyName Name of property.
|
||||
* @return Property descriptor for given property or null if no property with given
|
||||
* name exists in given class.
|
||||
*/
|
||||
public static PropertyDescriptor getPropertyDescriptor(final Class clazz, final String propertyName) {
|
||||
try {
|
||||
return getPropertyDescriptor(Introspector.getBeanInfo(clazz), propertyName);
|
||||
} catch (final IntrospectionException e) {
|
||||
throw new RuntimeException("Failed getting bean info for " + clazz, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the property descriptor for the named property from the bean info describing
|
||||
* a particular class to which property belongs.
|
||||
* @param info Bean info describing class to which property belongs.
|
||||
* @param propertyName Name of property.
|
||||
* @return Property descriptor for given property or null if no property with given
|
||||
* name exists.
|
||||
*/
|
||||
public static PropertyDescriptor getPropertyDescriptor(final BeanInfo info, final String propertyName) {
|
||||
for (int i = 0; i < info.getPropertyDescriptors().length; i++) {
|
||||
final PropertyDescriptor pd = info.getPropertyDescriptors()[i];
|
||||
if (pd.getName().equals(propertyName)) {
|
||||
return pd;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given property on the target javabean using bean instrospection.
|
||||
* @param propertyName Property to set.
|
||||
* @param value Property value to set.
|
||||
* @param target Target java bean on which to set property.
|
||||
*/
|
||||
public static void setProperty(final String propertyName, final Object value, final Object target) {
|
||||
try {
|
||||
setProperty(propertyName, value, target, Introspector.getBeanInfo(target.getClass()));
|
||||
} catch (final IntrospectionException e) {
|
||||
throw new RuntimeException("Failed getting bean info on target javabean " + target, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given property on the target javabean using bean instrospection.
|
||||
* @param propertyName Property to set.
|
||||
* @param value Property value to set.
|
||||
* @param target Target javabean on which to set property.
|
||||
* @param info BeanInfo describing the target javabean.
|
||||
*/
|
||||
public static void setProperty(final String propertyName, final Object value, final Object target, final BeanInfo info) {
|
||||
try {
|
||||
final PropertyDescriptor pd = getPropertyDescriptor(info, propertyName);
|
||||
pd.getWriteMethod().invoke(target, new Object[] { value });
|
||||
} catch (final InvocationTargetException e) {
|
||||
throw new RuntimeException("Error setting property " + propertyName, e.getCause());
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException("Error setting property " + propertyName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package org.jasig.cas.client;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
|
|
@ -16,35 +15,28 @@ public final class PublicTestHttpServer extends Thread {
|
|||
|
||||
public byte[] content;
|
||||
|
||||
private byte[] header;
|
||||
private final byte[] header;
|
||||
|
||||
private int port = 80;
|
||||
private final int port;
|
||||
|
||||
public String encoding;
|
||||
public final String encoding;
|
||||
|
||||
private PublicTestHttpServer(String data, String encoding, String MIMEType,
|
||||
int port) throws UnsupportedEncodingException {
|
||||
private PublicTestHttpServer(String data, String encoding, String MIMEType, int port) throws UnsupportedEncodingException {
|
||||
this(data.getBytes(encoding), encoding, MIMEType, port);
|
||||
}
|
||||
|
||||
private PublicTestHttpServer(byte[] data, String encoding, String MIMEType,
|
||||
int port) throws UnsupportedEncodingException {
|
||||
|
||||
private PublicTestHttpServer(byte[] data, String encoding, String MIMEType, int port) throws UnsupportedEncodingException {
|
||||
this.content = data;
|
||||
this.port = port;
|
||||
this.encoding = encoding;
|
||||
String header = "HTTP/1.0 200 OK\r\n" + "Server: OneFile 1.0\r\n"
|
||||
// + "Content-length: " + this.content.length + "\r\n"
|
||||
+ "Content-type: " + MIMEType + "\r\n\r\n";
|
||||
String header = "HTTP/1.0 200 OK\r\n" + "Server: OneFile 1.0\r\n" + "Content-type: " + MIMEType + "\r\n\r\n";
|
||||
this.header = header.getBytes("ASCII");
|
||||
|
||||
}
|
||||
|
||||
public static synchronized PublicTestHttpServer instance() {
|
||||
if (httpServer == null) {
|
||||
try {
|
||||
httpServer = new PublicTestHttpServer("test", "ASCII",
|
||||
"text/plain", 8085);
|
||||
httpServer = new PublicTestHttpServer("test", "ASCII", "text/plain", 8085);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
@ -59,19 +51,16 @@ public final class PublicTestHttpServer extends Thread {
|
|||
|
||||
try {
|
||||
ServerSocket server = new ServerSocket(this.port);
|
||||
System.out.println("Accepting connections on port "
|
||||
+ server.getLocalPort());
|
||||
System.out.println("Accepting connections on port " + server.getLocalPort());
|
||||
while (true) {
|
||||
|
||||
Socket connection = null;
|
||||
try {
|
||||
connection = server.accept();
|
||||
OutputStream out = new BufferedOutputStream(connection
|
||||
.getOutputStream());
|
||||
InputStream in = new BufferedInputStream(connection
|
||||
.getInputStream());
|
||||
final OutputStream out = new BufferedOutputStream(connection.getOutputStream());
|
||||
final InputStream in = new BufferedInputStream(connection.getInputStream());
|
||||
// read the first line only; that's all we need
|
||||
StringBuffer request = new StringBuffer(80);
|
||||
final StringBuffer request = new StringBuffer(80);
|
||||
while (true) {
|
||||
int c = in.read();
|
||||
if (c == '\r' || c == '\n' || c == -1)
|
||||
|
|
@ -89,7 +78,7 @@ public final class PublicTestHttpServer extends Thread {
|
|||
out.write(this.content);
|
||||
out.flush();
|
||||
} // end try
|
||||
catch (IOException e) {
|
||||
catch (final IOException e) {
|
||||
// nothing to do with this IOException
|
||||
} finally {
|
||||
if (connection != null)
|
||||
|
|
@ -98,7 +87,7 @@ public final class PublicTestHttpServer extends Thread {
|
|||
|
||||
} // end while
|
||||
} // end try
|
||||
catch (IOException e) {
|
||||
catch (final IOException e) {
|
||||
System.err.println("Could not start server. Port Occupied");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright 2010 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
*/
|
||||
package org.jasig.cas.client;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.Collections;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.jasig.cas.client.authentication.AttributePrincipalImpl;
|
||||
import org.jasig.cas.client.authentication.SimpleGroup;
|
||||
import org.jasig.cas.client.authentication.SimplePrincipal;
|
||||
import org.jasig.cas.client.jaas.AssertionPrincipal;
|
||||
import org.jasig.cas.client.validation.AssertionImpl;
|
||||
|
||||
/**
|
||||
* Confirms serialization support for classes intended for session storage or
|
||||
* other potential serialization use cases.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$
|
||||
* @since 3.1.11
|
||||
*
|
||||
*/
|
||||
public class SerializationTests extends TestCase {
|
||||
|
||||
public void testSerializeDeserialize() throws Exception {
|
||||
final Object[] subjects = getTestSubjects();
|
||||
for (int i = 0; i < subjects.length; i++) {
|
||||
final ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
|
||||
final ObjectOutputStream out = new ObjectOutputStream(byteOut);
|
||||
try {
|
||||
out.writeObject(subjects[i]);
|
||||
} catch (Exception e) {
|
||||
Assert.fail("Serialization failed for " + subjects[i]);
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
|
||||
final ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
|
||||
final ObjectInputStream in = new ObjectInputStream(byteIn);
|
||||
try {
|
||||
Assert.assertEquals(subjects[i], in.readObject());
|
||||
} catch (Exception e) {
|
||||
Assert.fail("Deserialization failed for " + subjects[i]);
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object[] getTestSubjects() {
|
||||
final SimplePrincipal simplePrincipal = new SimplePrincipal("simple");
|
||||
final SimpleGroup simpleGroup = new SimpleGroup("group");
|
||||
final AttributePrincipalImpl attributePrincipal =
|
||||
new AttributePrincipalImpl("attr", Collections.singletonMap("LOA", "3"));
|
||||
final AssertionPrincipal assertionPrincipal = new AssertionPrincipal(
|
||||
"assertion",
|
||||
new AssertionImpl(attributePrincipal, Collections.singletonMap("authenticationMethod", "username")));
|
||||
|
||||
return new Object[] {
|
||||
simplePrincipal,
|
||||
simpleGroup,
|
||||
attributePrincipal,
|
||||
assertionPrincipal,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2010 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
*/
|
||||
package org.jasig.cas.client.jaas;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.security.acl.Group;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.login.LoginException;
|
||||
|
||||
import org.jasig.cas.client.PublicTestHttpServer;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Unit test for {@link CasLoginModule} class.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$
|
||||
*
|
||||
*/
|
||||
public class CasLoginModuleTests extends TestCase {
|
||||
private static final String CONST_CAS_SERVER_URL = "http://localhost:8085/";
|
||||
|
||||
private CasLoginModule module;
|
||||
|
||||
private Subject subject;
|
||||
|
||||
/** {@inheritDoc} */
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
module = new CasLoginModule();
|
||||
subject = new Subject();
|
||||
final Map options = new HashMap();
|
||||
options.put("service", "https://service.example.com/webapp");
|
||||
options.put("ticketValidatorClass", "org.jasig.cas.client.validation.Cas20ServiceTicketValidator");
|
||||
options.put("casServerUrlPrefix", CONST_CAS_SERVER_URL);
|
||||
options.put("proxyCallbackUrl", "https://service.example.com/webapp/proxy");
|
||||
options.put("renew", "true");
|
||||
options.put("defaultRoles", "ADMIN");
|
||||
options.put("principalGroupName", "CallerPrincipal");
|
||||
options.put("roleGroupName", "Roles");
|
||||
module.initialize(
|
||||
subject,
|
||||
new ServiceAndTicketCallbackHandler("myService", "myTicket"),
|
||||
new HashMap(),
|
||||
options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test JAAS login success.
|
||||
*/
|
||||
public void testLoginSuccess() throws Exception {
|
||||
final String USERNAME = "username";
|
||||
final String RESPONSE = "<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>"
|
||||
+ "<cas:authenticationSuccess><cas:user>"
|
||||
+ USERNAME
|
||||
+ "</cas:user></cas:authenticationSuccess></cas:serviceResponse>";
|
||||
PublicTestHttpServer.instance().content = RESPONSE
|
||||
.getBytes(PublicTestHttpServer.instance().encoding);
|
||||
module.login();
|
||||
module.commit();
|
||||
assertEquals(this.subject.getPrincipals().size(), 3);
|
||||
assertTrue(hasPrincipalName(this.subject, AssertionPrincipal.class, USERNAME));
|
||||
assertTrue(hasPrincipalName(this.subject, Group.class, "CallerPrincipal"));
|
||||
assertTrue(hasPrincipalName(this.subject, Group.class, "Roles"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test JAAS login failure.
|
||||
*/
|
||||
public void testLoginFailure() throws Exception {
|
||||
final String RESPONSE = "<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'><cas:authenticationFailure code=\"INVALID_TICKET\">Ticket ST-1856339-aA5Yuvrxzpv8Tau1cYQ7 not recognized</cas:authenticationFailure></cas:serviceResponse>";
|
||||
PublicTestHttpServer.instance().content = RESPONSE.getBytes(PublicTestHttpServer.instance().encoding);
|
||||
try {
|
||||
module.login();
|
||||
fail("Login did not throw LoginException as expected.");
|
||||
} catch (Exception e) {
|
||||
assertTrue(e instanceof LoginException);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasPrincipalName(final Subject subject, final Class principalClass, final String name) {
|
||||
final Set principals = subject.getPrincipals(principalClass);
|
||||
final Iterator iter = principals.iterator();
|
||||
while (iter.hasNext()) {
|
||||
final Principal p = (Principal) iter.next();
|
||||
if (p.getName().equals(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright 2010 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
*/
|
||||
package org.jasig.cas.client.util;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Unit test for {@link ReflectUtils} class.
|
||||
*
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$
|
||||
* @since 3.1.11
|
||||
*
|
||||
*/
|
||||
public class ReflectUtilsTests extends TestCase {
|
||||
/**
|
||||
* Test method for {@link org.jasig.cas.client.util.ReflectUtils#newInstance(java.lang.String, java.lang.Object[])}.
|
||||
*/
|
||||
public void testNewInstanceStringObjectArray() {
|
||||
final Object result = ReflectUtils.newInstance(
|
||||
"org.jasig.cas.client.validation.Cas10TicketValidator",
|
||||
new Object[] {"https://localhost/cas"} );
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link org.jasig.cas.client.util.ReflectUtils#setProperty(java.lang.String, java.lang.Object, java.lang.Object)}.
|
||||
*/
|
||||
public void testSetPropertyStringObjectObject() {
|
||||
final TestBean bean = new TestBean();
|
||||
|
||||
ReflectUtils.setProperty("count", new Integer(30000), bean);
|
||||
assertEquals(30000, bean.getCount());
|
||||
|
||||
ReflectUtils.setProperty("name", "bob", bean);
|
||||
assertEquals("bob", bean.getName());
|
||||
|
||||
ReflectUtils.setProperty("flag", Boolean.TRUE, bean);
|
||||
assertTrue(bean.isFlag());
|
||||
}
|
||||
|
||||
static class TestBean {
|
||||
private int count;
|
||||
private boolean flag;
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* @return the count
|
||||
*/
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param count the count to set
|
||||
*/
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name the name to set
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the flag
|
||||
*/
|
||||
public boolean isFlag() {
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param flag the flag to set
|
||||
*/
|
||||
public void setFlag(boolean flag) {
|
||||
this.flag = flag;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.jasig.cas</groupId>
|
||||
<version>3.1.10</version>
|
||||
<version>3.1.11-SNAPSHOT</version>
|
||||
<artifactId>cas-client</artifactId>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
|
@ -10,50 +10,7 @@
|
|||
<artifactId>cas-client-integration-atlassian</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>JA-SIG CAS Client for Java - Atlassian Integration</name>
|
||||
<build>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<testSourceDirectory>src/test/java</testSourceDirectory>
|
||||
<testResources>
|
||||
<testResource>
|
||||
<directory>src/test/resources</directory>
|
||||
<filtering>false</filtering>
|
||||
</testResource>
|
||||
</testResources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.4</source>
|
||||
<target>1.4</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Tests*</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-clover-plugin</artifactId>
|
||||
<configuration>
|
||||
<licenseLocation>${basedir}/src/test/clover/clover.license</licenseLocation>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>pre-site</phase>
|
||||
<goals>
|
||||
<goal>instrument</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<artifactId>atlassian-seraph</artifactId>
|
||||
|
|
@ -554,7 +511,8 @@
|
|||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
<!--
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>atlassian</id>
|
||||
|
|
@ -562,4 +520,5 @@
|
|||
<url>http://repository.atlassian.com/maven2/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
-->
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.jasig.cas</groupId>
|
||||
<version>3.1.11-SNAPSHOT</version>
|
||||
<artifactId>cas-client</artifactId>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.jasig.cas</groupId>
|
||||
<artifactId>cas-client-integration-jboss</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>JA-SIG CAS Client for Java - JBoss Integration</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jasig.cas</groupId>
|
||||
<artifactId>cas-client-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.jbossas</groupId>
|
||||
<artifactId>jboss-as-tomcat</artifactId>
|
||||
<version>${jboss.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<!--
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jboss</id>
|
||||
<name>JBoss Repository</name>
|
||||
<url>http://repository.jboss.com/maven2/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
-->
|
||||
|
||||
<properties>
|
||||
<jboss.version>5.1.0.GA</jboss.version>
|
||||
</properties>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2010 The JA-SIG Collaborative. All rights reserved. See license
|
||||
* distributed with this file and available online at
|
||||
* http://www.ja-sig.org/products/cas/overview/license/index.html
|
||||
*/
|
||||
package org.jasig.cas.client.jboss.authentication;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.jasig.cas.client.jaas.AssertionPrincipal;
|
||||
import org.jasig.cas.client.util.AbstractCasFilter;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
import org.jboss.web.tomcat.security.login.WebAuthentication;
|
||||
|
||||
/**
|
||||
* This servlet filter performs a programmatic JAAS login using the JBoss
|
||||
* <a href="http://community.jboss.org/wiki/WebAuthentication">WebAuthentication</a> class.
|
||||
* The filter executes when it receives a CAS ticket and expects the
|
||||
* {@link org.jasig.cas.client.jaas.CasLoginModule} JAAS module to perform the CAS
|
||||
* ticket validation in order to produce an {@link AssertionPrincipal} from which
|
||||
* the CAS assertion is obtained and inserted into the session to enable SSO.
|
||||
* <p>
|
||||
* If a <code>service</code> init-param is specified for this filter, it supersedes
|
||||
* the service defined for the {@link org.jasig.cas.client.jaas.CasLoginModule}.
|
||||
*
|
||||
* @author Daniel Fisher
|
||||
* @author Marvin S. Addison
|
||||
* @version $Revision$
|
||||
* @since 3.1.11
|
||||
*/
|
||||
public final class WebAuthenticationFilter extends AbstractCasFilter {
|
||||
|
||||
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain chain) throws IOException, ServletException {
|
||||
final HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
final HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
final HttpSession session = request.getSession();
|
||||
final String ticket = CommonUtils.safeGetParameter(request, getArtifactParameterName());
|
||||
|
||||
if (session != null && session.getAttribute(CONST_CAS_ASSERTION) == null && ticket != null) {
|
||||
try {
|
||||
final String service = constructServiceUrl(request, response);
|
||||
log.debug("Attempting CAS ticket validation with service=" + service + " and ticket=" + ticket);
|
||||
if (!new WebAuthentication().login(service, ticket)) {
|
||||
log.debug("JBoss Web authentication failed.");
|
||||
throw new GeneralSecurityException("JBoss Web authentication failed.");
|
||||
}
|
||||
if (request.getUserPrincipal() instanceof AssertionPrincipal) {
|
||||
final AssertionPrincipal principal = (AssertionPrincipal) request.getUserPrincipal();
|
||||
log.debug("Installing CAS assertion into session.");
|
||||
session.setAttribute(CONST_CAS_ASSERTION, principal.getAssertion());
|
||||
} else {
|
||||
log.debug("Aborting -- principal is not of type AssertionPrincipal");
|
||||
throw new GeneralSecurityException("JBoss Web authentication did not produce CAS AssertionPrincipal.");
|
||||
}
|
||||
} catch (final GeneralSecurityException e) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
|
||||
}
|
||||
} else if (session != null && request.getUserPrincipal() == null) {
|
||||
// There is evidence that in some cases the principal can disappear
|
||||
// in JBoss despite a valid session.
|
||||
// This block forces consistency between principal and assertion.
|
||||
log.info("User principal not found. Removing CAS assertion from session to force reauthentication.");
|
||||
session.removeAttribute(CONST_CAS_ASSERTION);
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>cas-client</artifactId>
|
||||
<groupId>org.jasig.cas</groupId>
|
||||
<version>3.1.10</version>
|
||||
<version>3.1.11-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<name>Jasig CAS Client for Java - Distributed Proxy Storage Support: EhCache</name>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>cas-client</artifactId>
|
||||
<groupId>org.jasig.cas</groupId>
|
||||
<version>3.1.10</version>
|
||||
<version>3.1.11-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
<dependency>
|
||||
<groupId>spy</groupId>
|
||||
<artifactId>memcached</artifactId>
|
||||
<version>2.4.2</version>
|
||||
<version>2.5</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
|
|
|||
52
pom.xml
52
pom.xml
|
|
@ -1,7 +1,7 @@
|
|||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.jasig.cas</groupId>
|
||||
<version>3.1.10</version>
|
||||
<version>3.1.11-SNAPSHOT</version>
|
||||
<artifactId>cas-client</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>JA-SIG CAS Client for Java</name>
|
||||
|
|
@ -20,28 +20,7 @@
|
|||
<url>https://www.ja-sig.org/svn/cas-clients/java-client</url>
|
||||
</scm>
|
||||
<inceptionYear>2006</inceptionYear>
|
||||
<mailingLists>
|
||||
<mailingList>
|
||||
<name>CAS Community Discussion List</name>
|
||||
<subscribe>http://tp.its.yale.edu/mailman/listinfo/cas</subscribe>
|
||||
<unsubscribe>http://tp.its.yale.edu/mailman/listinfo/cas</unsubscribe>
|
||||
<post>cas@tp.its.yale.edu</post>
|
||||
<archive>http://tp.its.yale.edu/pipermail/cas/</archive>
|
||||
<otherArchives>
|
||||
<otherArchive>http://news.gmane.org/gmane.comp.java.jasig.cas.user</otherArchive>
|
||||
</otherArchives>
|
||||
</mailingList>
|
||||
<mailingList>
|
||||
<name>CAS Developers Discussion List</name>
|
||||
<subscribe>http://tp.its.yale.edu/mailman/listinfo/cas-dev</subscribe>
|
||||
<unsubscribe>http://tp.its.yale.edu/mailman/listinfo/cas-dev</unsubscribe>
|
||||
<post>cas-dev@tp.its.yale.edu</post>
|
||||
<archive>http://tp.its.yale.edu/pipermail/cas-dev/</archive>
|
||||
<otherArchives>
|
||||
<otherArchive>http://news.gmane.org/gmane.comp.java.jasig.cas.devel</otherArchive>
|
||||
</otherArchives>
|
||||
</mailingList>
|
||||
</mailingLists>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>battags</id>
|
||||
|
|
@ -66,7 +45,7 @@
|
|||
</licenses>
|
||||
<organization>
|
||||
<name>JA-SIG</name>
|
||||
<url>http://www.ja-sig.org</url>
|
||||
<url>http://www.jasig.org</url>
|
||||
</organization>
|
||||
<build>
|
||||
<plugins>
|
||||
|
|
@ -88,6 +67,30 @@
|
|||
<target>1.4</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Tests*</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-clover-plugin</artifactId>
|
||||
<configuration>
|
||||
<licenseLocation>${basedir}/src/test/clover/clover.license</licenseLocation>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>pre-site</phase>
|
||||
<goals>
|
||||
<goal>instrument</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
|
@ -129,6 +132,7 @@
|
|||
<modules>
|
||||
<module>cas-client-core</module>
|
||||
<module>cas-client-integration-atlassian</module>
|
||||
<module>cas-client-integration-jboss</module>
|
||||
<module>cas-client-support-distributed-ehcache</module>
|
||||
<module>cas-client-support-distributed-memcached</module>
|
||||
</modules>
|
||||
|
|
|
|||
Loading…
Reference in New Issue