package org.apereo.cas.adaptors.radius.authentication;
import net.jradius.exception.TimeoutException;
import org.apache.commons.lang3.tuple.Pair;
import org.apereo.cas.adaptors.radius.RadiusServer;
import org.apereo.cas.adaptors.radius.RadiusUtils;
import org.apereo.cas.authentication.Credential;
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.PrincipalFactory;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.web.support.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.RequestContextHolder;
import javax.security.auth.login.FailedLoginException;
import java.net.SocketTimeoutException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* This is {@link RadiusTokenAuthenticationHandler}.
*
* @author Misagh Moayyed
* @since 5.0.0
*/
public class RadiusTokenAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(RadiusTokenAuthenticationHandler.class);
private final List<RadiusServer> servers;
private final boolean failoverOnException;
private final boolean failoverOnAuthenticationFailure;
public RadiusTokenAuthenticationHandler(final String name, final ServicesManager servicesManager,
final PrincipalFactory principalFactory,
final List<RadiusServer> servers,
final boolean failoverOnException,
final boolean failoverOnAuthenticationFailure) {
super(name, servicesManager, principalFactory, null);
this.servers = servers;
this.failoverOnException = failoverOnException;
this.failoverOnAuthenticationFailure = failoverOnAuthenticationFailure;
LOGGER.debug("Using [{}]", getClass().getSimpleName());
}
@Override
public boolean supports(final Credential credential) {
return RadiusTokenCredential.class.isAssignableFrom(credential.getClass());
}
@Override
protected HandlerResult doAuthentication(final Credential credential) throws GeneralSecurityException, PreventedException {
try {
final RadiusTokenCredential radiusCredential = (RadiusTokenCredential) credential;
final String password = radiusCredential.getToken();
final RequestContext context = RequestContextHolder.getRequestContext();
final String username = WebUtils.getAuthentication(context).getPrincipal().getId();
final Pair<Boolean, Optional<Map<String, Object>>> result =
RadiusUtils.authenticate(username, password, this.servers,
this.failoverOnAuthenticationFailure, this.failoverOnException);
if (result.getKey()) {
return createHandlerResult(credential,
this.principalFactory.createPrincipal(username, result.getValue().get()),
new ArrayList<>());
}
throw new FailedLoginException("Radius authentication failed for user " + username);
} catch (final Exception e) {
throw new FailedLoginException("Radius authentication failed " + e.getMessage());
}
}
/**
* Can ping boolean.
*
* @return true/false
*/
public boolean canPing() {
final String uidPsw = getClass().getSimpleName();
for (final RadiusServer server : this.servers) {
LOGGER.debug("Attempting to ping RADIUS server [{}] via simulating an authentication request. If the server responds "
+ "successfully, mock authentication will fail correctly.", server);
try {
server.authenticate(uidPsw, uidPsw);
} catch (final TimeoutException | SocketTimeoutException e) {
LOGGER.debug("Server [{}] is not available", server);
continue;
} catch (final Exception e) {
LOGGER.debug("Pinging RADIUS server was successful. Response [{}]", e.getMessage());
}
return true;
}
return false;
}
}