Skip to content

Commit

Permalink
Merge pull request apereo#446 from Unicon/CAS-1475
Browse files Browse the repository at this point in the history
CAS-1475: Refactoring of GoogleApps altUsername config
  • Loading branch information
mmoayyed committed Sep 2, 2014
2 parents a6b7cfe + 9ca2e30 commit d77c6a9
Show file tree
Hide file tree
Showing 27 changed files with 798 additions and 279 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,9 @@
*/
package org.jasig.cas.services.web.support;

import java.util.Set;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

import org.apache.commons.lang3.StringUtils;
import org.jasig.cas.services.RegisteredService;
import org.jasig.cas.services.ServicesManager;
import org.jasig.services.persondir.IPersonAttributeDao;
Expand Down Expand Up @@ -102,16 +99,6 @@ public void validate(final Object o, final Errors errors) {
errors.rejectValue("description",
"registeredService.description.length", null);
}

if (!StringUtils.isBlank(r.getUsernameAttribute()) && !r.isAnonymousAccess()) {
final Set<String> availableAttributes = this.personAttributeDao.getPossibleUserAttributeNames();
if (availableAttributes != null) {
if (!availableAttributes.contains(r.getUsernameAttribute())) {
errors.rejectValue("usernameAttribute", "registeredService.usernameAttribute.notAvailable",
"This attribute is not available from configured user attribute sources.");
}
}
}
}

public void setMaxDescriptionLength(final int maxLength) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.services.RegisteredService;
import org.jasig.cas.services.RegisteredServiceImpl;
import org.jasig.cas.services.ReturnAllAttributeReleasePolicy;
import org.jasig.cas.services.ServicesManager;
import org.jasig.services.persondir.IPersonAttributeDao;
import org.jasig.services.persondir.support.StubPersonAttributeDao;
Expand Down Expand Up @@ -102,24 +101,7 @@ public void testMaxLength() {

assertEquals(1, exception.getErrorCount());
}

@Test
public void testUsernameAttributeWithAllFilteringPolicy() {
final RegisteredServiceImpl impl = new RegisteredServiceImpl();
impl.setServiceId("test");
impl.setDescription("fasdfdsafsafsafdsa");
impl.setUsernameAttribute("k3");
impl.setAttributeReleasePolicy(new ReturnAllAttributeReleasePolicy());

final BindException exception = new BindException(impl, "registeredService");

final RegisteredServiceValidator validator = getValidator(false);
validator.setMaxDescriptionLength(100);
validator.validate(impl, exception);

assertEquals(0, exception.getErrorCount());
}

protected void checkId(final boolean exists, final int expectedErrors, final String name) {
final Validator validator = getValidator(exists);
final RegisteredServiceImpl impl = new RegisteredServiceImpl();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
package org.jasig.cas;

import com.github.inspektr.audit.annotation.Audit;
import org.apache.commons.lang3.StringUtils;
import org.jasig.cas.authentication.AcceptAnyAuthenticationPolicyFactory;
import org.jasig.cas.authentication.Authentication;
import org.jasig.cas.authentication.AuthenticationBuilder;
Expand All @@ -32,7 +31,6 @@
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.logout.LogoutManager;
import org.jasig.cas.logout.LogoutRequest;
Expand Down Expand Up @@ -142,10 +140,6 @@ public final class CentralAuthenticationServiceImpl implements CentralAuthentica
@NotNull
private ExpirationPolicy serviceTicketExpirationPolicy;

/** Encoder to generate PseudoIds. */
@NotNull
private PersistentIdGenerator persistentIdGenerator = new ShibbolethCompatiblePersistentIdGenerator();

/**
* Authentication policy that uses a service context to produce stateful security policies to apply when
* authenticating credentials.
Expand Down Expand Up @@ -387,7 +381,7 @@ public Assertion validateServiceTicket(final String serviceTicketId, final Servi
final ServiceTicket serviceTicket = this.serviceTicketRegistry.getTicket(serviceTicketId, ServiceTicket.class);

if (serviceTicket == null) {
logger.info("ServiceTicket [{}] does not exist.", serviceTicketId);
logger.info("Service ticket [{}] does not exist.", serviceTicketId);
throw new InvalidTicketException(serviceTicketId);
}

Expand All @@ -403,7 +397,7 @@ public Assertion validateServiceTicket(final String serviceTicketId, final Servi
}

if (!serviceTicket.isValidFor(service)) {
logger.error("ServiceTicket [{}] with service [{}] does not match supplied service [{}]",
logger.error("Service ticket [{}] with service [{}] does not match supplied service [{}]",
serviceTicketId, serviceTicket.getService().getId(), service);
throw new TicketValidationException(serviceTicket.getService());
}
Expand All @@ -420,8 +414,8 @@ public Assertion validateServiceTicket(final String serviceTicketId, final Servi
@SuppressWarnings("unchecked")
final Map<String, Object> attributesToRelease = attributePolicy != null
? attributePolicy.getAttributes(principal) : Collections.EMPTY_MAP;
final String principalId = determinePrincipalIdForRegisteredService(principal, registeredService, serviceTicket);

final String principalId = registeredService.getUsernameAttributeProvider().resolveUsername(principal, service);
final Principal modifiedPrincipal = new SimplePrincipal(principalId, attributesToRelease);
final AuthenticationBuilder builder = AuthenticationBuilder.newInstance(authentication);
builder.setPrincipal(modifiedPrincipal);
Expand All @@ -437,59 +431,6 @@ public Assertion validateServiceTicket(final String serviceTicketId, final Servi
}
}
}

/**
* 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 {@link org.jasig.cas.services.RegisteredService#getUsernameAttribute()} is blank, then the default
* principal id is returned.</li>
* <li>If the username attribute is available as part of the principal's attributes,
* 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 (principal.getAttributes().containsKey(serviceUsernameAttribute)) {
principalId = principal.getAttributes().get(serviceUsernameAttribute).toString();
} else {
principalId = principal.getId();
final Object[] errorLogParameters = new Object[] {
principalId,
registeredService.getUsernameAttribute(),
principal.getAttributes(),
registeredService.getServiceId(),
principalId };
logger.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);
}

}

logger.debug("Principal id to return for service [{}] is [{}]. The default principal id is [{}].",
new Object[]{registeredService.getName(), principal.getId(), principalId});
return principalId;
}

@Audit(
action="TICKET_GRANTING_TICKET",
Expand All @@ -514,11 +455,6 @@ public String createTicketGrantingTicket(final Credential... credentials)
return ticketGrantingTicket.getId();
}

public void setPersistentIdGenerator(
final PersistentIdGenerator persistentIdGenerator) {
this.persistentIdGenerator = persistentIdGenerator;
}

public void setServiceContextAuthenticationPolicyFactory(final ContextualAuthenticationPolicyFactory<ServiceContext> policy) {
this.serviceContextAuthenticationPolicyFactory = policy;
}
Expand All @@ -537,6 +473,18 @@ public void setServiceTicketExpirationPolicy(final ExpirationPolicy serviceTicke
this.serviceTicketExpirationPolicy = serviceTicketExpirationPolicy;
}

/**
* @deprecated
* Sets persistent id generator.
*
* @param persistentIdGenerator the persistent id generator
*/
@Deprecated
public void setPersistentIdGenerator(final PersistentIdGenerator persistentIdGenerator) {
logger.warn("setPersistentIdGenerator() is deprecated and no longer available. Consider "
+ "configuring the an attribute provider for service definitions.");
}

/**
* Gets the authentication satisfied by policy.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,18 @@
package org.jasig.cas.authentication.principal;

/**
* Generates a unique consistant Id based on the principal, a service, and some
* algorithm.
* Generates a unique consistent Id based on the principal.
*
* @author Scott Battaglia
* @since 3.1
*/
public interface PersistentIdGenerator {

/**
* Generates a PersistentId based on some algorithm plus the principal and
* service.
* Generates a PersistentId based on some algorithm plus the principal.
*
* @param principal the principal to generate the id for.
* @param service the service to generate the id for.
* @param service the service for which the id may be generated.
* @return the generated persistent id.
*/
String generate(Principal principal, Service service);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class PersonDirectoryPrincipalResolver implements PrincipalResolver {
@NotNull
private IPersonAttributeDao attributeRepository = new StubPersonAttributeDao(new HashMap<String, List<Object>>());

/** Optional prinicpal attribute name. */
/** Optional principal attribute name. */
private String principalAttributeName;

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,63 @@
*/
package org.jasig.cas.authentication.principal;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.constraints.NotNull;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
* Generates PersistentIds based on the Shibboleth algorithm.
*
* @author Scott Battaglia
* @since 3.1
*/
public final class ShibbolethCompatiblePersistentIdGenerator implements
PersistentIdGenerator {
public final class ShibbolethCompatiblePersistentIdGenerator implements PersistentIdGenerator {

/** Log instance. */
private static final Logger LOGGER = LoggerFactory.getLogger(ShibbolethCompatiblePersistentIdGenerator.class);

private static final byte CONST_SEPARATOR = (byte) '!';

@NotNull
private byte[] salt;

/**
* Instantiates a new shibboleth compatible persistent id generator.
* The salt is initialized to a random 16-digit alphanumeric string.
* The generated id is pseudo-anonymous which allows it to be continually uniquely
* identified by for a particular service.
*/
public ShibbolethCompatiblePersistentIdGenerator() {
this.salt = RandomStringUtils.randomAlphanumeric(16).getBytes();
}

/**
* Instantiates a new shibboleth compatible persistent id generator.
*
* @param salt the the salt
*/
public ShibbolethCompatiblePersistentIdGenerator(@NotNull final String salt) {
this.salt = salt.getBytes();
}

/**
* @deprecated As of 4.1.
* Sets salt.
*
* @param salt the salt
*/
@Deprecated
public void setSalt(final String salt) {
this.salt = salt.getBytes();
LOGGER.warn("setSalt() is deprecated and will be removed. Use the constructor instead.");
}

@Override
public String generate(final Principal principal, final Service service) {
try {
Expand All @@ -55,7 +91,28 @@ public String generate(final Principal principal, final Service service) {
}
}

public void setSalt(final String salt) {
this.salt = salt.getBytes();

@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj.getClass() != getClass()) {
return false;
}
final ShibbolethCompatiblePersistentIdGenerator rhs = (ShibbolethCompatiblePersistentIdGenerator) obj;
return new EqualsBuilder()
.append(this.salt, rhs.salt)
.isEquals();
}

@Override
public int hashCode() {
return new HashCodeBuilder()
.append(salt)
.toHashCode();
}
}
Loading

0 comments on commit d77c6a9

Please sign in to comment.