| /* |
| * user authentication via LDAP |
| */ |
| |
| package de.ids_mannheim.korap.authentication; |
| |
| import java.net.UnknownHostException; |
| import java.security.GeneralSecurityException; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.net.ssl.SSLSocketFactory; |
| |
| import org.apache.commons.text.StringSubstitutor; |
| import org.apache.logging.log4j.LogManager; |
| import org.apache.logging.log4j.Logger; |
| |
| import com.unboundid.ldap.sdk.BindResult; |
| import com.unboundid.ldap.sdk.Filter; |
| import com.unboundid.ldap.sdk.LDAPConnection; |
| import com.unboundid.ldap.sdk.LDAPException; |
| import com.unboundid.ldap.sdk.LDAPSearchException; |
| import com.unboundid.ldap.sdk.ResultCode; |
| import com.unboundid.ldap.sdk.SearchResult; |
| import com.unboundid.ldap.sdk.SearchResultEntry; |
| import com.unboundid.ldap.sdk.SearchScope; |
| import com.unboundid.util.NotNull; |
| import com.unboundid.util.ssl.SSLUtil; |
| import com.unboundid.util.ssl.TrustAllTrustManager; |
| import com.unboundid.util.ssl.TrustStoreTrustManager; |
| |
| import de.ids_mannheim.korap.server.EmbeddedLdapServer; |
| |
| |
| /** |
| * LDAP Login |
| * |
| * @author bodmer, margaretha, kupietz |
| */ |
| public class LdapAuth3 { |
| |
| public static final int LDAP_AUTH_ROK = 0; |
| public static final int LDAP_AUTH_RCONNECT = 1; // cannot connect to LDAP Server |
| public static final int LDAP_AUTH_RINTERR = 2; // internal error: cannot verify User+Pwd. |
| public static final int LDAP_AUTH_RUNKNOWN = 3; // User Account or Pwd unknown; |
| public static final int LDAP_AUTH_RLOCKED = 4; // User Account locked; |
| public static final int LDAP_AUTH_RNOTREG = 5; // User known, but has not registered to KorAP/C2 Service yet; |
| public static final int LDAP_AUTH_RNOEMAIL = 6; // cannot obtain email for sUserDN |
| public static final int LDAP_AUTH_RNAUTH = 7; // User Account or Pwd unknown, or not authorized |
| final static Boolean DEBUGLOG = false; // log debug output. |
| |
| private static Logger jlog = LogManager.getLogger(LdapAuth3.class); |
| |
| public static String getErrMessage(int code) { |
| switch (code) { |
| case LDAP_AUTH_ROK: |
| return "LDAP Authentication successful."; |
| case LDAP_AUTH_RCONNECT: |
| return "LDAP Authentication: connecting to LDAP Server failed!"; |
| case LDAP_AUTH_RINTERR: |
| return "LDAP Authentication failed due to an internal error!"; |
| case LDAP_AUTH_RUNKNOWN: |
| return "LDAP Authentication failed due to unknown user or password!"; |
| case LDAP_AUTH_RLOCKED: |
| return "LDAP Authentication: known user is locked!"; |
| case LDAP_AUTH_RNOTREG: |
| return "LDAP Authentication: known user, but not registered for this service!"; |
| case LDAP_AUTH_RNOEMAIL: |
| return "LDAP Authentication: known user, but cannot obtain email!"; |
| case LDAP_AUTH_RNAUTH: |
| return "LDAP Authentication: unknown user or password, or user is locked or not authorized!"; |
| default: |
| return "LDAP Authentication failed with unknown error code!"; |
| } |
| } |
| |
| public static int login(String login, String password, String ldapConfigFilename) throws LDAPException { |
| LDAPConfig ldapConfig = new LDAPConfig(ldapConfigFilename); |
| |
| login = Filter.encodeValue(login); |
| password = Filter.encodeValue(password); |
| |
| if (ldapConfig.useEmbeddedServer) { |
| try { |
| EmbeddedLdapServer.startIfNotRunning(ldapConfig); |
| } catch (GeneralSecurityException | UnknownHostException | LDAPException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| LdapAuth3Result ldapAuth3Result = search(login, password, ldapConfig, !ldapConfig.searchFilter.contains("${password}"), true); |
| SearchResult srchRes = ldapAuth3Result.getSearchResultValue(); |
| |
| if (ldapAuth3Result.getErrorCode() != 0 || srchRes == null || srchRes.getEntryCount() == 0) { |
| if (DEBUGLOG) System.out.printf("Finding '%s': no entry found!\n", login); |
| return ldapAuth3Result.getErrorCode(); |
| } |
| |
| return LDAP_AUTH_ROK; |
| } |
| |
| @NotNull |
| public static LdapAuth3Result search(String login, String password, LDAPConfig ldapConfig, boolean bindWithFoundDN, boolean applyExtraFilters) { |
| Map<String, String> valuesMap = new HashMap<>(); |
| valuesMap.put("login", login); |
| valuesMap.put("password", password); |
| StringSubstitutor sub = new StringSubstitutor(valuesMap); |
| String searchFilterInstance = sub.replace(ldapConfig.searchFilter); |
| |
| valuesMap.clear(); |
| valuesMap.put("login", login); |
| sub = new StringSubstitutor(valuesMap); |
| String insensitiveSearchFilter = sub.replace(ldapConfig.searchFilter); |
| |
| if (DEBUGLOG) { |
| //System.out.printf("LDAP Version = %d.\n", LDAPConnection.LDAP_V3); |
| System.out.printf("LDAP Host & Port = '%s':%d.\n", ldapConfig.host, ldapConfig.port); |
| System.out.printf("Login User = '%s'\n", login); |
| System.out.println("LDAPS " + ldapConfig.useSSL); |
| } |
| |
| LDAPConnection lc; |
| |
| if (ldapConfig.useSSL) { |
| try { |
| SSLUtil sslUtil; |
| if (ldapConfig.trustStorePath != null && !ldapConfig.trustStorePath.isEmpty()) { |
| sslUtil = new SSLUtil(new TrustStoreTrustManager(ldapConfig.trustStorePath)); |
| } else { |
| sslUtil = new SSLUtil(new TrustAllTrustManager()); |
| } |
| if (ldapConfig.additionalCipherSuites != null && !ldapConfig.additionalCipherSuites.isEmpty()) { |
| addSSLCipherSuites(ldapConfig.additionalCipherSuites); |
| } |
| SSLSocketFactory socketFactory = sslUtil.createSSLSocketFactory(); |
| lc = new LDAPConnection(socketFactory); |
| } catch (GeneralSecurityException e) { |
| System.err.printf("Error: login: Connecting to LDAPS Server: failed: '%s'!\n", e); |
| ldapTerminate(null); |
| return new LdapAuth3Result(null, LDAP_AUTH_RCONNECT); |
| } |
| } else { |
| lc = new LDAPConnection(); |
| } |
| try { |
| lc.connect(ldapConfig.host, ldapConfig.port); |
| if (DEBUGLOG && ldapConfig.useSSL) System.out.println("LDAPS Connection = OK\n"); |
| if (DEBUGLOG && !ldapConfig.useSSL) System.out.println("LDAP Connection = OK\n"); |
| } catch (LDAPException e) { |
| String fullStackTrace = org.apache.commons.lang.exception.ExceptionUtils.getFullStackTrace(e); |
| System.err.printf("Error: login: Connecting to LDAP Server: failed: '%s'!\n", fullStackTrace); |
| ldapTerminate(lc); |
| return new LdapAuth3Result(null, LDAP_AUTH_RCONNECT); |
| } |
| if (DEBUGLOG) System.out.printf("Debug: isConnected=%d\n", lc.isConnected() ? 1 : 0); |
| |
| try { |
| // bind to server: |
| if (DEBUGLOG) System.out.printf("Binding with '%s' ...\n", ldapConfig.sLoginDN); |
| lc.bind(ldapConfig.sLoginDN, ldapConfig.sPwd); |
| if (DEBUGLOG) System.out.print("Binding: OK.\n"); |
| } catch (LDAPException e) { |
| System.err.printf("Error: login: Binding failed: '%s'!\n", e); |
| ldapTerminate(lc); |
| return new LdapAuth3Result(null, LDAP_AUTH_RINTERR); |
| } |
| |
| if (DEBUGLOG) System.out.printf("Debug: isConnected=%d\n", lc.isConnected() ? 1 : 0); |
| |
| if (DEBUGLOG) System.out.printf("Finding user '%s'...\n", login); |
| |
| SearchResult srchRes = null; |
| try { |
| if (DEBUGLOG) System.out.printf("Searching with searchFilter: '%s'.\n", insensitiveSearchFilter); |
| |
| srchRes = lc.search(ldapConfig.searchBase, SearchScope.SUB, searchFilterInstance); |
| |
| if (DEBUGLOG) System.out.printf("Found '%s': %d entries.\n", login, srchRes.getEntryCount()); |
| } catch (LDAPSearchException e) { |
| System.err.printf("Error: Search for User failed: '%s'!\n", e); |
| } |
| |
| if (srchRes == null || srchRes.getEntryCount() == 0) { |
| if (DEBUGLOG) System.out.printf("Finding '%s': no entry found!\n", login); |
| ldapTerminate(lc); |
| return new LdapAuth3Result(null, LDAP_AUTH_RUNKNOWN); |
| } |
| |
| if (bindWithFoundDN) { |
| String matchedDN = srchRes.getSearchEntries().get(0).getDN(); |
| if (DEBUGLOG) System.out.printf("Requested bind for found user %s' failed.\n", matchedDN); |
| try { |
| // bind to server: |
| if (DEBUGLOG) System.out.printf("Binding with '%s' ...\n", matchedDN); |
| BindResult bindResult = lc.bind(matchedDN, password); |
| if (DEBUGLOG) System.out.print("Binding: OK.\n"); |
| if (!bindResult.getResultCode().equals(ResultCode.SUCCESS)) { |
| ldapTerminate(lc); |
| return new LdapAuth3Result(null, LDAP_AUTH_RUNKNOWN); |
| } |
| } catch (LDAPException e) { |
| System.err.printf("Error: login: Binding failed: '%s'!\n", e); |
| ldapTerminate(lc); |
| return new LdapAuth3Result(null, LDAP_AUTH_RUNKNOWN); |
| } |
| } |
| |
| if (applyExtraFilters) { |
| if (ldapConfig.authFilter != null && !ldapConfig.authFilter.isEmpty()) { |
| srchRes = applyAdditionalFilter(login, ldapConfig, ldapConfig.authFilter, searchFilterInstance, lc); |
| if (srchRes == null || srchRes.getEntryCount() == 0) { |
| ldapTerminate(lc); |
| return new LdapAuth3Result(null, LDAP_AUTH_RNOTREG); |
| } |
| } |
| |
| if (ldapConfig.userNotBlockedFilter != null && !ldapConfig.userNotBlockedFilter.isEmpty()) { |
| srchRes = applyAdditionalFilter(login, ldapConfig, ldapConfig.userNotBlockedFilter, searchFilterInstance, lc); |
| if (srchRes == null || srchRes.getEntryCount() == 0) { |
| ldapTerminate(lc); |
| return new LdapAuth3Result(null, LDAP_AUTH_RLOCKED); |
| } |
| } |
| } |
| |
| ldapTerminate(lc); |
| return new LdapAuth3Result(srchRes, LDAP_AUTH_ROK); |
| } |
| |
| private static SearchResult applyAdditionalFilter(String login, LDAPConfig ldapConfig, String searchFilterInstance, String extraFilter, LDAPConnection lc) { |
| SearchResult srchRes; |
| srchRes = null; |
| try { |
| String combindedFilterInstance = "(&" + searchFilterInstance + extraFilter + ")"; |
| if (DEBUGLOG) System.out.printf("Searching with additional Filter: '%s'.\n", extraFilter); |
| srchRes = lc.search(ldapConfig.searchBase, SearchScope.SUB, combindedFilterInstance); |
| if (DEBUGLOG) System.out.printf("Found '%s': %d entries.\n", login, srchRes.getEntryCount()); |
| } catch (LDAPSearchException e) { |
| System.err.printf("Error: Search for User failed: '%s'!\n", e); |
| } |
| return srchRes; |
| } |
| |
| public static String getEmail(String sUserDN, String ldapConfigFilename) throws LDAPException { |
| String sUserPwd = "*"; |
| LDAPConfig ldapConfig = new LDAPConfig(ldapConfigFilename); |
| final String emailAttribute = ldapConfig.emailAttribute; |
| |
| SearchResult searchResult = search(sUserDN, sUserPwd, ldapConfig, false, false).getSearchResultValue(); |
| |
| if (searchResult == null) { |
| return null; |
| } |
| |
| for (SearchResultEntry entry : searchResult.getSearchEntries()) { |
| String mail = entry.getAttributeValue(emailAttribute); |
| if (mail != null) { |
| return mail; |
| } |
| } |
| return null; |
| } |
| |
| public static String getUsername(String sUserDN, String ldapConfigFilename) throws LDAPException { |
| String sUserPwd = "*"; |
| LDAPConfig ldapConfig = new LDAPConfig(ldapConfigFilename); |
| final String idsC2Attribute = "idsC2Profile"; |
| final String uidAttribute = "uid"; |
| |
| SearchResult searchResult = search(sUserDN, sUserPwd, ldapConfig, false, false) |
| .getSearchResultValue(); |
| |
| if (searchResult == null) { |
| return null; |
| } |
| |
| String username = null; |
| for (SearchResultEntry entry : searchResult.getSearchEntries()) { |
| username = entry.getAttributeValue(idsC2Attribute); |
| if (username == null) { |
| username = entry.getAttributeValue(uidAttribute); |
| jlog.warn("idsC2Profile not found for uid: "+username); |
| } |
| } |
| return username; |
| } |
| |
| public static void ldapTerminate(LDAPConnection lc) { |
| if (DEBUGLOG) System.out.println("Terminating..."); |
| |
| if (lc != null) { |
| lc.close(null); |
| } |
| if (DEBUGLOG) System.out.println("closing connection: done.\n"); |
| } |
| |
| private static void addSSLCipherSuites(String ciphersCsv) { |
| // add e.g. TLS_RSA_WITH_AES_256_GCM_SHA384 |
| Set<String> ciphers = new HashSet<>(); |
| ciphers.addAll(SSLUtil.getEnabledSSLCipherSuites()); |
| ciphers.addAll(Arrays.asList(ciphersCsv.split(", *"))); |
| SSLUtil.setEnabledSSLCipherSuites(ciphers); |
| } |
| |
| public static class LdapAuth3Result { |
| final int errorCode; |
| final Object value; |
| |
| |
| public LdapAuth3Result(Object value, int errorCode) { |
| this.errorCode = errorCode; |
| this.value = value; |
| } |
| |
| public int getErrorCode() { |
| return errorCode; |
| } |
| |
| public Object getValue() { |
| return value; |
| } |
| |
| public SearchResult getSearchResultValue() { |
| return (SearchResult) value; |
| } |
| } |
| } |