package org.apereo.cas.support.spnego.authentication.handler.support;
import jcifs.spnego.Authentication;
import org.apereo.cas.authentication.BasicCredentialMetaData;
import org.apereo.cas.authentication.Credential;
import org.apereo.cas.authentication.DefaultHandlerResult;
import org.apereo.cas.authentication.HandlerResult;
import org.apereo.cas.authentication.PreventedException;
import org.apereo.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler;
import org.apereo.cas.authentication.principal.Principal;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.support.spnego.authentication.principal.SpnegoCredential;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.security.auth.login.FailedLoginException;
import java.security.GeneralSecurityException;
import java.util.regex.Pattern;
/**
* Implementation of an AuthenticationHandler for SPNEGO supports. This Handler
* support both NTLM and Kerberos. NTLM is disabled by default.
*
* @author Arnaud Lesueur
* @author Marc-Antoine Garrigue
* @author Scott Battaglia
* @author Marvin S. Addison
* @since 3.1
*/
public class JcifsSpnegoAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(JcifsSpnegoAuthenticationHandler.class);
private Authentication authentication;
private boolean principalWithDomainName;
private boolean isNTLMallowed;
public JcifsSpnegoAuthenticationHandler(final String name, final ServicesManager servicesManager, final PrincipalFactory principalFactory,
final Authentication authentication, final boolean principalWithDomainName, final boolean isNTLMallowed) {
super(name, servicesManager, principalFactory, null);
this.authentication = authentication;
this.principalWithDomainName = principalWithDomainName;
this.isNTLMallowed = isNTLMallowed;
}
@Override
protected HandlerResult doAuthentication(final Credential credential) throws GeneralSecurityException, PreventedException {
final SpnegoCredential spnegoCredential = (SpnegoCredential) credential;
final java.security.Principal principal;
final byte[] nextToken;
if (!this.isNTLMallowed && spnegoCredential.isNtlm()) {
throw new FailedLoginException("NTLM not allowed");
}
try {
// proceed authentication using jcifs
synchronized (this) {
this.authentication.reset();
LOGGER.debug("Processing SPNEGO authentication");
this.authentication.process(spnegoCredential.getInitToken());
principal = this.authentication.getPrincipal();
LOGGER.debug("Authenticated SPNEGO principal [{}]", principal.getName());
LOGGER.debug("Retrieving the next token for authentication");
nextToken = this.authentication.getNextToken();
}
} catch (final jcifs.spnego.AuthenticationException e) {
throw new FailedLoginException(e.getMessage());
}
// evaluate jcifs response
if (nextToken != null) {
LOGGER.debug("Setting nextToken in credential");
spnegoCredential.setNextToken(nextToken);
} else {
LOGGER.debug("nextToken is null");
}
boolean success = false;
if (principal != null) {
if (spnegoCredential.isNtlm()) {
LOGGER.debug("NTLM Credential is valid for user [{}]", principal.getName());
} else {
LOGGER.debug("Kerberos Credential is valid for user [{}]", principal.getName());
}
spnegoCredential.setPrincipal(getPrincipal(principal.getName(), spnegoCredential.isNtlm()));
success = true;
}
if (!success) {
throw new FailedLoginException("Principal is null, the processing of the SPNEGO Token failed");
}
return new DefaultHandlerResult(this, new BasicCredentialMetaData(credential), spnegoCredential.getPrincipal());
}
@Override
public boolean supports(final Credential credential) {
return credential instanceof SpnegoCredential;
}
public void setAuthentication(final Authentication authentication) {
this.authentication = authentication;
}
public void setPrincipalWithDomainName(final boolean principalWithDomainName) {
this.principalWithDomainName = principalWithDomainName;
}
public void setNTLMallowed(final boolean isNTLMallowed) {
this.isNTLMallowed = isNTLMallowed;
}
/**
* Gets the principal from the given name. The principal
* is created by the factory instance.
*
* @param name the name
* @param isNtlm the is ntlm
* @return the simple principal
*/
protected Principal getPrincipal(final String name, final boolean isNtlm) {
if (this.principalWithDomainName) {
return this.principalFactory.createPrincipal(name);
}
if (isNtlm) {
return Pattern.matches("\\S+\\\\\\S+", name)
? this.principalFactory.createPrincipal(name.split("\\\\")[1])
: this.principalFactory.createPrincipal(name);
}
return this.principalFactory.createPrincipal(name.split("@")[0]);
}
}