You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by ad...@apache.org on 2008/06/20 19:09:20 UTC

svn commit: r669994 - in /ofbiz/trunk: applications/party/config/ applications/party/webapp/partymgr/party/ framework/common/config/ framework/common/servicedef/ framework/common/src/org/ofbiz/common/login/ framework/security/config/ framework/security...

Author: adrianc
Date: Fri Jun 20 10:09:19 2008
New Revision: 669994

URL: http://svn.apache.org/viewvc?rev=669994&view=rev
Log:
Added LDAP user authentication, based on work contributed by Mohamed Amine Azzi and Torsten Schlabach - https://issues.apache.org/jira/browse/OFBIZ-811.

Internationalization note: this commit contains new UI labels.

Added:
    ofbiz/trunk/framework/common/src/org/ofbiz/common/login/LdapAuthenticationServices.java   (with props)
    ofbiz/trunk/framework/security/config/jndiLdap.properties   (with props)
Modified:
    ofbiz/trunk/applications/party/config/PartyUiLabels.xml
    ofbiz/trunk/applications/party/webapp/partymgr/party/PartyForms.xml
    ofbiz/trunk/framework/common/config/SecurityextUiLabels.xml
    ofbiz/trunk/framework/common/servicedef/services.xml
    ofbiz/trunk/framework/common/src/org/ofbiz/common/login/LoginServices.java
    ofbiz/trunk/framework/security/config/security.properties
    ofbiz/trunk/framework/security/entitydef/entitymodel.xml

Modified: ofbiz/trunk/applications/party/config/PartyUiLabels.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/party/config/PartyUiLabels.xml?rev=669994&r1=669993&r2=669994&view=diff
==============================================================================
--- ofbiz/trunk/applications/party/config/PartyUiLabels.xml (original)
+++ ofbiz/trunk/applications/party/config/PartyUiLabels.xml Fri Jun 20 10:09:19 2008
@@ -1043,6 +1043,9 @@
         <value xml:lang="th">ผลรวมของปีที่มีประสบการณ์การทำงาน</value>
         <value xml:lang="zh">工作经历年数合计</value>
     </property>
+    <property key="FormFieldTitle_userLdapDn">
+        <value xml:lang="en">LDAP Distinguished Name</value>
+    </property>
     <property key="FormFieldTitle_userLoginId">
         <value xml:lang="en">User Login Id</value>
         <value xml:lang="fr">Utilisateur de connexion</value>

Modified: ofbiz/trunk/applications/party/webapp/partymgr/party/PartyForms.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/applications/party/webapp/partymgr/party/PartyForms.xml?rev=669994&r1=669993&r2=669994&view=diff
==============================================================================
--- ofbiz/trunk/applications/party/webapp/partymgr/party/PartyForms.xml (original)
+++ ofbiz/trunk/applications/party/webapp/partymgr/party/PartyForms.xml Fri Jun 20 10:09:19 2008
@@ -162,9 +162,13 @@
 
     <form name="updateUserLoginSecurity" type="single" target="updateUserLoginSecurity" default-map-name="editUserLogin"
         header-row-style="header-row" default-table-style="basic-table">
+        <actions>
+            <property-to-field field="ldapEnabled" resource="security" property="security.ldap.enable"/>
+        </actions>
         <auto-fields-service service-name="updateUserLoginSecurity"/>
         <field name="partyId"><hidden/></field>
         <field name="userLoginId"><hidden/></field>
+        <field name="userLdapDn" use-when="&quot;true&quot;.equals(ldapEnabled)"><text/></field>
         <field name="submitButton" title="${uiLabelMap.CommonSave}" widget-style="smallSubmit"><submit button-type="text-link"/></field>
         <field name="cancelLink" title="${uiLabelMap.CommonEmptyHeader}" widget-style="smallSubmit"><hyperlink target="${donePage}?partyId=${partyId}" also-hidden="false" description="${uiLabelMap.CommonCancelDone}"/></field>
     </form>

Modified: ofbiz/trunk/framework/common/config/SecurityextUiLabels.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/common/config/SecurityextUiLabels.xml?rev=669994&r1=669993&r2=669994&view=diff
==============================================================================
--- ofbiz/trunk/framework/common/config/SecurityextUiLabels.xml (original)
+++ ofbiz/trunk/framework/common/config/SecurityextUiLabels.xml Fri Jun 20 10:09:19 2008
@@ -611,4 +611,7 @@
         <value xml:lang="th">มันจะไม่สามารถใช้ได้อีกครั้ง ${reEnableTime}.</value>
         <value xml:lang="zh">将重新启用 ${reEnableTime}。</value>
     </property>
+    <property key="loginservices.ldap_authentication_failed">
+        <value xml:lang="en">LDAP authentication failed.</value>
+    </property>
 </resource>

Modified: ofbiz/trunk/framework/common/servicedef/services.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/common/servicedef/services.xml?rev=669994&r1=669993&r2=669994&view=diff
==============================================================================
--- ofbiz/trunk/framework/common/servicedef/services.xml (original)
+++ ofbiz/trunk/framework/common/servicedef/services.xml Fri Jun 20 10:09:19 2008
@@ -398,6 +398,7 @@
         <attribute name="enabled" type="String" mode="IN" optional="false"/>
         <attribute name="disabledDateTime" type="java.sql.Timestamp" mode="IN" optional="true"/>
         <attribute name="successiveFailedLogins" type="Long" mode="IN" optional="true"/>
+        <attribute name="userLdapDn" type="String" mode="IN" optional="true"/>
     </service>
 
     <!-- common permission services -->

Added: ofbiz/trunk/framework/common/src/org/ofbiz/common/login/LdapAuthenticationServices.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/common/src/org/ofbiz/common/login/LdapAuthenticationServices.java?rev=669994&view=auto
==============================================================================
--- ofbiz/trunk/framework/common/src/org/ofbiz/common/login/LdapAuthenticationServices.java (added)
+++ ofbiz/trunk/framework/common/src/org/ofbiz/common/login/LdapAuthenticationServices.java Fri Jun 20 10:09:19 2008
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.ofbiz.common.login;
+
+import java.util.Map;
+import java.util.Properties;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.transaction.Transaction;
+
+import org.ofbiz.base.crypto.HashCrypt;
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.UtilProperties;
+import org.ofbiz.base.util.UtilValidate;
+import org.ofbiz.entity.GenericDelegator;
+import org.ofbiz.entity.GenericEntityException;
+import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.transaction.GenericTransactionException;
+import org.ofbiz.entity.transaction.TransactionUtil;
+import org.ofbiz.service.DispatchContext;
+
+/** LDAP Authentication Services.
+ */
+public class LdapAuthenticationServices {
+
+    public static final String module = LdapAuthenticationServices.class.getName();
+
+    public static boolean userLogin(DispatchContext ctx, Map<String, Object> context) {
+        Debug.logVerbose("Starting LDAP authentication", module);
+        Properties env = UtilProperties.getProperties("jndiLdap");
+        String username = (String) context.get("login.username");
+        if (username == null) {
+            username = (String) context.get("username");
+        }
+        String password = (String) context.get("login.password");
+        if (password == null) {
+            password = (String) context.get("password");
+        }
+        String dn = null;
+        GenericDelegator delegator = ctx.getDelegator();
+        boolean isServiceAuth = context.get("isServiceAuth") != null && ((Boolean) context.get("isServiceAuth")).booleanValue();
+        GenericValue userLogin = null;
+        try {
+            userLogin = delegator.findOne("UserLogin", isServiceAuth, "userLoginId", username);
+        } catch (GenericEntityException e) {
+            Debug.logWarning(e, "", module);
+        }
+        if (userLogin != null) {
+            dn = userLogin.getString("userLdapDn");
+        }
+        if (UtilValidate.isEmpty(dn)) {
+            String dnTemplate = (String) env.get("ldap.dn.template");
+            if (dnTemplate != null) {
+                dn = dnTemplate.replace("%u", username);
+            }
+            Debug.logVerbose("Using DN template: " + dn, module);
+        } else {
+            Debug.logVerbose("Using UserLogin.userLdapDn: " + dn, module);
+        }
+        env.put(Context.SECURITY_PRINCIPAL, dn);
+        env.put(Context.SECURITY_CREDENTIALS, password);
+        try {
+            // Create initial context
+            DirContext ldapCtx = new InitialDirContext(env);
+            ldapCtx.close();
+        } catch (NamingException e) {
+            Debug.logVerbose("LDAP authentication failed: " + e.getMessage(), module);
+            return false;
+        }
+        Debug.logVerbose("LDAP authentication succeeded", module);
+        if (!"true".equals(env.get("ldap.synchronize.passwords"))) {
+            return true;
+        }
+        // Synchronize user's OFBiz password with user's LDAP password
+        if (userLogin != null) {
+            boolean useEncryption = "true".equals(UtilProperties.getPropertyValue("security.properties", "password.encrypt"));
+            String encodedPassword = useEncryption ? HashCrypt.getDigestHash(password, LoginServices.getHashType()) : password;
+            String encodedPasswordOldFunnyHexEncode = useEncryption ? HashCrypt.getDigestHashOldFunnyHexEncode(password, LoginServices.getHashType()) : password;
+            String encodedPasswordUsingDbHashType = encodedPassword;
+            String currentPassword = userLogin.getString("currentPassword");
+            if (useEncryption && currentPassword != null && currentPassword.startsWith("{")) {
+                String dbHashType = HashCrypt.getHashTypeFromPrefix(currentPassword);
+                if (dbHashType != null) {
+                    encodedPasswordUsingDbHashType = HashCrypt.getDigestHash(password, dbHashType);
+                }
+            }
+            boolean samePassword = currentPassword != null &&
+                    (HashCrypt.removeHashTypePrefix(encodedPassword).equals(HashCrypt.removeHashTypePrefix(currentPassword)) ||
+                            HashCrypt.removeHashTypePrefix(encodedPasswordOldFunnyHexEncode).equals(HashCrypt.removeHashTypePrefix(currentPassword)) ||
+                            HashCrypt.removeHashTypePrefix(encodedPasswordUsingDbHashType).equals(HashCrypt.removeHashTypePrefix(currentPassword)) ||
+                        ("true".equals(UtilProperties.getPropertyValue("security.properties", "password.accept.encrypted.and.plain")) && password.equals(currentPassword)));
+            if (!samePassword) {
+                Debug.logVerbose("Starting password synchronization", module);
+                userLogin.set("currentPassword", useEncryption ? HashCrypt.getDigestHash(password, LoginServices.getHashType()) : password, false);
+                Transaction parentTx = null;
+                boolean beganTransaction = false;
+                try {
+                    try {
+                        parentTx = TransactionUtil.suspend();
+                    } catch (GenericTransactionException e) {
+                        Debug.logError(e, "Could not suspend transaction: " + e.getMessage(), module);
+                    }                                
+                    try {
+                        beganTransaction = TransactionUtil.begin();
+                        userLogin.store();
+                    } catch (GenericEntityException e) {
+                        Debug.logError(e, "Error saving UserLogin", module);
+                        try {
+                            TransactionUtil.rollback(beganTransaction, "Error saving UserLogin", e);
+                        } catch (GenericTransactionException e2) {
+                            Debug.logError(e2, "Could not rollback nested transaction: " + e2.getMessage(), module);
+                        }
+                    } finally {
+                        try {
+                            TransactionUtil.commit(beganTransaction);
+                            Debug.logVerbose("Password synchronized", module);
+                        } catch (GenericTransactionException e) {
+                            Debug.logError(e, "Could not commit nested transaction: " + e.getMessage(), module);
+                        }
+                    }
+                } finally {
+                    if (parentTx != null) {
+                        try {
+                            TransactionUtil.resume(parentTx);
+                            Debug.logVerbose("Resumed the parent transaction.", module);
+                        } catch (GenericTransactionException e) {
+                            Debug.logError(e, "Could not resume parent nested transaction: " + e.getMessage(), module);
+                        }
+                    }
+                }
+            }
+        }
+        return true;
+    }
+}

Propchange: ofbiz/trunk/framework/common/src/org/ofbiz/common/login/LdapAuthenticationServices.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: ofbiz/trunk/framework/common/src/org/ofbiz/common/login/LoginServices.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/common/src/org/ofbiz/common/login/LoginServices.java?rev=669994&r1=669993&r2=669994&view=diff
==============================================================================
--- ofbiz/trunk/framework/common/src/org/ofbiz/common/login/LoginServices.java (original)
+++ ofbiz/trunk/framework/common/src/org/ofbiz/common/login/LoginServices.java Fri Jun 20 10:09:19 2008
@@ -64,10 +64,22 @@
      * @return Map of results including (userLogin) GenericValue object
      */
     public static Map userLogin(DispatchContext ctx, Map context) {
-        Map result = FastMap.newInstance();
-        GenericDelegator delegator = ctx.getDelegator();
         Locale locale = (Locale) context.get("locale");
 
+        // Authenticate to LDAP if configured to do so
+        if ("true".equals(UtilProperties.getPropertyValue("security", "security.ldap.enable"))) {
+            if (!LdapAuthenticationServices.userLogin(ctx, context)) {
+                String errMsg = UtilProperties.getMessage(resource, "loginservices.ldap_authentication_failed", locale);
+                if ("true".equals(UtilProperties.getPropertyValue("security", "security.ldap.fail.login"))) {
+                    return ServiceUtil.returnError(errMsg);
+                } else {
+                    Debug.logInfo(errMsg, module);
+                }
+            }
+        }
+        
+        Map result = FastMap.newInstance();
+        GenericDelegator delegator = ctx.getDelegator();
         boolean useEncryption = "true".equals(UtilProperties.getPropertyValue("security.properties", "password.encrypt"));
 
         // if isServiceAuth is not specified, default to not a service auth
@@ -744,6 +756,9 @@
         if (context.containsKey("successiveFailedLogins")) {
             userLoginToUpdate.set("successiveFailedLogins", context.get("successiveFailedLogins"), true);
         }
+        if (context.containsKey("userLdapDn")) {
+            userLoginToUpdate.set("userLdapDn", context.get("userLdapDn"), true);
+        }
 
         // if was disabled and we are enabling it, clear disabledDateTime
         if (!wasEnabled && "Y".equals(context.get("enabled"))) {

Added: ofbiz/trunk/framework/security/config/jndiLdap.properties
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/config/jndiLdap.properties?rev=669994&view=auto
==============================================================================
--- ofbiz/trunk/framework/security/config/jndiLdap.properties (added)
+++ ofbiz/trunk/framework/security/config/jndiLdap.properties Fri Jun 20 10:09:19 2008
@@ -0,0 +1,38 @@
+###############################################################################
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+###############################################################################
+####
+# OFBiz LDAP Authentication Settings
+####
+
+# JNDI LDAP settings. Change the following line to
+# point to your LDAP server.
+java.naming.provider.url=ldap://localhost:389
+java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
+java.naming.security.authentication=simple
+com.sun.jndi.ldap.connect.timeout=5000
+
+# Distinguished Name template. This is used as a default if
+# UserLogin.userLdapDn is empty.
+# The %u placeholder will be replaced by the user's login name,
+# then the resulting string will be used to authenticate the user.
+ldap.dn.template=cn=%u,ou=system
+
+# The following property controls whether the user's OFBiz password
+# is synchronized with the user's LDAP password.
+ldap.synchronize.passwords=true

Propchange: ofbiz/trunk/framework/security/config/jndiLdap.properties
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: ofbiz/trunk/framework/security/config/security.properties
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/config/security.properties?rev=669994&r1=669993&r2=669994&view=diff
==============================================================================
--- ofbiz/trunk/framework/security/config/security.properties (original)
+++ ofbiz/trunk/framework/security/config/security.properties Fri Jun 20 10:09:19 2008
@@ -60,6 +60,12 @@
 username.lowercase=false
 password.lowercase=false
 
+# -- Use LDAP for user authentication? --
+security.ldap.enable=false
+
+# -- Fail login if LDAP authentication fails? --
+security.ldap.fail.login=false
+
 # -- should we allow x509 certificate login
 security.login.cert.allow=true
 
@@ -70,4 +76,4 @@
 security.login.cert.pattern=^(\\w*\\s?\\w*)\\W*.*$
 
 # -- Hours after which EmailAdressVerification should expire
-email_verification.expire.hours=48
\ No newline at end of file
+email_verification.expire.hours=48

Modified: ofbiz/trunk/framework/security/entitydef/entitymodel.xml
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/security/entitydef/entitymodel.xml?rev=669994&r1=669993&r2=669994&view=diff
==============================================================================
--- ofbiz/trunk/framework/security/entitydef/entitymodel.xml (original)
+++ ofbiz/trunk/framework/security/entitydef/entitymodel.xml Fri Jun 20 10:09:19 2008
@@ -75,6 +75,9 @@
       <field name="lastTimeZone" type="id-long"></field>
       <field name="disabledDateTime" type="date-time"></field>
       <field name="successiveFailedLogins" type="numeric"></field>
+      <field name="userLdapDn" type="id-vlong-ne">
+          <description>The user's LDAP Distinguished Name - used for LDAP authentication</description>
+      </field>
       <prim-key field="userLoginId"/>
     </entity>
     <entity entity-name="UserLoginPasswordHistory"



Re: svn commit: r669994 - in /ofbiz/trunk: applications/party/config/ applications/party/webapp/partymgr/party/ framework/common/config/ framework/common/servicedef/ framework/common/src/org/ofbiz/common/login/ framework/security/config/ framework/security...

Posted by David E Jones <jo...@undersunconsulting.com>.
This looks good Adrian, thanks for working on it.

This was on my own little list of things I'd like to see added to the  
framework before we do the framework-only release, so I'm really happy  
to see it in!

-David


On Jun 20, 2008, at 11:09 AM, adrianc@apache.org wrote:

> Author: adrianc
> Date: Fri Jun 20 10:09:19 2008
> New Revision: 669994
>
> URL: http://svn.apache.org/viewvc?rev=669994&view=rev
> Log:
> Added LDAP user authentication, based on work contributed by Mohamed  
> Amine Azzi and Torsten Schlabach - https://issues.apache.org/jira/browse/OFBIZ-811 
> .
>
> Internationalization note: this commit contains new UI labels.
>
> Added:
>    ofbiz/trunk/framework/common/src/org/ofbiz/common/login/ 
> LdapAuthenticationServices.java   (with props)
>    ofbiz/trunk/framework/security/config/jndiLdap.properties   (with  
> props)
> Modified:
>    ofbiz/trunk/applications/party/config/PartyUiLabels.xml
>    ofbiz/trunk/applications/party/webapp/partymgr/party/PartyForms.xml
>    ofbiz/trunk/framework/common/config/SecurityextUiLabels.xml
>    ofbiz/trunk/framework/common/servicedef/services.xml
>    ofbiz/trunk/framework/common/src/org/ofbiz/common/login/ 
> LoginServices.java
>    ofbiz/trunk/framework/security/config/security.properties
>    ofbiz/trunk/framework/security/entitydef/entitymodel.xml