
keywhiz.auth.ldap.LdapAuthenticator Maven / Gradle / Ivy
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package keywhiz.auth.ldap;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.collect.Sets;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchResultEntry;
import com.unboundid.ldap.sdk.SearchScope;
import io.dropwizard.auth.basic.BasicCredentials;
import io.dropwizard.java8.auth.Authenticator;
import java.util.Optional;
import java.util.Set;
import javax.ws.rs.ForbiddenException;
import keywhiz.auth.User;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.IETFUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LdapAuthenticator implements Authenticator {
private static final Logger logger = LoggerFactory.getLogger(LdapAuthenticator.class);
private final LdapConnectionFactory connectionFactory;
private final LdapLookupConfig config;
public LdapAuthenticator(LdapConnectionFactory connectionFactory, LdapLookupConfig config) {
this.connectionFactory = connectionFactory;
this.config = config;
}
@Override
public Optional authenticate(BasicCredentials credentials) {
User user = null;
try {
String username = credentials.getUsername();
if (!User.isSanitizedUsername(username)) {
logger.info("Username: {} must match pattern: {}", username, User.USERNAME_PATTERN);
return Optional.empty();
}
String userDN = dnFromUsername(username);
String password = credentials.getPassword();
// Must have password for current config
if (Strings.isNullOrEmpty(password)) {
logger.info("No password for user provided");
return Optional.empty();
}
LDAPConnection authenticatedConnection = connectionFactory.getLDAPConnection(userDN, password);
authenticatedConnection.close();
Set requiredRoles = config.getRequiredRoles();
if (!requiredRoles.isEmpty()) {
Set roles = rolesFromDN(userDN);
boolean accessAllowed = false;
for (String requiredRole : requiredRoles) {
if (roles.contains(requiredRole)) {
accessAllowed = true;
}
}
if (!accessAllowed) {
logger.warn("User {} not in one of required LDAP roles: [{}].", username, requiredRoles);
throw new ForbiddenException();
}
}
user = User.named(username);
} catch (LDAPException le) {
// The INVALID_CREDENTIALS case is handled by returning an absent optional from this function
if (le.getResultCode() != ResultCode.INVALID_CREDENTIALS) {
logger.error("Error connecting to LDAP", le);
throw Throwables.propagate(le);
}
}
return Optional.ofNullable(user);
}
private String dnFromUsername(String username) throws LDAPException {
String baseDN = config.getUserBaseDN();
String lookup = String.format("(%s=%s)", config.getUserAttribute(), username);
SearchRequest searchRequest = new SearchRequest(baseDN, SearchScope.SUB, lookup);
LDAPConnection connection = connectionFactory.getLDAPConnection();
try {
SearchResult sr = connection.search(searchRequest);
if (sr.getEntryCount() == 0) {
throw new LDAPException(ResultCode.INVALID_CREDENTIALS);
}
return sr.getSearchEntries().get(0).getDN();
} finally {
connection.close();
}
}
private Set rolesFromDN(String userDN) throws LDAPException {
SearchRequest searchRequest = new SearchRequest(config.getRoleBaseDN(),
SearchScope.SUB, Filter.createEqualityFilter("uniqueMember", userDN));
Set roles = Sets.newLinkedHashSet();
LDAPConnection connection = connectionFactory.getLDAPConnection();
try {
SearchResult sr = connection.search(searchRequest);
for (SearchResultEntry sre : sr.getSearchEntries()) {
X500Name x500Name = new X500Name(sre.getDN());
RDN[] rdns = x500Name.getRDNs(BCStyle.CN);
if (rdns.length == 0) {
logger.error("Could not create X500 Name for role:" + sre.getDN());
} else {
String commonName = IETFUtils.valueToString(rdns[0].getFirst().getValue());
roles.add(commonName);
}
}
} finally {
connection.close();
}
return roles;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy