package io.lumify.web.auth;
import com.google.inject.Inject;
import com.unboundid.ldap.sdk.SearchResultEntry;
import io.lumify.core.config.Configuration;
import io.lumify.core.exception.LumifyException;
import io.lumify.core.model.user.UserRepository;
import io.lumify.core.user.User;
import io.lumify.core.util.LumifyLogger;
import io.lumify.core.util.LumifyLoggerFactory;
import io.lumify.ldap.LdapSearchService;
import io.lumify.web.X509AuthenticationHandler;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.securegraph.Graph;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class LdapX509AuthenticationHandler extends X509AuthenticationHandler {
private static final LumifyLogger LOGGER = LumifyLoggerFactory.getLogger(LdapX509AuthenticationHandler.class);
private LdapSearchService ldapSearchService;
private LdapX509AuthenticationConfiguration ldapX509AuthenticationConfiguration;
@Inject
public LdapX509AuthenticationHandler(final UserRepository userRepository, final Graph graph, final LdapSearchService ldapSearchService, final Configuration configuration) {
super(userRepository, graph);
this.ldapSearchService = ldapSearchService;
ldapX509AuthenticationConfiguration = new LdapX509AuthenticationConfiguration();
configuration.setConfigurables(ldapX509AuthenticationConfiguration, "ldap.x509Authentication");
}
@Override
protected X509Certificate extractCertificate(HttpServletRequest request) {
X509Certificate cert = super.extractCertificate(request);
if (cert != null) {
LOGGER.info("using cert from %s request attribute", CERTIFICATE_REQUEST_ATTRIBUTE);
} else {
try {
cert = getHeaderClientCert(request);
} catch (Exception e) {
throw new LumifyException("failed to extract cert from request header", e);
}
if (cert != null) {
LOGGER.info("using cert from %s request header", ldapX509AuthenticationConfiguration.getClientCertHeader());
LOGGER.info("client dn from %s request header is %s", ldapX509AuthenticationConfiguration.getClientDnHeader(), getHeaderClientDN(request));
} else {
LOGGER.error("no certificate found in request attribute %s or request header %s", CERTIFICATE_REQUEST_ATTRIBUTE, ldapX509AuthenticationConfiguration.getClientCertHeader());
return null;
}
}
return cert;
}
@Override
protected User getUser(HttpServletRequest request, X509Certificate cert) {
SearchResultEntry searchResultEntry = ldapSearchService.searchPeople(cert);
LOGGER.debug("searchResultEntry is\n" + searchResultEntry.toLDIFString());
// required role(s)
String requiredAttribute = ldapX509AuthenticationConfiguration.getRequiredAttribute();
if (requiredAttribute != null) {
List<String> requiredAttributeValues = ldapX509AuthenticationConfiguration.getRequiredAttributeValues();
if (requiredAttributeValues != null) {
String[] valueArray = searchResultEntry.getAttributeValues(requiredAttribute);
if (valueArray != null) {
List<String> valueList = Arrays.asList(valueArray);
for (String requiredValue : requiredAttributeValues) {
if (!valueList.contains(requiredValue)) {
LOGGER.warn("LDAP attribute [" + requiredAttribute + "] does not include required value: " + requiredValue);
return null;
}
}
} else {
LOGGER.warn("LDAP attribute [" + requiredAttribute + "] not found");
return null;
}
}
}
Set<String> groups = ldapSearchService.searchGroups(searchResultEntry);
LOGGER.debug("retrieved groups %s for user dn %s", groups, searchResultEntry.getDN());
// required group(s)
List<String> requiredGroups = ldapX509AuthenticationConfiguration.getRequiredGroups();
if (requiredGroups != null) {
for (String group : requiredGroups) {
if (!groups.contains(group)) {
LOGGER.warn("LDAP entry is not a member of required group: " + group);
return null;
}
}
}
String username = getAttributeValue(
searchResultEntry, ldapX509AuthenticationConfiguration.getUsernameAttribute(), super.getUsername(cert));
String displayName = getAttributeValue(
searchResultEntry, ldapX509AuthenticationConfiguration.getDisplayNameAttribute(), username);
String randomPassword = UserRepository.createRandomPassword();
User user = getUserRepository().findOrAddUser(
username,
displayName,
null,
randomPassword,
groups.toArray(new String[groups.size()])
);
LOGGER.debug("user is %s", user.toString());
return user;
}
private String getAttributeValue(SearchResultEntry entry, String attrName, String defaultValue) {
return attrName != null ? entry.getAttributeValue(attrName) : defaultValue;
}
private String getHeaderClientDN(HttpServletRequest request) {
String dnComponents = request.getHeader(ldapX509AuthenticationConfiguration.getClientDnHeader());
if (dnComponents.startsWith("/")) {
dnComponents = dnComponents.substring("/".length());
}
ArrayUtils.reverse(dnComponents.split("/"));
return StringUtils.join(Arrays.asList(dnComponents), ",");
}
private X509Certificate getHeaderClientCert(HttpServletRequest request) throws NoSuchAlgorithmException, CertificateException {
String pemCertText = request.getHeader(ldapX509AuthenticationConfiguration.getClientCertHeader());
pemCertText = pemCertText.replaceAll("-----(BEGIN|END) CERTIFICATE-----", "");
pemCertText = pemCertText.replaceAll("\\n", "");
byte[] certBytes = Base64.decodeBase64(pemCertText);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
return (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(certBytes));
}
}