/*******************************************************************************
* 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;
}
}