| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 1 | /* |
| 2 | * user authentication via LDAP |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 3 | */ |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 4 | |
| margaretha | 139d0f7 | 2017-11-14 18:56:22 +0100 | [diff] [blame] | 5 | package de.ids_mannheim.korap.authentication; |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 6 | |
| margaretha | 235a680 | 2018-06-06 19:21:53 +0200 | [diff] [blame] | 7 | import com.nimbusds.jose.JOSEException; |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 8 | import com.unboundid.ldap.sdk.*; |
| 9 | import com.unboundid.util.ssl.SSLUtil; |
| 10 | import com.unboundid.util.ssl.TrustAllTrustManager; |
| 11 | import com.unboundid.util.ssl.TrustStoreTrustManager; |
| margaretha | 5225ed0 | 2018-06-25 18:38:40 +0200 | [diff] [blame] | 12 | import de.ids_mannheim.korap.config.FullConfiguration; |
| margaretha | 0e8f4e7 | 2018-04-05 14:11:52 +0200 | [diff] [blame] | 13 | import de.ids_mannheim.korap.constant.TokenType; |
| Marc Kupietz | 1e388b4 | 2022-04-30 18:37:03 +0200 | [diff] [blame^] | 14 | import de.ids_mannheim.korap.server.EmbeddedLdapServer; |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 15 | import org.apache.commons.text.StringSubstitutor; |
| 16 | |
| 17 | import javax.net.ssl.SSLSocketFactory; |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 18 | import java.io.IOException; |
| Marc Kupietz | 1e388b4 | 2022-04-30 18:37:03 +0200 | [diff] [blame^] | 19 | import java.net.UnknownHostException; |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 20 | import java.security.GeneralSecurityException; |
| 21 | import java.util.HashMap; |
| 22 | import java.util.Map; |
| Marc Kupietz | 1e388b4 | 2022-04-30 18:37:03 +0200 | [diff] [blame^] | 23 | |
| 24 | import static de.ids_mannheim.korap.server.EmbeddedLdapServer.loadProp; |
| margaretha | 4de4119 | 2017-11-15 11:47:11 +0100 | [diff] [blame] | 25 | |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 26 | |
| 27 | /** |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 28 | * LDAP Login |
| 29 | * |
| 30 | * @author bodmer, margaretha, kupietz |
| margaretha | 4de4119 | 2017-11-15 11:47:11 +0100 | [diff] [blame] | 31 | * @see APIAuthentication |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 32 | */ |
| margaretha | 4de4119 | 2017-11-15 11:47:11 +0100 | [diff] [blame] | 33 | public class LdapAuth3 extends APIAuthentication { |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 34 | |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 35 | public static final int LDAP_AUTH_ROK = 0; |
| 36 | public static final int LDAP_AUTH_RCONNECT = 1; // cannot connect to LDAP Server |
| 37 | public static final int LDAP_AUTH_RINTERR = 2; // internal error: cannot verify User+Pwd. |
| 38 | /* cannot be distinguished, currently |
| 39 | public static final int LDAP_AUTH_RUNKNOWN = 3; // User Account or Pwd unknown; |
| 40 | public static final int LDAP_AUTH_RLOCKED = 4; // User Account locked; |
| 41 | public static final int LDAP_AUTH_RNOTREG = 5; // User known, but has not registered to KorAP/C2 Service yet; |
| 42 | */ |
| 43 | public static final int LDAP_AUTH_RNOEMAIL = 6; // cannot obtain email for sUserDN |
| 44 | public static final int LDAP_AUTH_RNAUTH = 7; // User Account or Pwd unknown, or not authorized |
| 45 | final static Boolean DEBUGLOG = false; // log debug output. |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 46 | |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 47 | public LdapAuth3(FullConfiguration config) throws JOSEException { |
| margaretha | 4de4119 | 2017-11-15 11:47:11 +0100 | [diff] [blame] | 48 | super(config); |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 49 | } |
| 50 | |
| 51 | public static String getErrMessage(int code) { |
| 52 | switch (code) { |
| 53 | case LDAP_AUTH_ROK: |
| 54 | return "LDAP Authentication successful."; |
| 55 | case LDAP_AUTH_RCONNECT: |
| 56 | return "LDAP Authentication: connecting to LDAP Server failed!"; |
| 57 | case LDAP_AUTH_RINTERR: |
| 58 | return "LDAP Authentication failed due to an internal error!"; |
| 59 | /* cannot be distinguished, currently |
| 60 | case LDAP_AUTH_RUNKNOWN: |
| 61 | return "LDAP Authentication failed due to unknown user or password!"; |
| 62 | case LDAP_AUTH_RLOCKED: |
| 63 | return "LDAP Authentication: known user is locked!"; |
| 64 | case LDAP_AUTH_RNOTREG: |
| 65 | return "LDAP Authentication: known user has not registered yet!"; |
| 66 | */ |
| 67 | case LDAP_AUTH_RNOEMAIL: |
| 68 | return "LDAP Authentication: known user, but cannot obtain email!"; |
| 69 | case LDAP_AUTH_RNAUTH: |
| 70 | return "LDAP Authentication: unknown user or password, or user is locked or not authorized!"; |
| 71 | default: |
| 72 | return "LDAP Authentication failed with unknown error code!"; |
| 73 | } |
| 74 | } |
| 75 | |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 76 | |
| 77 | public static int login(String sUserDN, String sUserPwd, String ldapConfigFilename) throws LDAPException { |
| 78 | |
| 79 | sUserDN = Filter.encodeValue(sUserDN); |
| 80 | sUserPwd = Filter.encodeValue(sUserPwd); |
| 81 | |
| 82 | Map<String, String> ldapConfig; |
| 83 | try { |
| 84 | ldapConfig = loadProp(ldapConfigFilename); |
| 85 | } catch (IOException e) { |
| 86 | System.out.println("Error: LDAPAuth.login: cannot load Property file!"); |
| 87 | return LDAP_AUTH_RINTERR; |
| 88 | } |
| 89 | |
| 90 | final Boolean ldapS = Boolean.parseBoolean(ldapConfig.getOrDefault("ldapS", "false")); |
| 91 | final String ldapHost = ldapConfig.getOrDefault("ldapHost", "localhost"); |
| 92 | final int ldapPort = Integer.parseInt(ldapConfig.getOrDefault("ldapPort", (ldapS ? "636" : "389"))); |
| 93 | final String ldapBase = ldapConfig.getOrDefault("ldapBase", "dc=example,dc=com"); |
| 94 | final String sLoginDN = ldapConfig.getOrDefault("sLoginDN", "cn=admin,dc=example,dc=com"); |
| 95 | final String ldapFilter = ldapConfig.getOrDefault("ldapFilter", "(&(|(&(mail=${username})(idsC2Password=${password}))(&(idsC2Profile=${username})(idsC2Password=${password})))(&(idsC2=TRUE)(|(idsStatus=1)(|(idsStatus=0)(xidsStatus=\00)))))"); |
| 96 | final String sPwd = ldapConfig.getOrDefault("pwd", ""); |
| 97 | final String trustStorePath = ldapConfig.getOrDefault("trustStore", null); |
| Marc Kupietz | 1e388b4 | 2022-04-30 18:37:03 +0200 | [diff] [blame^] | 98 | final Boolean useEmbeddedServer = Boolean.parseBoolean(ldapConfig.getOrDefault("useEmbeddedServer", "false")); |
| 99 | |
| 100 | if (useEmbeddedServer && EmbeddedLdapServer.server == null) { |
| 101 | try { |
| 102 | EmbeddedLdapServer.start(ldapConfigFilename); |
| 103 | } catch (GeneralSecurityException e) { |
| 104 | throw new RuntimeException(e); |
| 105 | } catch (UnknownHostException e) { |
| 106 | throw new RuntimeException(e); |
| 107 | } |
| 108 | } |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 109 | |
| 110 | Map<String, String> valuesMap = new HashMap<>(); |
| 111 | valuesMap.put("username", sUserDN); |
| 112 | valuesMap.put("password", sUserPwd); |
| 113 | StringSubstitutor sub = new StringSubstitutor(valuesMap); |
| 114 | String ldapFilterInstance = sub.replace(ldapFilter); |
| 115 | |
| 116 | if (DEBUGLOG) { |
| 117 | //System.out.printf("LDAP Version = %d.\n", LDAPConnection.LDAP_V3); |
| 118 | System.out.printf("LDAP Host & Port = '%s':%d.\n", ldapHost, ldapPort); |
| 119 | System.out.printf("Login User = '%s'\n", sUserDN); |
| 120 | } |
| 121 | |
| 122 | // LDAP Connection: |
| 123 | if (DEBUGLOG) System.out.println("LDAPS " + ldapS); |
| 124 | |
| 125 | LDAPConnection lc = null; |
| 126 | |
| 127 | if (ldapS) { |
| 128 | try { |
| 129 | SSLUtil sslUtil; |
| 130 | if (trustStorePath != null && !trustStorePath.isEmpty()) { |
| 131 | sslUtil = new SSLUtil(new TrustStoreTrustManager(trustStorePath)); |
| 132 | } else { |
| 133 | sslUtil = new SSLUtil(new TrustAllTrustManager()); |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 134 | } |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 135 | SSLSocketFactory socketFactory = sslUtil.createSSLSocketFactory(); |
| 136 | lc = new LDAPConnection(socketFactory, ldapHost, ldapPort); |
| 137 | } catch (GeneralSecurityException e) { |
| 138 | System.err.printf("Error: login: Connecting to LDAPS Server: failed: '%s'!\n", e); |
| 139 | return ldapTerminate(lc, LDAP_AUTH_RCONNECT); |
| 140 | } |
| 141 | } else { |
| 142 | lc = new LDAPConnection(); |
| 143 | try { |
| 144 | lc.connect(ldapHost, ldapPort); |
| 145 | if (DEBUGLOG && ldapS) System.out.println("LDAPS Connection = OK\n"); |
| 146 | if (DEBUGLOG && !ldapS) System.out.println("LDAP Connection = OK\n"); |
| 147 | } catch (LDAPException e) { |
| 148 | System.err.printf("Error: login: Connecting to LDAP Server: failed: '%s'!\n", e); |
| 149 | return ldapTerminate(lc, LDAP_AUTH_RCONNECT); |
| 150 | } |
| 151 | } |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 152 | |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 153 | |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 154 | if (DEBUGLOG) System.out.printf("Debug: isConnected=%d\n", lc.isConnected() ? 1 : 0); |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 155 | |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 156 | try { |
| 157 | // bind to server: |
| 158 | if (DEBUGLOG) System.out.printf("Binding with '%s' ...\n", sLoginDN); |
| 159 | lc.bind(sLoginDN, sPwd); |
| 160 | if (DEBUGLOG) System.out.printf("Binding: OK.\n"); |
| 161 | } catch (LDAPException e) { |
| 162 | System.err.printf("Error: login: Binding failed: '%s'!\n", e); |
| 163 | return ldapTerminate(lc, LDAP_AUTH_RINTERR); |
| 164 | } |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 165 | |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 166 | if (DEBUGLOG) System.out.printf("Debug: isConnected=%d\n", lc.isConnected() ? 1 : 0); |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 167 | |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 168 | if (DEBUGLOG) System.out.printf("Finding user '%s'...\n", sUserDN); |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 169 | |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 170 | SearchResult srchRes; |
| 171 | try { |
| 172 | // SCOPE_SUB = Scope Subtree. |
| 173 | if (DEBUGLOG) System.out.printf("Finding Filter: '%s'.\n", ldapFilterInstance); |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 174 | |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 175 | srchRes = lc.search(ldapBase, SearchScope.SUB, ldapFilterInstance); |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 176 | |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 177 | if (DEBUGLOG) System.out.printf("Finding '%s': %d entries.\n", sUserDN, srchRes.getEntryCount()); |
| 178 | } catch (LDAPSearchException e) { |
| 179 | System.err.printf("Error: login: Search for User failed: '%s'!\n", e); |
| 180 | return ldapTerminate(lc, LDAP_AUTH_RNAUTH); |
| 181 | } |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 182 | |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 183 | if (srchRes.getEntryCount() == 0) { |
| 184 | if (DEBUGLOG) System.out.printf("Finding '%s': no entry found!\n", sUserDN); |
| 185 | return ldapTerminate(lc, LDAP_AUTH_RNAUTH); |
| 186 | } |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 187 | |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 188 | return ldapTerminate(lc, LDAP_AUTH_ROK); // OK. |
| 189 | } |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 190 | |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 191 | public static int ldapTerminate(LDAPConnection lc, int ret) { |
| 192 | if (DEBUGLOG) System.out.println("Terminating..."); |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 193 | |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 194 | lc.close(null); |
| 195 | if (DEBUGLOG) System.out.println("closing connection: done.\n"); |
| 196 | return ret; |
| 197 | } |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 198 | |
| Marc Kupietz | 0a37867 | 2022-04-30 09:35:27 +0200 | [diff] [blame] | 199 | @Override |
| 200 | public TokenType getTokenType() { |
| 201 | return TokenType.API; |
| 202 | } |
| Bodmo | 3d6bd35 | 2017-04-25 11:31:39 +0200 | [diff] [blame] | 203 | |
| 204 | } |