/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
// www.projectforge.org
//
// Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation; version 3 of the License.
//
// This community edition 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 General Public License along
// with this program; if not, see http://www.gnu.org/licenses/.
//
/////////////////////////////////////////////////////////////////////////////
package org.projectforge.user;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.hibernate.LockMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.projectforge.access.AccessChecker;
import org.projectforge.access.AccessException;
import org.projectforge.access.AccessType;
import org.projectforge.access.OperationType;
import org.projectforge.common.Crypt;
import org.projectforge.common.NumberHelper;
import org.projectforge.core.BaseDao;
import org.projectforge.core.BaseSearchFilter;
import org.projectforge.core.ConfigXml;
import org.projectforge.core.DisplayHistoryEntry;
import org.projectforge.core.ModificationStatus;
import org.projectforge.core.QueryFilter;
import org.projectforge.core.SecurityConfig;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
*
* @author Kai Reinhard (k.reinhard@micromata.de)
*
*/
public class UserDao extends BaseDao<PFUserDO>
{
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(UserDao.class);
private static final String MESSAGE_KEY_OLD_PASSWORD_WRONG = "user.changePassword.error.oldPasswordWrong";
private static final short AUTHENTICATION_TOKEN_LENGTH = 20;
private static final short STAY_LOGGED_IN_KEY_LENGTH = 20;
public static final String MESSAGE_KEY_PASSWORD_QUALITY_CHECK = "user.changePassword.error.passwordQualityCheck";
public UserDao()
{
super(PFUserDO.class);
}
public UserGroupCache getUserGroupCache()
{
return userGroupCache;
}
public QueryFilter getDefaultFilter()
{
final QueryFilter queryFilter = new QueryFilter();
queryFilter.add(Restrictions.eq("deleted", false));
return queryFilter;
}
@Override
public List<PFUserDO> getList(final BaseSearchFilter filter)
{
final PFUserFilter myFilter;
if (filter instanceof PFUserFilter) {
myFilter = (PFUserFilter) filter;
} else {
myFilter = new PFUserFilter(filter);
}
final QueryFilter queryFilter = new QueryFilter(myFilter);
if (myFilter.getDeactivatedUser() != null) {
queryFilter.add(Restrictions.eq("deactivated", myFilter.getDeactivatedUser()));
}
if (Login.getInstance().hasExternalUsermanagementSystem() == true) {
// Check hasExternalUsermngmntSystem because otherwise the filter is may-be preset for an user and the user can't change the filter
// (because the fields aren't visible).
if (myFilter.getRestrictedUser() != null) {
queryFilter.add(Restrictions.eq("restrictedUser", myFilter.getRestrictedUser()));
}
if (myFilter.getLocalUser() != null) {
queryFilter.add(Restrictions.eq("localUser", myFilter.getLocalUser()));
}
}
if (myFilter.getHrPlanning() != null) {
queryFilter.add(Restrictions.eq("hrPlanning", myFilter.getHrPlanning()));
}
queryFilter.addOrder(Order.asc("username"));
List<PFUserDO> list = getList(queryFilter);
if (myFilter.getIsAdminUser() != null) {
final List<PFUserDO> origList = list;
list = new LinkedList<PFUserDO>();
for (final PFUserDO user : origList) {
if (myFilter.getIsAdminUser() == accessChecker.isUserMemberOfAdminGroup(user, false)) {
list.add(user);
}
}
}
return list;
}
public String getGroupnames(final PFUserDO user)
{
if (user == null) {
return "";
}
return userGroupCache.getGroupnames(user.getId());
}
public Collection<Integer> getAssignedGroups(final PFUserDO user)
{
return userGroupCache.getUserGroups(user);
}
public List<UserRightDO> getUserRights(final Integer userId)
{
return userGroupCache.getUserRights(userId);
}
public String[] getPersonalPhoneIdentifiers(final PFUserDO user)
{
final String[] tokens = StringUtils.split(user.getPersonalPhoneIdentifiers(), ", ;|");
if (tokens == null) {
return null;
}
int n = 0;
for (final String token : tokens) {
if (StringUtils.isNotBlank(token) == true) {
n++;
}
}
if (n == 0) {
return null;
}
final String[] result = new String[n];
n = 0;
for (final String token : tokens) {
if (StringUtils.isNotBlank(token) == true) {
result[n] = token.trim();
n++;
}
}
return result;
}
public String getNormalizedPersonalPhoneIdentifiers(final PFUserDO user)
{
if (StringUtils.isNotBlank(user.getPersonalPhoneIdentifiers()) == true) {
final String[] ids = getPersonalPhoneIdentifiers(user);
if (ids != null) {
final StringBuffer buf = new StringBuffer();
boolean first = true;
for (final String id : ids) {
if (first == true) {
first = false;
} else {
buf.append(",");
}
buf.append(id);
}
return buf.toString();
}
}
return null;
}
/**
* @see org.projectforge.core.BaseDao#afterSaveOrModify(org.projectforge.core.ExtendedBaseDO)
*/
@Override
protected void afterSaveOrModify(final PFUserDO obj)
{
if (obj.isMinorChange() == false) {
userGroupCache.setExpired();
}
}
/**
* @see org.projectforge.core.BaseDao#hasAccess(Object, OperationType)
*/
@Override
public boolean hasAccess(final PFUserDO user, final PFUserDO obj, final PFUserDO oldObj, final OperationType operationType,
final boolean throwException)
{
return accessChecker.isUserMemberOfAdminGroup(user, throwException);
}
/**
* @return false, if no admin user and the context user is not at minimum in one groups assigned to the given user or false. Also deleted
* and deactivated users are only visible for admin users.
* @see org.projectforge.core.BaseDao#hasSelectAccess(org.projectforge.core.BaseDO, boolean)
* @see AccessChecker#areUsersInSameGroup(PFUserDO, PFUserDO)
*/
@Override
public boolean hasSelectAccess(final PFUserDO user, final PFUserDO obj, final boolean throwException)
{
boolean result = accessChecker.isUserMemberOfAdminGroup(user)
|| accessChecker.isUserMemberOfGroup(user, ProjectForgeGroup.FINANCE_GROUP, ProjectForgeGroup.CONTROLLING_GROUP);
if (result == false && obj.hasSystemAccess() == true) {
result = accessChecker.areUsersInSameGroup(user, obj);
}
if (throwException == true && result == false) {
throw new AccessException(user, AccessType.GROUP, OperationType.SELECT);
}
return result;
}
@Override
public boolean hasSelectAccess(final PFUserDO user, final boolean throwException)
{
return true;
}
/**
* @see org.projectforge.core.BaseDao#hasInsertAccess(org.projectforge.user.PFUserDO)
*/
@Override
public boolean hasInsertAccess(final PFUserDO user)
{
return accessChecker.isUserMemberOfAdminGroup(user, false);
}
@Override
protected void onSaveOrModify(final PFUserDO obj)
{
obj.checkAndFixPassword();
}
/**
* Ohne Zugangsbegrenzung. Wird bei Anmeldung benötigt.
* @param username
* @param encryptedPassword
* @return
*/
@SuppressWarnings("unchecked")
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public PFUserDO authenticateUser(final String username, final String password)
{
Validate.notNull(username);
Validate.notNull(password);
PFUserDO user = getUser(username, password, true);
if (user != null) {
final int loginFailures = user.getLoginFailures();
final Timestamp lastLogin = user.getLastLogin();
user.setLastLogin(new Timestamp(new Date().getTime()));
user.setLoginFailures(0);
user.setMinorChange(true); // Avoid re-indexing of all dependent objects.
internalUpdate(user, false);
if (user.hasSystemAccess() == false) {
log.warn("Deleted/deactivated user tried to login: " + user);
return null;
}
final PFUserDO contextUser = new PFUserDO();
contextUser.copyValuesFrom(user);
contextUser.setLoginFailures(loginFailures); // Restore loginFailures for current user session.
contextUser.setLastLogin(lastLogin); // Restore lastLogin for current user session.
contextUser.setPassword(null);
return contextUser;
}
final List<PFUserDO> list = getHibernateTemplate().find("from PFUserDO u where u.username = ?", username);
if (list != null && list.isEmpty() == false && list.get(0) != null) {
user = list.get(0);
user.setLoginFailures(user.getLoginFailures() + 1);
internalUpdate(user);
}
return null;
}
@SuppressWarnings("unchecked")
private PFUserDO getUser(final String username, final String password, final boolean updateSaltAndPepperIfNeeded)
{
final List<PFUserDO> list = getHibernateTemplate().find("from PFUserDO u where u.username = ?", username);
if (list == null || list.isEmpty() == true || list.get(0) == null) {
return null;
}
final PFUserDO user = list.get(0);
final PasswordCheckResult passwordCheckResult = checkPassword(user, password);
if (passwordCheckResult.isOK() == false) {
return null;
}
if (updateSaltAndPepperIfNeeded == true && passwordCheckResult.isPasswordUpdateNeeded() == true) {
log.info("Giving salt and/or pepper to the password of the user " + user.getId() + ".");
createEncryptedPassword(user, password);
}
return user;
}
@SuppressWarnings("unchecked")
public PFUserDO getUserByStayLoggedInKey(final String username, final String stayLoggedInKey)
{
final List<PFUserDO> list = getHibernateTemplate().find("from PFUserDO u where u.username = ? and u.stayLoggedInKey = ?",
new Object[] { username, stayLoggedInKey});
PFUserDO user = null;
if (list != null && list.isEmpty() == false && list.get(0) != null) {
user = list.get(0);
}
if (user != null && user.hasSystemAccess() == false) {
log.warn("Deleted/deactivated user tried to login (via stay-logged-in): " + user);
return null;
}
return user;
}
/**
* Does an user with the given username already exists? Works also for existing users (if username was modified).
* @param user
* @return
*/
@SuppressWarnings("unchecked")
public boolean doesUsernameAlreadyExist(final PFUserDO user)
{
Validate.notNull(user);
List<PFUserDO> list = null;
if (user.getId() == null) {
// New user
list = getHibernateTemplate().find("from PFUserDO u where u.username = ?", user.getUsername());
} else {
// user already exists. Check maybe changed username:
list = getHibernateTemplate().find("from PFUserDO u where u.username = ? and pk <> ?",
new Object[] { user.getUsername(), user.getId()});
}
if (CollectionUtils.isNotEmpty(list) == true) {
return true;
}
return false;
}
/**
* Encrypts the password with a new generated salt string and the pepper string if configured any.
* @param user The user to user.
* @param password as clear text.
* @see Crypt#digest(String)
*/
public void createEncryptedPassword(final PFUserDO user, final String password)
{
final String saltString = createSaltString();
user.setPasswordSalt(saltString);
final String encryptedPassword = Crypt.digest(getPepperString() + saltString + password);
user.setPassword(encryptedPassword);
}
/**
* Checks the given password by comparing it with the stored user password. For backward compatibility the password is encrypted with and
* without pepper (if configured). The salt string of the given user is used.
* @param user
* @param password as clear text.
* @return true if the password matches the user's password.
*/
public PasswordCheckResult checkPassword(final PFUserDO user, final String password)
{
if (user == null) {
log.warn("User not given in checkPassword(PFUserDO, String) method.");
return PasswordCheckResult.FAILED;
}
final String userPassword = user.getPassword();
if (StringUtils.isBlank(userPassword) == true) {
log.warn("User's password is blank, can't checkPassword(PFUserDO, String) for user with id " + user.getId());
return PasswordCheckResult.FAILED;
}
String saltString = user.getPasswordSalt();
if (saltString == null) {
saltString = "";
}
final String pepperString = getPepperString();
String encryptedPassword = Crypt.digest(pepperString + saltString + password);
if (userPassword.equals(encryptedPassword) == true) {
// Passwords match!
if (StringUtils.isEmpty(saltString) == true) {
log.info("Password of user " + user.getId() + " with username '" + user.getUsername() + "' is not yet salted!");
return PasswordCheckResult.OK_WITHOUT_SALT;
}
return PasswordCheckResult.OK;
}
if (StringUtils.isNotBlank(pepperString) == true) {
// Check password without pepper:
encryptedPassword = Crypt.digest(saltString + password);
if (userPassword.equals(encryptedPassword) == true) {
// Passwords match!
if (StringUtils.isEmpty(saltString) == true) {
log.info("Password of user " + user.getId() + " with username '" + user.getUsername() + "' is not yet salted and has no pepper!");
return PasswordCheckResult.OK_WITHOUT_SALT_AND_PEPPER;
}
log.info("Password of user " + user.getId() + " with username '" + user.getUsername() + "' has no pepper!");
return PasswordCheckResult.OK_WITHOUT_PEPPER;
}
}
return PasswordCheckResult.FAILED;
}
private String createSaltString()
{
return NumberHelper.getSecureRandomBase64String(10);
}
private String getPepperString()
{
final SecurityConfig securityConfig = ConfigXml.getInstance().getSecurityConfig();
if (securityConfig != null) {
return securityConfig.getPasswordPepper();
}
return "";
}
/**
* Changes the user's password. Checks the password quality and the correct authentication for the old password before. Also the
* stay-logged-in-key will be renewed, so any existing stay-logged-in cookie will be invalid.
* @param user
* @param oldPassword
* @param newPassword
* @return Error message key if any check failed or null, if successfully changed.
*/
@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public String changePassword(PFUserDO user, final String oldPassword, final String newPassword)
{
Validate.notNull(user);
Validate.notNull(oldPassword);
Validate.notNull(newPassword);
final String errorMsgKey = checkPasswordQuality(newPassword);
if (errorMsgKey != null) {
return errorMsgKey;
}
accessChecker.checkRestrictedOrDemoUser();
user = getUser(user.getUsername(), oldPassword, false);
if (user == null) {
return MESSAGE_KEY_OLD_PASSWORD_WRONG;
}
createEncryptedPassword(user, newPassword);
onPasswordChange(user);
Login.getInstance().passwordChanged(user, newPassword);
log.info("Password changed and stay-logged-key renewed for user: " + user.getId() + " - " + user.getUsername());
return null;
}
public void onPasswordChange(final PFUserDO user)
{
user.checkAndFixPassword();
if (user.getPassword() != null) {
user.setStayLoggedInKey(createStayLoggedInKey());
user.setLastPasswordChange(new Date());
} else {
throw new IllegalArgumentException(
"Given password seems to be not encrypted! Aborting due to security reasons (for avoiding storage of clear password in the database).");
}
}
/**
* Returns the user's stay-logged-in key if exists (must be not blank with a size >= 10). If not, a new stay-logged-in key will be
* generated.
* @param userId
* @return
*/
@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public String getStayLoggedInKey(final Integer userId)
{
final PFUserDO user = internalGetById(userId);
if (StringUtils.isBlank(user.getStayLoggedInKey()) || user.getStayLoggedInKey().trim().length() < 10) {
user.setStayLoggedInKey(createStayLoggedInKey());
log.info("Stay-logged-key renewed for user: " + userId + " - " + user.getUsername());
}
return user.getStayLoggedInKey();
}
/**
* Renews the user's stay-logged-in key (random string sequence).
*/
@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void renewStayLoggedInKey(final Integer userId)
{
if (PFUserContext.getUserId().equals(userId) == false) {
// Only admin users are able to renew authentication token of other users:
accessChecker.checkIsLoggedInUserMemberOfAdminGroup();
}
accessChecker.checkRestrictedOrDemoUser(); // Demo users are also not allowed to do this.
final PFUserDO user = internalGetById(userId);
user.setStayLoggedInKey(createStayLoggedInKey());
log.info("Stay-logged-key renewed for user: " + userId + " - " + user.getUsername());
}
private String createStayLoggedInKey()
{
return NumberHelper.getSecureRandomUrlSaveString(STAY_LOGGED_IN_KEY_LENGTH);
}
/**
* Get authentication key by user. ; )
*
* @param userName
* @param authKey
* @return
*/
@SuppressWarnings("unchecked")
public PFUserDO getUserByAuthenticationToken(final Integer userId, final String authKey)
{
final List<PFUserDO> list = getHibernateTemplate().find("from PFUserDO u where u.id = ? and u.authenticationToken = ?",
new Object[] { userId, authKey});
PFUserDO user = null;
if (list != null && list.isEmpty() == false && list.get(0) != null) {
user = list.get(0);
}
if (user != null && user.hasSystemAccess() == false) {
log.warn("Deleted user tried to login (via authentication token): " + user);
return null;
}
return user;
}
/**
* Returns the user's authentication token if exists (must be not blank with a size >= 10). If not, a new token key will be generated.
* @param userId
* @return
*/
@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public String getAuthenticationToken(final Integer userId)
{
final PFUserDO user = internalGetById(userId);
if (StringUtils.isBlank(user.getAuthenticationToken()) || user.getAuthenticationToken().trim().length() < 10) {
user.setAuthenticationToken(createAuthenticationToken());
log.info("Authentication token renewed for user: " + userId + " - " + user.getUsername());
}
return user.getAuthenticationToken();
}
/**
* Renews the user's authentication token (random string sequence).
*/
@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void renewAuthenticationToken(final Integer userId)
{
if (PFUserContext.getUserId().equals(userId) == false) {
// Only admin users are able to renew authentication token of other users:
accessChecker.checkIsLoggedInUserMemberOfAdminGroup();
}
accessChecker.checkRestrictedOrDemoUser(); // Demo users are also not allowed to do this.
final PFUserDO user = internalGetById(userId);
user.setAuthenticationToken(createAuthenticationToken());
log.info("Authentication token renewed for user: " + userId + " - " + user.getUsername());
}
private String createAuthenticationToken()
{
return NumberHelper.getSecureRandomUrlSaveString(AUTHENTICATION_TOKEN_LENGTH);
}
/**
* Uses the context user.
* @param data
* @return
* @see #encrypt(Integer, String)
*/
public String encrypt(final String data)
{
return encrypt(PFUserContext.getUserId(), data);
}
/**
* Encrypts the given str with AES. The key is the current authenticationToken of the given user (by id) (first 16 bytes of it).
* @param userId
* @param data
* @return The base64 encoded result (url safe).
* @see Crypt#encrypt(String, String)
*/
public String encrypt(final Integer userId, final String data)
{
final String authenticationToken = StringUtils.rightPad(getCachedAuthenticationToken(userId), 32, "x");
return Crypt.encrypt(authenticationToken, data);
}
/**
* for faster access (due to permanent usage e. g. by subscription of calendars
* @param userId
* @return
*/
public String getCachedAuthenticationToken(final Integer userId)
{
final PFUserDO user = userGroupCache.getUser(userId);
final String authenticationToken = user.getAuthenticationToken();
if (StringUtils.isBlank(authenticationToken) == false && authenticationToken.trim().length() >= 10) {
return authenticationToken;
}
return getAuthenticationToken(userId);
}
/**
* @param userId
* @param encryptedString
* @return The decrypted string.
* @see Crypt#decrypt(String, String)
*/
public String decrypt(final Integer userId, final String encryptedString)
{
// final PFUserDO user = userGroupCache.getUser(userId); // for faster access (due to permanent usage e. g. by subscription of calendars
// (ics).
final String authenticationToken = StringUtils.rightPad(getCachedAuthenticationToken(userId), 32, "x");
return Crypt.decrypt(authenticationToken, encryptedString);
}
/**
* Checks the password quality of a new password. Password must have at least 6 characters and at minimum one letter and one non-letter
* character.
* @param newPassword
* @return null if password quality is OK, otherwise the i18n message key of the password check failure.
*/
public String checkPasswordQuality(final String newPassword)
{
boolean letter = false;
boolean nonLetter = false;
if (newPassword == null || newPassword.length() < 6) {
return MESSAGE_KEY_PASSWORD_QUALITY_CHECK;
}
for (int i = 0; i < newPassword.length(); i++) {
final char ch = newPassword.charAt(i);
if (letter == false && Character.isLetter(ch) == true) {
letter = true;
} else if (nonLetter == false && Character.isLetter(ch) == false) {
nonLetter = true;
}
}
if (letter == true && nonLetter == true) {
return null;
}
return MESSAGE_KEY_PASSWORD_QUALITY_CHECK;
}
@SuppressWarnings("unchecked")
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
public PFUserDO getInternalByName(final String username)
{
final List<PFUserDO> list = getHibernateTemplate().find("from PFUserDO u where u.username = ?", username);
if (list != null && list.size() > 0) {
return list.get(0);
}
return null;
}
/**
* User can modify own setting, this method ensures that only such properties will be updated, the user's are allowed to.
* @param user
*/
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW, isolation = Isolation.REPEATABLE_READ)
public void updateMyAccount(final PFUserDO user)
{
accessChecker.checkRestrictedOrDemoUser();
final PFUserDO contextUser = PFUserContext.getUser();
Validate.isTrue(user.getId().equals(contextUser.getId()) == true);
final PFUserDO dbUser = getHibernateTemplate().load(clazz, user.getId(), LockMode.PESSIMISTIC_WRITE);
if (copyValues(user, dbUser, "deleted", "password", "lastLogin", "loginFailures", "username", "stayLoggedInKey", "authenticationToken",
"rights") != ModificationStatus.NONE) {
dbUser.setLastUpdate();
log.info("Object updated: " + dbUser.toString());
copyValues(user, contextUser, "deleted", "password", "lastLogin", "loginFailures", "username", "stayLoggedInKey",
"authenticationToken", "rights");
} else {
log.info("No modifications detected (no update needed): " + dbUser.toString());
}
userGroupCache.updateUser(user);
}
/**
* Gets history entries of super and adds all history entries of the AuftragsPositionDO childs.
* @see org.projectforge.core.BaseDao#getDisplayHistoryEntries(org.projectforge.core.ExtendedBaseDO)
*/
@Override
public List<DisplayHistoryEntry> getDisplayHistoryEntries(final PFUserDO obj)
{
final List<DisplayHistoryEntry> list = super.getDisplayHistoryEntries(obj);
if (hasLoggedInUserHistoryAccess(obj, false) == false) {
return list;
}
if (CollectionUtils.isNotEmpty(obj.getRights()) == true) {
for (final UserRightDO right : obj.getRights()) {
final List<DisplayHistoryEntry> entries = internalGetDisplayHistoryEntries(right);
for (final DisplayHistoryEntry entry : entries) {
final String propertyName = entry.getPropertyName();
if (propertyName != null) {
entry.setPropertyName(right.getRightId() + ":" + entry.getPropertyName()); // Prepend number of positon.
} else {
entry.setPropertyName(String.valueOf(right.getRightId()));
}
}
list.addAll(entries);
}
}
Collections.sort(list, new Comparator<DisplayHistoryEntry>() {
public int compare(final DisplayHistoryEntry o1, final DisplayHistoryEntry o2)
{
return (o2.getTimestamp().compareTo(o1.getTimestamp()));
}
});
return list;
}
@Override
public boolean hasHistoryAccess(final PFUserDO user, final boolean throwException)
{
return accessChecker.isUserMemberOfAdminGroup(user, throwException);
}
/**
* Re-index all dependent objects only if the username, first or last name was changed.
* @see org.projectforge.core.BaseDao#wantsReindexAllDependentObjects(org.projectforge.core.ExtendedBaseDO,
* org.projectforge.core.ExtendedBaseDO)
*/
@Override
protected boolean wantsReindexAllDependentObjects(final PFUserDO obj, final PFUserDO dbObj)
{
if (super.wantsReindexAllDependentObjects(obj, dbObj) == false) {
return false;
}
return StringUtils.equals(obj.getUsername(), dbObj.getUsername()) == false
|| StringUtils.equals(obj.getFirstname(), dbObj.getFirstname()) == false
|| StringUtils.equals(obj.getLastname(), dbObj.getLastname()) == false;
}
@Override
public PFUserDO newInstance()
{
return new PFUserDO();
}
}