Author: jaz
Date: Tue Apr 21 21:20:16 2009
New Revision: 767300

URL: http://svn.apache.org/viewvc?rev=767300&view=rev
Log:
dded touch points to userLogin & changePassword services to call AuthHelper for 
external authentication module support. Part of the new Authentication API. 
This commit also includes the service changes for the new 'externalAuthId' 
field on UserLogin.

Part 1: define the API for external authentication modules (done rev 767281)
Part 2: update LoginServices to call AuthHelper for external modules (done)
Part 3: create example Authenticator (in progress)
Part 4: update LDAP implementation to use new API (not started)


Modified:
    ofbiz/trunk/framework/common/servicedef/services.xml
    
ofbiz/trunk/framework/common/src/org/ofbiz/common/authentication/api/Authenticator.java
    ofbiz/trunk/framework/common/src/org/ofbiz/common/login/LoginServices.java

Modified: ofbiz/trunk/framework/common/servicedef/services.xml
URL: 
http://svn.apache.org/viewvc/ofbiz/trunk/framework/common/servicedef/services.xml?rev=767300&r1=767299&r2=767300&view=diff
==============================================================================
--- ofbiz/trunk/framework/common/servicedef/services.xml (original)
+++ ofbiz/trunk/framework/common/servicedef/services.xml Tue Apr 21 21:20:16 
2009
@@ -347,6 +347,7 @@
         <attribute name="currentPasswordVerify" type="String" mode="IN" 
optional="false"/>
         <attribute name="passwordHint" type="String" mode="IN" 
optional="true"/>
         <attribute name="requirePasswordChange" type="String" mode="IN" 
optional="true"/>
+        <attribute name="externalAuthId" type="String" mode="IN" 
optional="true"/>
         <attribute name="partyId" type="String" mode="IN" optional="true"/>
     </service>
     <service name="updateUserLoginId" engine="java" 
location="org.ofbiz.common.login.LoginServices" invoke="updateUserLoginId" 
auth="true">
@@ -371,6 +372,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="externalAuthId" type="String" mode="IN" 
optional="true"/>
         <attribute name="userLdapDn" type="String" mode="IN" optional="true"/>
     </service>
 

Modified: 
ofbiz/trunk/framework/common/src/org/ofbiz/common/authentication/api/Authenticator.java
URL: 
http://svn.apache.org/viewvc/ofbiz/trunk/framework/common/src/org/ofbiz/common/authentication/api/Authenticator.java?rev=767300&r1=767299&r2=767300&view=diff
==============================================================================
--- 
ofbiz/trunk/framework/common/src/org/ofbiz/common/authentication/api/Authenticator.java
 (original)
+++ 
ofbiz/trunk/framework/common/src/org/ofbiz/common/authentication/api/Authenticator.java
 Tue Apr 21 21:20:16 2009
@@ -56,6 +56,7 @@
 
     /**
      * Reads user information and syncs it to OFBiz (i.e. UserLogin, Person, 
etc)
+     * Note: when creating a UserLogin object, be sure to set 'externalAuthId'
      * @param username User's username
      * @throws AuthenticatorException user synchronization fails
      */

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=767300&r1=767299&r2=767300&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 
Tue Apr 21 21:20:16 2009
@@ -23,6 +23,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.io.Serializable;
 
 import javax.transaction.Transaction;
 
@@ -47,10 +48,13 @@
 import org.ofbiz.entity.util.EntityFindOptions;
 import org.ofbiz.entity.util.EntityListIterator;
 import org.ofbiz.security.Security;
+import org.ofbiz.common.authentication.api.AuthenticatorException;
 import org.ofbiz.service.DispatchContext;
 import org.ofbiz.service.ModelService;
 import org.ofbiz.service.ServiceUtil;
+import org.ofbiz.service.LocalDispatcher;
 import org.ofbiz.webapp.control.LoginWorker;
+import org.ofbiz.common.authentication.AuthHelper;
 
 /**
  * <b>Title:</b> Login Services
@@ -64,9 +68,16 @@
      * @return Map of results including (userLogin) GenericValue object
      */
     public static Map<String, Object> userLogin(DispatchContext ctx, 
Map<String, ?> context) {
+        LocalDispatcher dispatcher = ctx.getDispatcher();
         Locale locale = (Locale) context.get("locale");
 
+        // load the external auth modules -- note: this will only run once and 
cache the objects
+        if (!AuthHelper.authenticatorsLoaded()) {
+            AuthHelper.loadAuthenticators(dispatcher);
+        }
+
         // Authenticate to LDAP if configured to do so
+        // TODO: this should be moved to using the NEW Authenticator API
         if ("true".equals(UtilProperties.getPropertyValue("security", 
"security.ldap.enable"))) {
             if (!LdapAuthenticationServices.userLogin(ctx, context)) {
                 String errMsg = UtilProperties.getMessage(resource, 
"loginservices.ldap_authentication_failed", locale);
@@ -118,6 +129,23 @@
                     Debug.logWarning(e, "", module);
                 }
 
+
+                // see if any external auth modules want to sync the user info
+                if (userLogin == null) {
+                    try {
+                        AuthHelper.syncUser(username);
+                    } catch (AuthenticatorException e) {
+                        Debug.logWarning(e, module);
+                    }
+
+                    // check the user login object again
+                    try {
+                        userLogin = delegator.findOne("UserLogin", 
isServiceAuth, "userLoginId", username);
+                    } catch (GenericEntityException e) {
+                        Debug.logWarning(e, "", module);
+                    }
+                }
+
                 if (userLogin != null) {
                     String encodedPassword = useEncryption ? 
HashCrypt.getDigestHash(password, getHashType()) : password;
                     String encodedPasswordOldFunnyHexEncode = useEncryption ? 
HashCrypt.getDigestHashOldFunnyHexEncode(password, getHashType()) : password;
@@ -170,9 +198,21 @@
                             userLogin.set("enabled", "Y");
                         }
 
+                        // attempt to authenticate with Authenticator class(es)
+                        boolean authFatalError = false;
+                        boolean externalAuth = false;
+                        try {
+                            externalAuth = AuthHelper.authenticate(username, 
password, isServiceAuth);
+                        } catch (AuthenticatorException e) {
+                            // fatal error -- or single authenticator found -- 
fail now
+                            Debug.logWarning(e, module);
+                            authFatalError = true;
+
+                        }
                         // if the password.accept.encrypted.and.plain property 
in security is set to true allow plain or encrypted passwords
                         // if this is a system account don't bother checking 
the passwords
-                        if ((userLogin.get("currentPassword") != null &&
+                        // if externalAuth passed; this is run as well
+                        if ((!authFatalError && externalAuth) || 
(userLogin.get("currentPassword") != null &&
                             
(HashCrypt.removeHashTypePrefix(encodedPassword).equals(HashCrypt.removeHashTypePrefix(currentPassword))
 ||
                                     
HashCrypt.removeHashTypePrefix(encodedPasswordOldFunnyHexEncode).equals(HashCrypt.removeHashTypePrefix(currentPassword))
 ||
                                     
HashCrypt.removeHashTypePrefix(encodedPasswordUsingDbHashType).equals(HashCrypt.removeHashTypePrefix(currentPassword))
 ||
@@ -213,7 +253,8 @@
 
                             // password is incorrect, but this may be the 
result of a stale cache entry,
                             // so lets clear the cache and try again if this 
is the first pass
-                            if (isServiceAuth && passNumber <= 1) {
+                            // but only if authFatalError is not true; this 
would mean the single authenticator failed
+                            if (!authFatalError && isServiceAuth && passNumber 
<= 1) {
                                 delegator.clearCacheLine("UserLogin", 
UtilMisc.toMap("userLoginId", username));
                                 repeat = true;
                                 continue;
@@ -357,9 +398,29 @@
                         }
                     }
                 } else {
-                    // userLogin record not found, user does not exist
-                    errMsg = UtilProperties.getMessage(resource, 
"loginservices.user_not_found", locale);
-                    Debug.logInfo("[LoginServices.userLogin] : Invalid User : 
" + errMsg, module);
+                    // no userLogin object; there may be a non-syncing 
authenticator
+                    boolean externalAuth = false;
+                    try {
+                        externalAuth = AuthHelper.authenticate(username, 
password, isServiceAuth);
+                    } catch (AuthenticatorException e) {
+                        errMsg = e.getMessage();
+                        Debug.logError(e, "External Authenticator had fatal 
exception : " + e.getMessage(), module);
+                    }
+                    if (externalAuth) {
+                        // external auth passed - create a placeholder object 
for session
+                        userLogin = delegator.makeValue("UserLogin");
+                        userLogin.set("userLoginId", username);
+                        userLogin.set("enabled", "Y");
+                        userLogin.set("hasLoggedOut", "N");
+                        result.put("userLogin", userLogin);
+                        result.put(ModelService.RESPONSE_MESSAGE, 
ModelService.RESPOND_SUCCESS);
+                        //TODO: more than this is needed to support 100% 
external authentication
+                        //TODO: party + security information is needed; 
Userlogin will need to be stored
+                    } else {
+                        // userLogin record not found, user does not exist
+                        errMsg = UtilProperties.getMessage(resource, 
"loginservices.user_not_found", locale);
+                        Debug.logInfo("[LoginServices.userLogin] : Invalid 
User : " + errMsg, module);
+                    }
                 }
             }
         }
@@ -433,6 +494,7 @@
         String enabled = (String) context.get("enabled");
         String passwordHint = (String) context.get("passwordHint");
         String requirePasswordChange = (String) 
context.get("requirePasswordChange");
+        String externalAuthId = (String) context.get("externalAuthId");
         String errMsg = null;
 
         // security: don't create a user login if the specified partyId (if 
not empty) already exists
@@ -466,6 +528,7 @@
         checkNewPassword(null, null, currentPassword, currentPasswordVerify, 
passwordHint, errorMessageList, true, locale);
 
         GenericValue userLoginToCreate = delegator.makeValue("UserLogin", 
UtilMisc.toMap("userLoginId", userLoginId));
+        userLoginToCreate.set("externalAuthId", externalAuthId);
         userLoginToCreate.set("passwordHint", passwordHint);
         userLoginToCreate.set("enabled", enabled);
         userLoginToCreate.set("requirePasswordChange", requirePasswordChange);
@@ -516,6 +579,11 @@
         GenericValue loggedInUserLogin = (GenericValue) 
context.get("userLogin");
         Locale locale = (Locale) context.get("locale");
 
+        // load the external auth modules -- note: this will only run once and 
cache the objects
+        if (!AuthHelper.authenticatorsLoaded()) {
+            AuthHelper.loadAuthenticators(ctx.getDispatcher());
+        }
+
         boolean useEncryption = 
"true".equals(UtilProperties.getPropertyValue("security.properties", 
"password.encrypt"));
         boolean adminUser = false;
 
@@ -538,6 +606,11 @@
             adminUser = true;
         }
 
+        String currentPassword = (String) context.get("currentPassword");
+        String newPassword = (String) context.get("newPassword");
+        String newPasswordVerify = (String) context.get("newPasswordVerify");
+        String passwordHint = (String) context.get("passwordHint");
+
         GenericValue userLoginToUpdate = null;
 
         try {
@@ -549,15 +622,34 @@
         }
 
         if (userLoginToUpdate == null) {
-            Map<String, String> messageMap = UtilMisc.toMap("userLoginId", 
userLoginId);
-            errMsg = 
UtilProperties.getMessage(resource,"loginservices.could_not_change_password_userlogin_with_id_not_exist",
 messageMap, locale);
-            return ServiceUtil.returnError(errMsg);
-        }
+            // this may be a full external authenticator; first try 
authenticating
+            boolean authenticated = false;
+            try {
+                authenticated = AuthHelper.authenticate(userLoginId, 
currentPassword, true);
+            } catch (AuthenticatorException e) {
+                // safe to ingore this; but we'll log it just in case
+                Debug.logWarning(e, e.getMessage(), module);
+            }
 
-        String currentPassword = (String) context.get("currentPassword");
-        String newPassword = (String) context.get("newPassword");
-        String newPasswordVerify = (String) context.get("newPasswordVerify");
-        String passwordHint = (String) context.get("passwordHint");
+            // call update password if auth passed
+            if (authenticated) {
+                try {
+                    AuthHelper.updatePassword(userLoginId, currentPassword, 
newPassword);
+                } catch (AuthenticatorException e) {
+                    Debug.logError(e, e.getMessage(), module);
+                    Map<String, String> messageMap = 
UtilMisc.toMap("userLoginId", userLoginId);
+                    errMsg = 
UtilProperties.getMessage(resource,"loginservices.could_not_change_password_userlogin_with_id_not_exist",
 messageMap, locale);
+                    return ServiceUtil.returnError(errMsg);
+                }
+                result.put(ModelService.RESPONSE_MESSAGE, 
ModelService.RESPOND_SUCCESS);
+                result.put("updatedUserLogin", userLoginToUpdate);
+                return result;
+            } else {
+                Map<String, String> messageMap = UtilMisc.toMap("userLoginId", 
userLoginId);
+                errMsg = 
UtilProperties.getMessage(resource,"loginservices.could_not_change_password_userlogin_with_id_not_exist",
 messageMap, locale);
+                return ServiceUtil.returnError(errMsg);
+            }
+        }
 
         if 
("true".equals(UtilProperties.getPropertyValue("security.properties", 
"password.lowercase"))) {
             currentPassword = currentPassword.toLowerCase();
@@ -575,17 +667,30 @@
             return ServiceUtil.returnError(errorMessageList);
         }
 
-        userLoginToUpdate.set("currentPassword", useEncryption ? 
HashCrypt.getDigestHash(newPassword, getHashType()) : newPassword, false);
-        userLoginToUpdate.set("passwordHint", passwordHint, false);
-        userLoginToUpdate.set("requirePasswordChange", "N");
+        String externalAuthId = userLoginToUpdate.getString("externalAuthId");
+        if (UtilValidate.isNotEmpty(externalAuthId)) {
+            // external auth is set; don't update the database record
+            try {
+                AuthHelper.updatePassword(externalAuthId, currentPassword, 
newPassword);
+            } catch (AuthenticatorException e) {
+                Debug.logError(e, e.getMessage(), module);
+                Map<String, String> messageMap = 
UtilMisc.toMap("errorMessage", e.getMessage());
+                errMsg = 
UtilProperties.getMessage(resource,"loginservices.could_not_change_password_write_failure",
 messageMap, locale);
+                return ServiceUtil.returnError(errMsg);
+            }
+        } else {
+            userLoginToUpdate.set("currentPassword", useEncryption ? 
HashCrypt.getDigestHash(newPassword, getHashType()) : newPassword, false);
+            userLoginToUpdate.set("passwordHint", passwordHint, false);
+            userLoginToUpdate.set("requirePasswordChange", "N");
 
-        try {
-            userLoginToUpdate.store();
-            createUserLoginPasswordHistory(delegator,userLoginId, newPassword);
-        } catch (GenericEntityException e) {
-            Map<String, String> messageMap = UtilMisc.toMap("errorMessage", 
e.getMessage());
-            errMsg = 
UtilProperties.getMessage(resource,"loginservices.could_not_change_password_write_failure",
 messageMap, locale);
-            return ServiceUtil.returnError(errMsg);
+            try {
+                userLoginToUpdate.store();
+                createUserLoginPasswordHistory(delegator,userLoginId, 
newPassword);
+            } catch (GenericEntityException e) {
+                Map<String, String> messageMap = 
UtilMisc.toMap("errorMessage", e.getMessage());
+                errMsg = 
UtilProperties.getMessage(resource,"loginservices.could_not_change_password_write_failure",
 messageMap, locale);
+                return ServiceUtil.returnError(errMsg);
+            }
         }
 
         result.put(ModelService.RESPONSE_MESSAGE, 
ModelService.RESPOND_SUCCESS);
@@ -758,6 +863,9 @@
         if (context.containsKey("successiveFailedLogins")) {
             userLoginToUpdate.set("successiveFailedLogins", 
context.get("successiveFailedLogins"), true);
         }
+        if (context.containsKey("externalAuthId")) {
+            userLoginToUpdate.set("externalAuthId", 
context.get("externalAuthId"), true);
+        }
         if (context.containsKey("userLdapDn")) {
             userLoginToUpdate.set("userLdapDn", context.get("userLdapDn"), 
true);
         }


Reply via email to