Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / MyDerby-10.3 / java / engine / org / apache / derby / impl / jdbc / authentication / LDAPAuthenticationSchemeImpl.java
diff --git a/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/jdbc/authentication/LDAPAuthenticationSchemeImpl.java b/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/jdbc/authentication/LDAPAuthenticationSchemeImpl.java
new file mode 100644 (file)
index 0000000..a374a77
--- /dev/null
@@ -0,0 +1,505 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.jdbc.authentication.LDAPAuthenticationSchemeImpl\r
+\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to you under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+      http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.jdbc.authentication;\r
+\r
+import org.apache.derby.iapi.reference.MessageId;\r
+import org.apache.derby.iapi.services.monitor.Monitor;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.services.i18n.MessageService;\r
+import org.apache.derby.iapi.jdbc.AuthenticationService;\r
+\r
+import org.apache.derby.authentication.UserAuthenticator;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.util.StringUtil;\r
+\r
+import javax.naming.*;\r
+import javax.naming.directory.*;\r
+\r
+\r
+import java.util.Properties;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.security.AccessController;\r
+import java.security.PrivilegedActionException;\r
+import java.security.PrivilegedExceptionAction;\r
+import java.sql.SQLException;\r
+\r
+/**\r
+ * This is the Derby LDAP authentication scheme implementation.\r
+ *\r
+ * JNDI system/environment properties can be set at the database\r
+ * level as database properties. They will be picked-up and set in\r
+ * the JNDI initial context if any are found.\r
+ *\r
+ * We do connect first to the LDAP server in order to retrieve the\r
+ * user's distinguished name (DN) and then we reconnect and try to\r
+ * authenticate with the user's DN and passed-in password.\r
+ *\r
+ * In 2.0 release, we first connect to do a search (user full DN lookup).\r
+ * This initial lookup can be done through anonymous bind or using special\r
+ * LDAP search credentials that the user may have configured on the\r
+ * LDAP settings for the database or the system.\r
+ * It is a typical operation with LDAP servers where sometimes it is\r
+ * hard to tell/guess in advance a users' full DN's.\r
+ *\r
+ * NOTE: In a future release, we will cache/maintain the user DN within\r
+ * the the Derby database or system to avoid the initial lookup.\r
+ * Also note that LDAP search/retrieval operations are usually very fast.\r
+ *\r
+ * The default LDAP url is ldap:/// (ldap://localhost:389/)\r
+ *\r
+ * @see org.apache.derby.authentication.UserAuthenticator \r
+ *\r
+ */\r
+\r
+public final class LDAPAuthenticationSchemeImpl\r
+extends JNDIAuthenticationSchemeBase\r
+{\r
+       private static final String dfltLDAPURL = "ldap://";\r
+\r
+       private String searchBaseDN;\r
+\r
+       private String leftSearchFilter; // stick in uid in between\r
+       private String rightSearchFilter;\r
+       private boolean useUserPropertyAsDN;\r
+\r
+       // Search Auth DN & Password if anonymous search not allowed\r
+       private String searchAuthDN;\r
+       private String searchAuthPW;\r
+       // we only want the user's full DN in return\r
+       private static final String[] attrDN = {"dn"};                                                          ;\r
+\r
+       //\r
+       // Derby LDAP Configuration properties\r
+       //\r
+       private static final String LDAP_SEARCH_BASE =\r
+                                                               "derby.authentication.ldap.searchBase";\r
+       private static final String LDAP_SEARCH_FILTER =\r
+                                                               "derby.authentication.ldap.searchFilter";\r
+       private static final String LDAP_SEARCH_AUTH_DN =\r
+                                                               "derby.authentication.ldap.searchAuthDN";\r
+       private static final String LDAP_SEARCH_AUTH_PW =\r
+                                                               "derby.authentication.ldap.searchAuthPW";\r
+       private static final String LDAP_LOCAL_USER_DN =\r
+                                                               "derby.user";\r
+       private static final String LDAP_SEARCH_FILTER_USERNAME =\r
+                                                               "%USERNAME%";\r
+\r
+       public LDAPAuthenticationSchemeImpl(JNDIAuthenticationService as, Properties dbProperties) {\r
+\r
+               super(as, dbProperties);\r
+       }\r
+\r
+       /**\r
+        * Authenticate the passed-in user's credentials.\r
+        *\r
+        * We authenticate against a LDAP Server.\r
+        *\r
+        *\r
+        * @param userName              The user's name used to connect to JBMS system\r
+        * @param userPassword  The user's password used to connect to JBMS system\r
+        * @param databaseName  The database which the user wants to connect to.\r
+        * @param info                  Additional jdbc connection info.\r
+        */\r
+       public boolean  authenticateUser(String userName,\r
+                                                                String userPassword,\r
+                                                                String databaseName,\r
+                                                                Properties info\r
+                                                               )\r
+                                                               throws java.sql.SQLException\r
+       {\r
+               if ( ((userName == null) || (userName.length() == 0)) ||\r
+                        ((userPassword == null) || (userPassword.length() == 0)) )\r
+               {\r
+                       // We don't tolerate 'guest' user for now as well as\r
+                       // null password.\r
+                       // If a null password is passed upon authenticating a user\r
+                       // through LDAP, then the LDAP server might consider this as\r
+                       // anonymous bind and therefore no authentication will be done\r
+                       // at all.\r
+                       return false;\r
+               }\r
+\r
+\r
+               Exception e;\r
+               try {\r
+                       Properties env = (Properties) initDirContextEnv.clone();\r
+                       String userDN = null;\r
+                       //\r
+                       // Retrieve the user's DN (Distinguished Name)\r
+                       // If we're asked to look it up locally, do it first\r
+                       // and if we don't find it, we go against the LDAP\r
+                       // server for a look-up (search)\r
+                       //\r
+                       if (useUserPropertyAsDN)\r
+                               userDN =\r
+                                       authenticationService.getProperty(\r
+                                               org.apache.derby.iapi.reference.Property.USER_PROPERTY_PREFIX);\r
+\r
+                       if (userDN == (String) null) {\r
+                               userDN = getDNFromUID(userName);\r
+                       }\r
+               \r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               if (SanityManager.DEBUG_ON(\r
+                                               AuthenticationServiceBase.AuthenticationTrace)) {\r
+                                       SanityManager.DEBUG(AuthenticationServiceBase.AuthenticationTrace,\r
+                                       "User DN = ["+ userDN+"]\n");\r
+                               }\r
+                       }\r
+\r
+                       env.put(Context.SECURITY_PRINCIPAL, userDN);\r
+                       env.put(Context.SECURITY_CREDENTIALS, userPassword);\r
+                       \r
+                       // Connect & authenticate (bind) to the LDAP server now\r
+\r
+                       // it is happening right here\r
+\r
+            DirContext ctx =   privInitialDirContext(env);\r
+          \r
+            \r
+\r
+                       // if the above was successfull, then username and\r
+                       // password must be correct\r
+                       return true;\r
+\r
+               } catch (javax.naming.AuthenticationException jndiae) {\r
+                       return false;\r
+\r
+               } catch (javax.naming.NameNotFoundException jndinnfe) {\r
+                       return false;\r
+\r
+               } catch (javax.naming.NamingException jndine) {\r
+                       e = jndine;\r
+               }\r
+\r
+               throw getLoginSQLException(e);\r
+       }\r
+\r
+       \r
+\r
+    /**\r
+     * Call new InitialDirContext in a privilege block\r
+     * @param env environment used to create the initial DirContext. Null indicates an empty environment.\r
+     * @return an initial DirContext using the supplied environment. \r
+     */\r
+    private DirContext privInitialDirContext(final Properties env) throws NamingException {\r
+        try {\r
+            return ((InitialDirContext)AccessController.doPrivileged(\r
+                    new PrivilegedExceptionAction() {\r
+                        public Object run() throws SecurityException, NamingException {\r
+                            return new InitialDirContext(env);\r
+                    }\r
+                }));\r
+    } catch (PrivilegedActionException pae) {\r
+            Exception e = pae.getException();\r
+       \r
+            if (e instanceof NamingException)\r
+                    throw (NamingException)e;\r
+            else\r
+                throw (SecurityException)e;\r
+        }   \r
+   \r
+    }   \r
+\r
+    /**\r
+        * This method basically tests and sets default/expected JNDI properties\r
+        * for the JNDI provider scheme (here it is LDAP).\r
+        *\r
+        **/\r
+       protected void setJNDIProviderProperties()\r
+       {\r
+\r
+               // check if we're told to use a different initial context factory\r
+               if (initDirContextEnv.getProperty(\r
+                                                       Context.INITIAL_CONTEXT_FACTORY) == (String) null)\r
+               {\r
+                       initDirContextEnv.put(Context.INITIAL_CONTEXT_FACTORY,\r
+                                                                         "com.sun.jndi.ldap.LdapCtxFactory");\r
+               }\r
+\r
+               // retrieve LDAP server name/port# and construct LDAP url\r
+               if (initDirContextEnv.getProperty(\r
+                                                       Context.PROVIDER_URL) == (String) null)\r
+               {\r
+                       // Now we construct the LDAP url and expect to find the LDAP Server\r
+                       // name.\r
+                       //\r
+                       String ldapServer = authenticationService.getProperty(\r
+                                               org.apache.derby.iapi.reference.Property.AUTHENTICATION_SERVER_PARAMETER);\r
+\r
+                       if (ldapServer == (String) null) {\r
+\r
+                               // we do expect a LDAP Server name to be configured\r
+                               Monitor.logTextMessage(\r
+                                       MessageId.AUTH_NO_LDAP_HOST_MENTIONED,\r
+                                                org.apache.derby.iapi.reference.Property.AUTHENTICATION_SERVER_PARAMETER);\r
+\r
+                               this.providerURL = dfltLDAPURL + "/";\r
+\r
+                       } else {\r
+\r
+                               if (ldapServer.startsWith(dfltLDAPURL) || ldapServer.startsWith("ldaps://") )\r
+                                       this.providerURL = ldapServer;\r
+                               else if (ldapServer.startsWith("//"))\r
+                                       this.providerURL = "ldap:" + ldapServer;\r
+                               else\r
+                                       this.providerURL = dfltLDAPURL + ldapServer;\r
+                       }\r
+                       initDirContextEnv.put(Context.PROVIDER_URL, providerURL);\r
+               }\r
+\r
+               // check if we should we use a particular authentication method\r
+               // we assume the ldap server supports this authentication method\r
+               // (Netscape DS 3.1.1 does not support CRAM-MD5 for instance)\r
+               if (initDirContextEnv.getProperty(\r
+                                                       Context.SECURITY_AUTHENTICATION) == (String) null)\r
+               {\r
+                       // set the default to be clear userName/Password as not of all the\r
+                       // LDAP server(s) support CRAM-MD5 (especially ldap v2 ones)\r
+                       // Netscape Directory Server 3.1.1 does not support CRAM-MD5\r
+                       // (told by Sun JNDI engineering). Netscape DS 4.0 allows SASL\r
+                       // plug-ins to be installed and that can be used as authentication\r
+                       // method.\r
+                       //\r
+                       initDirContextEnv.put(Context.SECURITY_AUTHENTICATION,\r
+                                                                         "simple"\r
+                                                                         );\r
+               }\r
+\r
+               // Retrieve and set the search base (root) DN to use on the ldap\r
+               // server.\r
+               String ldapSearchBase =\r
+                                       authenticationService.getProperty(LDAP_SEARCH_BASE);\r
+               if (ldapSearchBase != (String) null)\r
+                       this.searchBaseDN = ldapSearchBase;\r
+               else\r
+                       this.searchBaseDN = "";\r
+\r
+               // retrieve principal and credentials for the search bind as the\r
+               // user may not want to allow anonymous binds (for searches)\r
+               this.searchAuthDN =\r
+                                       authenticationService.getProperty(LDAP_SEARCH_AUTH_DN);\r
+               this.searchAuthPW =\r
+                                       authenticationService.getProperty(LDAP_SEARCH_AUTH_PW);\r
+\r
+               //\r
+               // Construct the LDAP search filter:\r
+               //\r
+               // If we were told to use a special search filther, we do so;\r
+               // otherwise we use our default search filter.\r
+               // The user may have set the search filter 3 different ways:\r
+               //\r
+               // - if %USERNAME% was found in the search filter, then we\r
+               // will substitute this with the passed-in uid at runtime.\r
+               //\r
+               // - if "derby.user" is the search filter value, then we\r
+               // will assume the user's DN can be found in the system or\r
+               // database property "derby.user.<uid>" . If the property\r
+               // does not exist, then we will do a normal lookup with our\r
+               // default search filter; otherwise we will perform an\r
+               // authenticated bind to the LDAP server using the found DN.\r
+               //\r
+               // - if neither of the 2 previous values were found, then we use\r
+               // our default search filter and we will substitute insert the\r
+               // uid passed at runtime into our default search filter.\r
+               //\r
+               String searchFilterProp =\r
+                                       authenticationService.getProperty(LDAP_SEARCH_FILTER);\r
+               \r
+               if (searchFilterProp == (String) null)\r
+               {\r
+                       // use our default search filter\r
+                       this.leftSearchFilter = "(&(objectClass=inetOrgPerson)(uid=";\r
+                       this.rightSearchFilter = "))";\r
+\r
+               } else if (StringUtil.SQLEqualsIgnoreCase(searchFilterProp,LDAP_LOCAL_USER_DN)) {\r
+\r
+                       // use local user DN in derby.user.<uid>\r
+                       this.leftSearchFilter = "(&(objectClass=inetOrgPerson)(uid=";\r
+                       this.rightSearchFilter = "))";\r
+                       this.useUserPropertyAsDN = true;\r
+\r
+               } else if (searchFilterProp.indexOf(\r
+                                                                       LDAP_SEARCH_FILTER_USERNAME) != -1) {\r
+\r
+                       // user has set %USERNAME% in the search filter\r
+                       this.leftSearchFilter = searchFilterProp.substring(0,\r
+                               searchFilterProp.indexOf(LDAP_SEARCH_FILTER_USERNAME));\r
+                       this.rightSearchFilter = searchFilterProp.substring(\r
+                               searchFilterProp.indexOf(LDAP_SEARCH_FILTER_USERNAME)+\r
+                               (int) LDAP_SEARCH_FILTER_USERNAME.length());\r
+\r
+\r
+               } else  { // add this search filter to ours\r
+\r
+                       // complement this search predicate to ours\r
+                       this.leftSearchFilter = "(&("+searchFilterProp+")"+\r
+                                                                       "(objectClass=inetOrgPerson)(uid=";\r
+                       this.rightSearchFilter = "))";\r
+\r
+               }\r
+\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (SanityManager.DEBUG_ON(\r
+                                               AuthenticationServiceBase.AuthenticationTrace)) {\r
+\r
+                               java.io.PrintWriter iDbgStream =\r
+                                       SanityManager.GET_DEBUG_STREAM();\r
+\r
+                               iDbgStream.println(\r
+                                                               "\n\n+ LDAP Authentication Configuration:\n"+\r
+                                                               "   - provider URL ["+this.providerURL+"]\n"+\r
+                                                               "   - search base ["+this.searchBaseDN+"]\n"+\r
+                                                               "   - search filter to be [" +\r
+                                                               this.leftSearchFilter + "<uid>" +\r
+                                                               this.rightSearchFilter + "]\n" +\r
+                                                               "   - use local DN [" +\r
+                                                               (useUserPropertyAsDN ? "true" : "false") +\r
+                                                               "]\n"\r
+                                                               );\r
+                       }\r
+               }\r
+\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (SanityManager.DEBUG_ON(\r
+                                               AuthenticationServiceBase.AuthenticationTrace)) {\r
+                             \r
+                                // This tracing needs some investigation and cleanup.\r
+                                // 1) It creates the file in user.dir instead of derby.system.home\r
+                                // 2) It doesn't seem to work. The file is empty after successful\r
+                                //    and unsuccessful ldap connects.  Perhaps the fileOutputStream\r
+                                // is never flushed and closed.\r
+                                // I (Kathey Marsden) wrapped this in a priv block and kept the previous\r
+                                // behaviour that it will not stop processing if file \r
+                                // creation fails. Perhaps that should be investigated as well.\r
+                                FileOutputStream fos = null;\r
+                                try {\r
+                                    fos =  ((FileOutputStream)AccessController.doPrivileged(\r
+                                                new PrivilegedExceptionAction() {\r
+                                                    public Object run() throws SecurityException, java.io.IOException {\r
+                                                        return new  FileOutputStream("DerbyLDAP.out");\r
+                                                    }\r
+                                                }));\r
+                                } catch (PrivilegedActionException pae) {\r
+                                    // If trace file creation fails do not stop execution.                                    \r
+                                }\r
+                                if (fos != null)\r
+                                    initDirContextEnv.put("com.sun.naming.ldap.trace.ber",fos);\r
+\r
+                               \r
+                       }\r
+               }\r
+       }\r
+\r
+       \r
+       \r
+       \r
+\r
+       /**\r
+        * Search for the full user's DN in the LDAP server.\r
+        * LDAP server bind may or not be anonymous.\r
+        *\r
+        * If the admin does not want us to do anonymous bind/search, then we\r
+        * must have been given principal/credentials in order to successfully\r
+        * bind to perform the user's DN search.\r
+        *\r
+        * @exception NamingException if could not retrieve the user DN.\r
+        **/\r
+       private String getDNFromUID(String uid)\r
+               throws javax.naming.NamingException\r
+       {\r
+               //\r
+               // We bind to the LDAP server here\r
+               // Note that this bind might be anonymous (if anonymous searches\r
+               // are allowed in the LDAP server, or authenticated if we were\r
+               // told/configured to.\r
+               //\r
+               Properties env = null;\r
+               if (this.searchAuthDN != (String) null) {\r
+                       env = (Properties) initDirContextEnv.clone();\r
+                       env.put(Context.SECURITY_PRINCIPAL, this.searchAuthDN);\r
+                       env.put(Context.SECURITY_CREDENTIALS, this.searchAuthPW);\r
+               }\r
+               else\r
+                       env = initDirContextEnv;\r
+\r
+               DirContext ctx = privInitialDirContext(env);\r
+\r
+               // Construct Search Filter\r
+               SearchControls ctls = new SearchControls();\r
+               // Set-up a LDAP subtree search scope\r
+               ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);\r
+\r
+               // Just retrieve the DN\r
+               ctls.setReturningAttributes(attrDN);\r
+\r
+               String searchFilter =\r
+                                               this.leftSearchFilter + uid + this.rightSearchFilter; \r
+               NamingEnumeration results =\r
+                                               ctx.search(searchBaseDN, searchFilter, ctls);\r
+                       \r
+               // If we did not find anything then login failed\r
+               if (results == null || !results.hasMore())\r
+                       throw new NameNotFoundException();\r
+                       \r
+               SearchResult result = (SearchResult)results.next();\r
+               \r
+               if (results.hasMore())\r
+               {\r
+                       // This is a login failure as we cannot assume the first one\r
+                       // is the valid one.\r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               if (SanityManager.DEBUG_ON(\r
+                                               AuthenticationServiceBase.AuthenticationTrace)) {\r
+\r
+                                       java.io.PrintWriter iDbgStream =\r
+                                               SanityManager.GET_DEBUG_STREAM();\r
+\r
+                                       iDbgStream.println(\r
+                                               " - LDAP Authentication request failure: "+\r
+                                               "search filter [" + searchFilter + "]"+\r
+                                               ", retrieve more than one occurence in "+\r
+                                               "LDAP server [" + this.providerURL + "]");\r
+                               }\r
+                       }\r
+                       throw new NameNotFoundException();\r
+               }\r
+\r
+               NameParser parser = ctx.getNameParser(searchBaseDN);\r
+               Name userDN = parser.parse(searchBaseDN);\r
+\r
+               if (userDN == (Name) null)\r
+                       // This should not happen in theory\r
+                       throw new NameNotFoundException();\r
+               else\r
+                       userDN.addAll(parser.parse(result.getName()));\r
+               \r
+               // Return the full user's DN\r
+               return userDN.toString();\r
+       }\r
+}\r