/////////////////////////////////////////////////////////////////////////////
//
// 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.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.log4j.Logger;
import org.projectforge.common.AbstractCache;
import org.projectforge.common.StringHelper;
import org.projectforge.fibu.EmployeeDO;
import org.projectforge.fibu.ProjektDO;
import org.projectforge.web.UserFilter;
import org.springframework.orm.hibernate3.HibernateTemplate;
/**
* The group user relations will be cached with this class.
* @author Kai Reinhard (k.reinhard@micromata.de)
*/
public class UserGroupCache extends AbstractCache
{
private static Logger log = Logger.getLogger(UserGroupCache.class);
/** The key is the user id and the value is a list of assigned groups. */
private Map<Integer, Set<Integer>> userGroupIdMap;
private Map<Integer, GroupDO> groupMap;
/**
* List of all rights (value) defined for the user ids (key).
*/
private Map<Integer, List<UserRightDO>> rightMap;
private Map<Integer, PFUserDO> userMap;
private Map<Integer, EmployeeDO> employeeMap;
private Set<Integer> adminUsers;
private Set<Integer> financeUsers;
private Set<Integer> controllingUsers;
private Set<Integer> projectManagers;
private Set<Integer> projectAssistants;
private Set<Integer> marketingUsers;
private Set<Integer> orgaUsers;
private HibernateTemplate hibernateTemplate;
public void setHibernateTemplate(final HibernateTemplate hibernateTemplate)
{
this.hibernateTemplate = hibernateTemplate;
}
public GroupDO getGroup(final Integer groupId)
{
checkRefresh();
return getGroupMap().get(groupId);
}
public GroupDO getGroup(final ProjectForgeGroup group)
{
checkRefresh();
for (final GroupDO g : groupMap.values()) {
if (group.equals(g.getName()) == true) {
return g;
}
}
return null;
}
public String getGroupname(final Integer groupId)
{
checkRefresh();
final GroupDO group = getGroup(groupId);
return group == null ? null : group.getName();
}
public PFUserDO getUser(final Integer userId)
{
if (userId == null) {
return null;
}
// checkRefresh(); Done by getUserMap().
return getUserMap() != null ? userMap.get(userId) : null; // Only null in maintenance mode (if t_user isn't readable).
}
public PFUserDO getUser(final String username)
{
if (StringUtils.isEmpty(username) == true) {
return null;
}
for (final PFUserDO user : getUserMap().values()) {
if (username.equals(user.getUsername()) == true) {
return user;
}
}
return null;
}
public PFUserDO getUserByFullname(final String fullname)
{
if (StringUtils.isEmpty(fullname) == true) {
return null;
}
for (final PFUserDO user : getUserMap().values()) {
if (fullname.equals(user.getFullname()) == true) {
return user;
}
}
return null;
}
/**
* @return all users (also deleted users).
*/
public Collection<PFUserDO> getAllUsers()
{
// checkRefresh(); Done by getUserMap().
return getUserMap().values();
}
/**
* @return all groups (also deleted groups).
*/
public Collection<GroupDO> getAllGroups()
{
// checkRefresh(); Done by getGMap().
return getGroupMap().values();
}
/**
* Only for internal use.
*/
public int internalGetNumberOfUsers()
{
if (userMap == null) {
return 0;
} else {
// checkRefresh(); Done by getUserMap().
return getUserMap().size();
}
}
public String getUsername(final Integer userId)
{
// checkRefresh(); Done by getUserMap().
final PFUserDO user = getUserMap().get(userId);
if (user == null) {
return String.valueOf(userId);
}
return user.getUsername();
}
/**
* Check for current logged in user.
* @param groupId
* @return
*/
public boolean isLoggedInUserMemberOfGroup(final Integer groupId)
{
return isUserMemberOfGroup(PFUserContext.getUserId(), groupId);
}
/**
* @param groupId
* @return
*/
public boolean isUserMemberOfGroup(final PFUserDO user, final Integer groupId)
{
if (user == null) {
return false;
}
return isUserMemberOfGroup(user.getId(), groupId);
}
public boolean isUserMemberOfGroup(final Integer userId, final Integer groupId)
{
if (groupId == null) {
return false;
}
checkRefresh();
final Set<Integer> groupSet = getUserGroupIdMap().get(userId);
return (groupSet != null) ? groupSet.contains(groupId) : false;
}
public boolean isUserMemberOfAtLeastOneGroup(final Integer userId, final Integer... groupIds)
{
if (groupIds == null) {
return false;
}
checkRefresh();
final Set<Integer> groupSet = getUserGroupIdMap().get(userId);
if (groupSet == null) {
return false;
}
for (final Integer groupId : groupIds) {
if (groupId == null) {
continue;
}
if (groupSet.contains(groupId) == true) {
return true;
}
}
return false;
}
public boolean isUserMemberOfAdminGroup()
{
return isUserMemberOfAdminGroup(PFUserContext.getUserId());
}
public boolean isUserMemberOfAdminGroup(final Integer userId)
{
checkRefresh();
// adminUsers should only be null in maintenance mode (e. g. if user table isn't readable).
return adminUsers != null ? adminUsers.contains(userId) : false;
}
public boolean isUserMemberOfFinanceGroup()
{
return isUserMemberOfFinanceGroup(PFUserContext.getUserId());
}
public boolean isUserMemberOfFinanceGroup(final Integer userId)
{
checkRefresh();
// financeUsers should only be null in maintenance mode (e. g. if user table isn't readable).
return financeUsers != null ? financeUsers.contains(userId) : false;
}
public boolean isUserMemberOfProjectManagers()
{
return isUserMemberOfProjectManagers(PFUserContext.getUserId());
}
public boolean isUserMemberOfProjectManagers(final Integer userId)
{
checkRefresh();
// projectManagers should only be null in maintenance mode (e. g. if user table isn't readable).
return projectManagers != null ? projectManagers.contains(userId) : false;
}
public boolean isUserMemberOfProjectAssistant()
{
return isUserMemberOfProjectAssistant(PFUserContext.getUserId());
}
public boolean isUserMemberOfProjectAssistant(final Integer userId)
{
checkRefresh();
// projectAssistants should only be null in maintenance mode (e. g. if user table isn't readable).
return projectAssistants != null ? projectAssistants.contains(userId) : false;
}
public boolean isUserProjectManagerOrAssistantForProject(final ProjektDO projekt)
{
if (projekt == null || projekt.getProjektManagerGroupId() == null) {
return false;
}
final Integer userId = PFUserContext.getUserId();
if (isUserMemberOfProjectAssistant(userId) == false && isUserMemberOfProjectManagers(userId) == false) {
return false;
}
return isUserMemberOfGroup(userId, projekt.getProjektManagerGroupId());
}
public boolean isUserMemberOfControllingGroup()
{
return isUserMemberOfControllingGroup(PFUserContext.getUserId());
}
public boolean isUserMemberOfControllingGroup(final Integer userId)
{
checkRefresh();
// controllingUsers should only be null in maintenance mode (e. g. if user table isn't readable).
return controllingUsers != null ? controllingUsers.contains(userId) : false;
}
public boolean isUserMemberOfMarketingGroup()
{
return isUserMemberOfMarketingGroup(PFUserContext.getUserId());
}
public boolean isUserMemberOfMarketingGroup(final Integer userId)
{
checkRefresh();
return marketingUsers.contains(userId);
}
public boolean isUserMemberOfOrgaGroup()
{
return isUserMemberOfOrgaGroup(PFUserContext.getUserId());
}
public boolean isUserMemberOfOrgaGroup(final Integer userId)
{
checkRefresh();
// orgaUsers should only be null in maintenance mode (e. g. if user table isn't readable).
return orgaUsers != null ? orgaUsers.contains(userId) : false;
}
/**
* Checks if the given user is at least member of one of the given groups.
* @param user
* @param groups
*/
public boolean isUserMemberOfGroup(final PFUserDO user, final ProjectForgeGroup... groups)
{
if (user == null) {
return false;
}
Validate.notNull(groups);
for (final ProjectForgeGroup group : groups) {
boolean result = false;
if (group == ProjectForgeGroup.ADMIN_GROUP) {
result = isUserMemberOfAdminGroup(user.getId());
} else if (group == ProjectForgeGroup.FINANCE_GROUP) {
result = isUserMemberOfFinanceGroup(user.getId());
} else if (group == ProjectForgeGroup.PROJECT_MANAGER) {
result = isUserMemberOfProjectManagers(user.getId());
} else if (group == ProjectForgeGroup.PROJECT_ASSISTANT) {
result = isUserMemberOfProjectAssistant(user.getId());
} else if (group == ProjectForgeGroup.CONTROLLING_GROUP) {
result = isUserMemberOfControllingGroup(user.getId());
} else if (group == ProjectForgeGroup.MARKETING_GROUP) {
result = isUserMemberOfMarketingGroup(user.getId());
} else if (group == ProjectForgeGroup.ORGA_TEAM) {
result = isUserMemberOfOrgaGroup(user.getId());
} else {
throw new UnsupportedOperationException("Group not yet supported: " + group);
}
if (result == true) {
return true;
}
}
return false;
}
public String getGroupnames(final Integer userId)
{
checkRefresh();
final Set<Integer> groupSet = getUserGroupIdMap().get(userId);
if (groupSet == null) {
return "";
}
final List<String> list = new ArrayList<String>();
for (final Integer groupId : groupSet) {
final GroupDO group = getGroup(groupId);
if (group != null) {
list.add(group.getName());
} else {
log.error("Group with id " + groupId + " not found.");
}
}
return StringHelper.listToString(list, "; ", true);
}
public List<UserRightDO> getUserRights(final Integer userId)
{
return getUserRightMap().get(userId);
}
private Map<Integer, List<UserRightDO>> getUserRightMap()
{
checkRefresh();
return rightMap;
}
/**
* Returns a collection of group id's to which the user is assigned to.
* @param user
* @return collection if found, otherwise null.
*/
public Collection<Integer> getUserGroups(final PFUserDO user)
{
checkRefresh();
return getUserGroupIdMap().get(user.getId());
}
public EmployeeDO getEmployee(final Integer userId)
{
checkRefresh();
EmployeeDO employee = this.employeeMap.get(userId);
if (employee == null) {
@SuppressWarnings("unchecked")
final List<EmployeeDO> list = this.hibernateTemplate.find("from EmployeeDO e where e.user.id = ?", userId);
if (list != null && list.size() > 0) {
employee = list.get(0);
this.employeeMap.put(userId, employee);
}
}
return employee;
}
/**
* Removes given employee from map, so refresh for next access is forced.
* @param userId
*/
public void refreshEmployee(final Integer userId)
{
if (this.employeeMap != null) {
this.employeeMap.remove(userId);
}
}
private Map<Integer, GroupDO> getGroupMap()
{
checkRefresh();
return groupMap;
}
private Map<Integer, Set<Integer>> getUserGroupIdMap()
{
checkRefresh();
return userGroupIdMap;
}
/**
* Should be called after user modifications.
* @param user
*/
void updateUser(final PFUserDO user)
{
getUserMap().put(user.getId(), user);
}
private Map<Integer, PFUserDO> getUserMap()
{
checkRefresh();
return userMap;
}
/**
* This method will be called by CacheHelper and is synchronized.
*/
@SuppressWarnings("unchecked")
@Override
protected void refresh()
{
log.info("Initializing UserGroupCache ...");
// This method must not be synchronized because it works with a new copy of maps.
final Map<Integer, PFUserDO> uMap = new HashMap<Integer, PFUserDO>();
// Could not autowire UserDao because of cyclic reference with AccessChecker.
final List<PFUserDO> users = Login.getInstance().getAllUsers();
for (final PFUserDO user : users) {
uMap.put(user.getId(), user);
}
final List<GroupDO> groups = Login.getInstance().getAllGroups();
final Map<Integer, GroupDO> gMap = new HashMap<Integer, GroupDO>();
final Map<Integer, Set<Integer>> ugIdMap = new HashMap<Integer, Set<Integer>>();
final Set<Integer> nAdminUsers = new HashSet<Integer>();
final Set<Integer> nFinanceUser = new HashSet<Integer>();
final Set<Integer> nControllingUsers = new HashSet<Integer>();
final Set<Integer> nProjectManagers = new HashSet<Integer>();
final Set<Integer> nProjectAssistants = new HashSet<Integer>();
final Set<Integer> nMarketingUsers = new HashSet<Integer>();
final Set<Integer> nOrgaUsers = new HashSet<Integer>();
for (final GroupDO group : groups) {
gMap.put(group.getId(), group);
if (group.getAssignedUsers() != null) {
for (final PFUserDO user : group.getAssignedUsers()) {
if (user != null) {
final Set<Integer> groupIdSet = ensureAndGetUserGroupIdMap(ugIdMap, user.getId());
groupIdSet.add(group.getId());
if (ProjectForgeGroup.ADMIN_GROUP.equals(group.getName()) == true) {
log.debug("Adding user '" + user.getUsername() + "' as administrator.");
nAdminUsers.add(user.getId());
} else if (ProjectForgeGroup.FINANCE_GROUP.equals(group.getName()) == true) {
log.debug("Adding user '" + user.getUsername() + "' for finance.");
nFinanceUser.add(user.getId());
} else if (ProjectForgeGroup.CONTROLLING_GROUP.equals(group.getName()) == true) {
log.debug("Adding user '" + user.getUsername() + "' for controlling.");
nControllingUsers.add(user.getId());
} else if (ProjectForgeGroup.PROJECT_MANAGER.equals(group.getName()) == true) {
log.debug("Adding user '" + user.getUsername() + "' as project manager.");
nProjectManagers.add(user.getId());
} else if (ProjectForgeGroup.PROJECT_ASSISTANT.equals(group.getName()) == true) {
log.debug("Adding user '" + user.getUsername() + "' as project assistant.");
nProjectAssistants.add(user.getId());
} else if (ProjectForgeGroup.MARKETING_GROUP.equals(group.getName()) == true) {
log.debug("Adding user '" + user.getUsername() + "' as marketing user.");
nMarketingUsers.add(user.getId());
} else if (ProjectForgeGroup.ORGA_TEAM.equals(group.getName()) == true) {
log.debug("Adding user '" + user.getUsername() + "' as orga user.");
nOrgaUsers.add(user.getId());
}
}
}
}
}
this.userMap = uMap;
this.groupMap = gMap;
this.adminUsers = nAdminUsers;
this.financeUsers = nFinanceUser;
this.controllingUsers = nControllingUsers;
this.projectManagers = nProjectManagers;
this.projectAssistants = nProjectAssistants;
this.marketingUsers = nMarketingUsers;
this.orgaUsers = nOrgaUsers;
this.userGroupIdMap = ugIdMap;
this.employeeMap = new HashMap<Integer, EmployeeDO>();
final Map<Integer, List<UserRightDO>> rMap = new HashMap<Integer, List<UserRightDO>>();
List<UserRightDO> rights;
try {
rights = hibernateTemplate.find("from UserRightDO t order by user.id, right_id");
} catch (final Exception ex) {
log.fatal("******* Exception while getting user rights from data-base (only OK for migration from older versions): "
+ ex.getMessage());
rights = new ArrayList<UserRightDO>();
}
List<UserRightDO> list = null;
Integer userId = null;
for (final UserRightDO right : rights) {
if (right.getUserId() == null) {
log.warn("Oups, userId = null: " + right);
continue;
}
if (right.getUserId().equals(userId) == false) {
list = new ArrayList<UserRightDO>();
userId = right.getUserId();
if (userId != null) {
rMap.put(userId, list);
}
}
if (UserRights.instance().getRight(right.getRightId()).isAvailable(this, right.getUser()) == true) {
list.add(right);
}
}
this.rightMap = rMap;
log.info("Initializing of UserGroupCache done.");
Login.getInstance().afterUserGroupCacheRefresh(users, groups);
}
private static Set<Integer> ensureAndGetUserGroupIdMap(final Map<Integer, Set<Integer>> ugIdMap, final Integer userId)
{
Set<Integer> set = ugIdMap.get(userId);
if (set == null) {
set = new HashSet<Integer>();
ugIdMap.put(userId, set);
}
return set;
}
public synchronized void internalSetAdminUser(final PFUserDO adminUser)
{
if (UserFilter.isUpdateRequiredFirst() == false) {
throw new IllegalStateException(
"Can't set admin user internally! This method is only available if system is under maintenance (update required first is true)!");
}
checkRefresh();
this.adminUsers.add(adminUser.getId());
}
}