/******************************************************************************* * Author: Emerson faria Nobre - emerson-faria.nobre@serpro.gov.br - january/09 * Organization: SERPRO - Servico Federal de Processamento de Dados * Description: This source code is an extension of UserProvisioningOfficer.java * New capabilities implemented: * - Authenticate user in Ldap. * - Automatically Create/Update the MailServerAccount * (tables: fnbl_email_account, fnbl_email_enable_account, * fnbl_email_push_registry) * Changes: * Author/Date/Description: * Emerson Faria Nobre - june/2009 - Inserted parameters UserFieldName and * PwdFieldName because the LDAP Server of each Company that I * need to install Funambol use diferent names for this fields. * ******************************************************************************* * * * * Funambol is a mobile platform developed by Funambol, Inc. * Copyright (C) 2006 - 2007 Funambol, Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License version 3 as published by * the Free Software Foundation with the addition of the following permission * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License * along with this program; if not, see http://www.gnu.org/licenses or write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA. * * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License version 3. * * In accordance with Section 7(b) of the GNU Affero General Public License * version 3, these Appropriate Legal Notices must retain the display of the * "Powered by Funambol" logo. If the display of the logo is not reasonably * feasible for technical reasons, the Appropriate Legal Notices must display * the words "Powered by Funambol". */ package com.funambol.server.security; import com.funambol.framework.core.Authentication; import com.funambol.framework.core.Cred; import com.funambol.framework.security.Sync4jPrincipal; import com.funambol.framework.server.Sync4jUser; import com.funambol.framework.server.store.NotFoundException; import com.funambol.framework.server.store.PersistentStoreException; import com.funambol.framework.tools.Base64; import com.funambol.framework.tools.beans.LazyInitBean; import com.funambol.server.admin.AdminException; import com.funambol.server.admin.UserManager; import com.funambol.server.config.Configuration; // Imports for LDAP customization - SERPRO import java.util.Hashtable; import javax.naming.*; import javax.naming.directory.*; // Imports for Insert/Update MailServerAccount - SERPRO import com.funambol.email.model.*; import com.funambol.email.console.dao.ConsoleDAO; import com.funambol.email.exception.*; import com.funambol.email.util.Def; import com.funambol.pushlistener.service.registry.RegistryEntryStatus; import com.funambol.framework.filter.*; /** * This is an implementation of the Officier interface. It provides * the user provisioning so if an user is not in the database he will be added. * It requires basic authentication * * @version $Id: UserProvisioningOfficer.java,v 1.4 2008-06-24 12:50:06 piter_may Exp $ */ public class LdapUserProvisioningOfficer extends DBOfficer implements LazyInitBean { // New properties - SERPRO ldap objLdap; MailServerAccount msa; Boolean InsertMSA; String ldapIP; String ldapPort; // New propertie - SERPRO - 2009/06 String ldapStartSearchPath; boolean MsaEnablePush; boolean MsaEnablePolling; int MsaRefreshTime; int MsaMaxEmailNumber; int MsaMaxImapEmails; // ------------------------------------------------------------ Constructors public LdapUserProvisioningOfficer() { super(); // New instance - SERPRO msa = new MailServerAccount(); } // ---------------------------------------------------------- Public methods public void init() { super.init(); } /** * Authenticates a credential. * * @param credential the credential to be authenticated * * @return the Sync4jUser if the credential is autenticated, null otherwise */ public Sync4jUser authenticateUser(Cred credential) { Configuration config = Configuration.getConfiguration(); ps = config.getStore(); userManager = (UserManager) config.getUserManager(); String type = credential.getType(); if ((Cred.AUTH_TYPE_BASIC).equals(type)) { return authenticateBasicCredential(credential); } else if ((Cred.AUTH_TYPE_MD5).equals(type)) { return authenticateMD5Credential(credential); } return null; } /** * Gets the supported authentication type * * @return the basic authentication type */ public String getClientAuth() { return Cred.AUTH_TYPE_BASIC; } // ------------------------------------------------------- Protected Methods // Insert/Update MailServerAccount (MSA) - SERPRO protected void InsertUpdateMSA(String user, String pwd) { if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I will create cdao object"); } try { ConsoleDAO cdao = new ConsoleDAO(); // // Insert/Update MailServerAccount - SERPRO // if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I will verify if it is insert or update"); } msa = null; if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - before command cdao.getUser(user)"); } msa = cdao.getUser(user); if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - after command cdao.getUser(user)"); } if (msa == null) { msa = new MailServerAccount(); this.InsertMSA = true; } else { this.InsertMSA = false; } if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - after if (msa == null)"); } if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - msa.getUsername() = " + msa.getUsername()); } if ((log.isTraceEnabled()) && (this.InsertMSA == true)) { log.trace("LdapUserProvisioningOfficer - It is Insert - username = " + msa.getUsername()); } if ((log.isTraceEnabled()) && (this.InsertMSA == false)) { log.trace("LdapUserProvisioningOfficer - It is Update - username = " + msa.getUsername()); } if (!this.InsertMSA) { if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - The key Id = " + msa.getId()); log.trace("LdapUserProvisioningOfficer - The key UserName = " + msa.getUsername()); } } if (this.InsertMSA) { if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I will set setUserName" + user); } msa.setUsername(user); } if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I already set setMsLogin" + user); } msa.setMsLogin(user); if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I will set setMsPassword"); } msa.setMsPassword(pwd); if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I will set setMsAddress = " + objLdap.getmail()); } msa.setMsAddress(objLdap.getmail()); if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I will set setPush = " + this.getMsaEnablePush()); } msa.setPush(this.getMsaEnablePush()); if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I will set setMaxEmailNumber = " + this.getMsaMaxEmailNumber()); } msa.setMaxEmailNumber(this.getMsaMaxEmailNumber()); if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I will set setMaxImapEmail = " + this.getMsaMaxImapEmails()); } msa.setMaxImapEmail(this.getMsaMaxImapEmails()); if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I will set setPeriod = " + this.getMsaRefreshTime()); } msa.setPeriod(this.getMsaRefreshTime()); if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I will set setActive = " + this.getMsaEnablePolling()); } msa.setActive(this.getMsaEnablePolling()); // set the push listener framework properties if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I will set fnbl_email_push_registry"); } msa.setTaskBeanFile(Def.DEFAULT_INBOX_LISTENER_BEAN_FILE); if (this.InsertMSA) { msa.setStatus(RegistryEntryStatus.NEW); } else { msa.setStatus(RegistryEntryStatus.UPDATED); } msa.setLastUpdate(System.currentTimeMillis()); if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I already set fnbl_email_push_registry"); } /*if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I will set 100 to cdao.getPubMailServer"); } msa.setMailServer(cdao.getPubMailServer("100"));*/ String[] param = {"description"}; String[] value = {"expresso"}; String[] operator = {WhereClause.OPT_EQ}; MailServer[] ms = cdao.getPubMailServers(new WhereClause(param[0], new String[]{value[0]}, operator[0], false)); if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I found the MailServer expresso - ID = " + ms[0].getMailServerId()); } msa.setMailServer(cdao.getPubMailServer(ms[0].getMailServerId())); if (this.InsertMSA) { if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I will call insertUser(msa)"); } cdao.insertUser(msa); } else { if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - I will call updateUser(msa)"); } cdao.updateUser(msa); } } catch (InboxListenerConfigException e) { log.error("LdapUserProvisioningOfficer - Error creating DAO layer to Insert/Update Mail User: ", e); } catch (DBAccessException e) { log.error("LdapUserProvisioningOfficer - Error accessing Database to Insert/Update Mail User: ", e); } } /** * Checks the given credential. If the user or the principal isn't found, * they are created. * * @param credential the credential to check * * @return the Sync4jUser if the credential is autenticated, null otherwise */ protected Sync4jUser authenticateBasicCredential(Cred credential) { String username = null, password = null; Authentication auth = credential.getAuthentication(); String deviceId = auth.getDeviceId(); String userpwd = new String(Base64.decode(auth.getData())); int p = userpwd.indexOf(':'); if (p == -1) { username = userpwd; password = ""; } else { username = (p > 0) ? userpwd.substring(0, p) : ""; password = (p == (userpwd.length() - 1)) ? "" : userpwd.substring(p + 1); } if (log.isTraceEnabled()) { log.trace("User to check: " + username); } // // Gets the user without checking the password // Sync4jUser user = getUser(username, null); if (user == null) { if (log.isTraceEnabled()) { log.trace("User '" + username + "' not found. A new user will be created"); } // // LDAP Checkpoint - SERPRO // if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - CheckPoint LDAP - getLdapIP: " + this.getLdapIP() + " getLdapPort: " + this.getLdapPort()); } // objLdap = new ldap(this.getLdapIP(), this.getLdapPort(), username, password); objLdap = new ldap(this.getLdapIP(), this.getLdapPort(), username, password, this.getLdapStartSearchPath()); // Try to althenticate in LDAP - SERPRO if (objLdap.getreturnStatus() == false) { if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - LDAP Authentication Failure: " + objLdap.geterrorMsg() + " - " + objLdap.geterrorStatus()); } return null; } try { user = insertUser(username, password); if (log.isTraceEnabled()) { log.trace("User '" + username + "' created"); } } catch (Exception e) { log.error("Error inserting a new user", e); return null; } // // Insert/Update MailServerAccount - SERPRO // this.InsertUpdateMSA(username, password); } else { if (log.isTraceEnabled()) { log.trace("User '" + username + "' found"); } // // Check the password // //String storedPassword = user.getPassword(); //if (!password.equals(storedPassword)) { // // The user isn't authenticated // //if (log.isTraceEnabled()) { // log.trace("The sent password is different from the stored " + "one. User not authenticated"); //} //return null; //} else { // // Check the roles // boolean isASyncUser = isASyncUser(user); if (isASyncUser) { // // User authenticated // if (log.isTraceEnabled()) { log.trace("User is a SyncUser"); } // // LDAP Checkpoint - SERPRO // if (log.isTraceEnabled()) { log.trace("LdapUserProvisioningOfficer - CheckPoint LDAP - getLdapIP: " + this.getLdapIP() + " getLdapPort: " + this.getLdapPort()); } objLdap = new ldap(this.getLdapIP(), this.getLdapPort(), username, password, this.getLdapStartSearchPath()); // Try to althenticate in LDAP - SERPRO if (objLdap.getreturnStatus() == false) { if (log.isTraceEnabled()) { log.trace("LDAP Authentication Failure: " + objLdap.geterrorMsg() + " - " + objLdap.geterrorStatus()); } return null; } // // Insert/Update MailServerAccount - SERPRO // this.InsertUpdateMSA(username, password); } else { // // User not authenticated // if (log.isTraceEnabled()) { log.trace("The user is not a '" + ROLE_USER + "'"); } return null; } //} } // // Verify that the principal for the specify deviceId and username exists // Otherwise a new principal will be created // try { handlePrincipal(user.getUsername(), deviceId); } catch (PersistentStoreException e) { log.error("Error handling the principal", e); return null; } return user; } /** * Insert a new user with the given username and password * * @param userName the username * @param password the password * * @return the new user * * @throws AdminException in case of admin errors * @throws PersistentStoreException if an error occurs */ protected Sync4jUser insertUser( String userName, String password) throws AdminException, PersistentStoreException { Sync4jUser user = new Sync4jUser(); user.setUsername(userName); user.setPassword(password); user.setFirstname(objLdap.getcn()); user.setRoles(new String[]{ROLE_USER}); // Update e-mail field of user table - Emerson Faria Nobre - SERPRO user.setEmail(objLdap.getmail()); userManager.insertUser(user); return user; } /** * Returns the principal with the given username and deviceId. * null if not found * @param userName the username * @param deviceId the device id * @return the principal found or null. * @throws PersistentStoreException if an error occurs */ protected Sync4jPrincipal getPrincipal(String userName, String deviceId) throws PersistentStoreException { Sync4jPrincipal principal = null; // // Verify that exist the principal for the specify deviceId and username // principal = Sync4jPrincipal.createPrincipal(userName, deviceId); try { ps.read(principal); } catch (NotFoundException ex) { return null; } return principal; } /** * Inserts a new principal with the given userName and deviceId * @param userName the username * @param deviceId the device id * @return the principal created * @throws PersistentStoreException if an error occurs creating the principal */ protected Sync4jPrincipal insertPrincipal(String userName, String deviceId) throws PersistentStoreException { // // We must create a new principal // Sync4jPrincipal principal = Sync4jPrincipal.createPrincipal(userName, deviceId); ps.store(principal); return principal; } /** * Searchs if there is a principal with the given username and device id. * if no principal is found, a new one is created. * @param userName the user name * @param deviceId the device id * @return the found principal or the new one */ protected Sync4jPrincipal handlePrincipal(String username, String deviceId) throws PersistentStoreException { Sync4jPrincipal principal = null; // // Verify if the principal for the specify deviceId and username exists // principal = getPrincipal(username, deviceId); if (log.isTraceEnabled()) { log.trace("Principal '" + username + "/" + deviceId + "' " + ((principal != null) ? "found" : "not found. A new principal will be created")); } if (principal == null) { principal = insertPrincipal(username, deviceId); if (log.isTraceEnabled()) { log.trace("Principal '" + username + "/" + deviceId + "' created"); } } return principal; } // Getters and Setters - SERPRO public void setLdapIP(String pldapIP) { this.ldapIP = pldapIP; } public String getLdapIP() { return this.ldapIP; } public void setLdapPort(String pLdapPort) { this.ldapPort = pLdapPort; } public String getLdapPort() { return this.ldapPort; } public void setLdapStartSearchPath(String pLdapStartSearchPath) { this.ldapStartSearchPath = pLdapStartSearchPath; } public String getLdapStartSearchPath() { return this.ldapStartSearchPath; } public void setMsaEnablePush(boolean pMsaEnablePush) { this.MsaEnablePush = pMsaEnablePush; } public boolean getMsaEnablePush() { return this.MsaEnablePush; } public void setMsaEnablePolling(boolean pMsaEnablePolling) { this.MsaEnablePolling = pMsaEnablePolling; } public boolean getMsaEnablePolling() { return this.MsaEnablePolling; } public void setMsaRefreshTime(int pMsaRefreshTime) { this.MsaRefreshTime = pMsaRefreshTime; } public int getMsaRefreshTime() { return this.MsaRefreshTime; } public void setMsaMaxEmailNumber(int pMsaMaxEmailNumber) { this.MsaMaxEmailNumber = pMsaMaxEmailNumber; } public int getMsaMaxEmailNumber() { return this.MsaMaxEmailNumber; } public void setMsaMaxImapEmails(int pMsaMaxImapEmails) { this.MsaMaxImapEmails = pMsaMaxImapEmails; } public int getMsaMaxImapEmails() { return this.MsaMaxImapEmails; } } // Class to manage LDAP - SERPRO class ldap { private String ldapServer; private String ldapPort; private String ldapSearchPath; private String UserID; private String userDN; private String pwd; private String cn; private String mail; private String errorMsg; private String errorStatus; private boolean returnStatus; public ldap(String ldapServer, String ldapPort, String UserID, String pwd, String ldapSearchPath) { this.ldapServer = ldapServer; this.ldapPort = ldapPort; this.UserID = UserID; this.pwd = pwd; this.ldapSearchPath = ldapSearchPath; this.returnStatus = processLDAP(); } private boolean processLDAP() { // Password cannot be null if ((pwd.trim().length()) == 0) { //TODO Create exception to null pwd this.errorMsg = "Password Cannot be null"; this.errorStatus = "nullPwd"; return false; } // Connecting as anonymous to get information about the user Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, "ldap://" + this.ldapServer + ":" + this.ldapPort); try { // Connecting DirContext ctx = new InitialDirContext(env); // Searching User SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); ctls.setCountLimit(1); ctls.setTimeLimit(10000); // Espera no max 10 segundos String filter = "(uid=" + this.UserID + ")"; // NamingEnumeration answer = ctx.search("dc=serpro,dc=gov,dc=br", filter, ctls); NamingEnumeration answer = ctx.search(this.getLdapSearchPath(), filter, ctls); if (answer.hasMore()) { SearchResult sr = (SearchResult) answer.next(); // this.userDN = sr.getName() + ",dc=serpro,dc=gov,dc=br"; this.userDN = sr.getName() + "," + this.getLdapSearchPath(); try { // Getting User attributes this.cn = sr.getAttributes().get("cn").get(0).toString(); this.mail = sr.getAttributes().get("mail").get(0).toString(); } catch (Exception e) { e.printStackTrace(); this.errorMsg = e.toString(); this.errorStatus = "notGetAttributes"; return false; } } else { //TODO Create exception this.errorMsg = "User/Password not Found in LDAP"; this.errorStatus = "notFound"; return false; } /*// This lines was used to DEBUG answer.close(); try { answer = ctx.search("dc=serpro,dc=gov,dc=br", filter, ctls); // formatResults(answer); } catch (Exception e) { e.printStackTrace(); return false; } // END - This lines was used to DEBUG*/ } catch (NamingException e) { e.printStackTrace(); this.errorMsg = e.toString(); this.errorStatus = "notConnect"; return false; } // Binding (Verifing Credentials) Hashtable env2 = new Hashtable(); env2.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env2.put(Context.PROVIDER_URL, "ldap://" + this.ldapServer + ":" + this.ldapPort); env2.put(Context.SECURITY_AUTHENTICATION, "simple"); env2.put(Context.SECURITY_PRINCIPAL, this.userDN); // specify the username env2.put(Context.SECURITY_CREDENTIALS, this.pwd); // specify the password try { DirContext ctx = new InitialDirContext(env2); ctx.close(); } catch (NamingException e) { e.printStackTrace(); this.errorMsg = e.toString(); this.errorStatus = "notBind"; return false; } return true; } // Getters methods public String getldapServer() { return this.ldapServer; } public String getldapPort() { return this.ldapPort; } public String getUserID() { return this.UserID; } public String getuserDN() { return this.userDN; } public String getcn() { return this.cn; } public String getmail() { return this.mail; } public String geterrorMsg() { return this.errorMsg; } public String geterrorStatus() { return this.errorStatus; } public boolean getreturnStatus() { return this.returnStatus; } public String getLdapSearchPath() { return this.ldapSearchPath; } }