net.unicon.cas.mfa.AbstractMultiFactorAuthenticationProtocolValidationSpecification Maven / Gradle / Ivy
Show all versions of cas-mfa-java Show documentation
package net.unicon.cas.mfa;
import net.unicon.cas.mfa.ticket.UnacceptableMultiFactorAuthenticationMethodException;
import net.unicon.cas.mfa.ticket.UnrecognizedMultiFactorAuthenticationMethodException;
import net.unicon.cas.mfa.util.MultiFactorUtils;
import org.apache.commons.lang.StringUtils;
import org.jasig.cas.authentication.Authentication;
import org.jasig.cas.validation.Assertion;
import org.jasig.cas.validation.Cas20ProtocolValidationSpecification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Set;
/**
* Validate the requested protocol spec, primarily based on the requested authentication method.
* @author Misagh Moayyed
* @see net.unicon.cas.mfa.web.MultiFactorServiceValidateController
*/
public abstract class AbstractMultiFactorAuthenticationProtocolValidationSpecification extends Cas20ProtocolValidationSpecification {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private String authenticationMethod = null;
private boolean validateProxyAuthenticationRequests = false;
/**
* Constructor to spin up the validation spec instance.
* No restrictions on the requested authentication method.
*
* @param validateProxyAuthenticationRequests flag
*/
public AbstractMultiFactorAuthenticationProtocolValidationSpecification(final boolean validateProxyAuthenticationRequests) {
super();
this.validateProxyAuthenticationRequests = validateProxyAuthenticationRequests;
}
/**
* Constructor to spin up the validation spec instance, requiring a particular
* authentication method.
* @param authnMethod required authentication method to match against.
*/
public AbstractMultiFactorAuthenticationProtocolValidationSpecification(final String authnMethod) {
this.authenticationMethod = authnMethod;
}
public final void setAuthenticationMethod(final String authnMethod) {
this.authenticationMethod = authnMethod;
}
public final String getAuthenticationMethod() {
return this.authenticationMethod;
}
/**
* {@inheritDoc}
* Validate the requested authentication method for this validation request.
* If the produced assertion cannot satisfy the authentication method requested,
* an instance of {@link UnacceptableMultiFactorAuthenticationMethodException} will be thrown.
* If the authentication method used by the assertion is not blank, but does not match
* the requested authentication method, an instance of {@link UnrecognizedMultiFactorAuthenticationMethodException}
* will be thrown.
*
*
Note: The current {@link #isSatisfiedByInternal(Assertion)} method signature
* only allows for returning of a boolean value indicating whether the assertion
* can satisfy the requested protocol. This is not sufficient to fully explain the context
* of a validation failure, as in the case of multifactor authentication, whether
* the authentication method is unrecognized or unacceptable. In order to accommodate this,
* and rather than changing the method signature to return more than just a boolean, the
* implementation opts to throwing specific exceptions in order to indicate the context
* of the failure. Exceptions are unchecked, yet are expected to be caught by the caller
* in order to digest the failure.
*
* @see UnacceptableMultiFactorAuthenticationMethodException
* @see UnrecognizedMultiFactorAuthenticationMethodException
*/
@Override
protected final boolean isSatisfiedByInternal(final Assertion assertion) {
final Authentication authentication = MultiFactorUtils.getAuthenticationFromAssertion(assertion);
if (authentication != null) {
final Set previouslyAchievedAuthenticationMethods =
MultiFactorUtils.getSatisfiedAuthenticationMethods(authentication);
if (!StringUtils.isBlank(getAuthenticationMethod())) {
if (previouslyAchievedAuthenticationMethods.size() == 0) {
final String msg = String.format("Requested authentication method [%s] is not available", getAuthenticationMethod());
logger.debug(msg);
throw new UnacceptableMultiFactorAuthenticationMethodException("UNACCEPTABLE_AUTHENTICATION_METHOD", msg,
getAuthenticationMethod());
}
if (!previouslyAchievedAuthenticationMethods.contains(getAuthenticationMethod())) {
final String msg = String.format("Requested authentication method [%s] does not "
+ "match the authentication method used [%s]", getAuthenticationMethod(),
previouslyAchievedAuthenticationMethods);
logger.debug(msg);
throw new UnrecognizedMultiFactorAuthenticationMethodException("UNRECOGNIZED_AUTHENTICATION_METHOD", msg,
getAuthenticationMethod());
}
}
return validateProxyAuthenticationRequests ? true : assertion.getChainedAuthentications().size() == 1;
}
logger.debug("No authentication context is available");
return false;
}
/**
* An MFA validator that handles proxy authentications.
*/
public static final class WithProxy extends AbstractMultiFactorAuthenticationProtocolValidationSpecification {
/**
* Instantiates a new with proxy spec.
*/
public WithProxy() {
super(true);
}
}
/**
* An MFA validator that does not handles proxy authentications.
*/
public static final class WithoutProxy extends AbstractMultiFactorAuthenticationProtocolValidationSpecification {
/**
* Instantiates a new without proxy spec.
*/
public WithoutProxy() {
super(false);
}
}
}