/////////////////////////////////////////////////////////////////////////////
//
// 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.access;
import java.util.Collection;
import org.apache.commons.lang.Validate;
import org.projectforge.common.StringHelper;
import org.projectforge.task.TaskNode;
import org.projectforge.task.TaskTree;
import org.projectforge.user.PFUserContext;
import org.projectforge.user.PFUserDO;
import org.projectforge.user.ProjectForgeGroup;
import org.projectforge.user.UserGroupCache;
import org.projectforge.user.UserRight;
import org.projectforge.user.UserRightAccessCheck;
import org.projectforge.user.UserRightDO;
import org.projectforge.user.UserRightId;
import org.projectforge.user.UserRightValue;
import org.projectforge.user.UserRights;
/**
* This class contains some helper methods for evaluation of user and group access'.
* @author Kai Reinhard (k.reinhard@micromata.de)
*/
public class AccessChecker
{
public static final String I18N_KEY_VIOLATION_USER_NOT_MEMBER_OF = "access.violation.userNotMemberOf";
private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(AccessChecker.class);
private TaskTree taskTree;
private UserGroupCache userGroupCache;
private final UserRights userRights = UserRights.initialize(this);
/**
* Tests for every group the user is assigned to, if the given permission is given.
* @return true, if the user owns the required permission, otherwise false.
*/
public boolean hasLoggedInUserPermission(final Integer taskId, final AccessType accessType, final OperationType operationType,
final boolean throwException)
{
return hasPermission(PFUserContext.getUser(), taskId, accessType, operationType, throwException);
}
/**
* Tests for every group the user is assigned to, if the given permission is given.
* @return true, if the user owns the required permission, otherwise false.
*/
public boolean hasPermission(final PFUserDO user, final Integer taskId, final AccessType accessType, final OperationType operationType,
final boolean throwException)
{
Validate.notNull(user);
if (userGroupCache.isUserMemberOfAdminGroup(user.getId()) == true) {
// A user group "Admin" has always access.
return true;
}
final TaskNode node = taskTree.getTaskNodeById(taskId);
if (node == null) {
log.error("Task with " + taskId + " not found.");
if (throwException == true) {
throw new AccessException(taskId, accessType, operationType);
}
return false;
}
final Collection<Integer> groupIds = userGroupCache.getUserGroups(user);
if (groupIds == null) {
// No groups are assigned to this user.
if (throwException == true) {
throw new AccessException(taskId, accessType, operationType);
}
return false;
}
for (final Integer groupId : groupIds) {
if (node.hasPermission(groupId, accessType, operationType) == true) {
return true;
}
}
if (throwException == true) {
throw new AccessException(taskId, accessType, operationType);
}
return false;
}
public void setTaskTree(final TaskTree taskTree)
{
this.taskTree = taskTree;
}
public TaskTree getTaskTree()
{
return taskTree;
}
public void setUserGroupCache(final UserGroupCache userGroupCache)
{
this.userGroupCache = userGroupCache;
}
public UserGroupCache getUserGroupCache()
{
return userGroupCache;
}
/**
* Checks if the user is an admin user (member of admin group). If not, an AccessException will be thrown.
*/
public void checkIsLoggedInUserMemberOfAdminGroup()
{
checkIsLoggedInUserMemberOfGroup(ProjectForgeGroup.ADMIN_GROUP);
}
/**
* @return
* @see org.projectforge.user.UserGroupCache#isUserMemberOfAdminGroup(java.lang.Integer)
*/
public boolean isLoggedInUserMemberOfAdminGroup()
{
return isUserMemberOfAdminGroup(PFUserContext.getUser());
}
/**
* @param user
* @return
*/
public boolean isUserMemberOfAdminGroup(final PFUserDO user)
{
return isUserMemberOfGroup(user, ProjectForgeGroup.ADMIN_GROUP);
}
/**
* @param throwException
* @return
*/
public boolean isLoggedInUserMemberOfAdminGroup(final boolean throwException)
{
return isLoggedInUserMemberOfGroup(throwException, ProjectForgeGroup.ADMIN_GROUP);
}
/**
* @param user
* @param throwException
* @return
* @see org.projectforge.user.UserGroupCache#isUserMemberOfAdminGroup(java.lang.Integer)
*/
public boolean isUserMemberOfAdminGroup(final PFUserDO user, final boolean throwException)
{
return isUserMemberOfGroup(user, throwException, ProjectForgeGroup.ADMIN_GROUP);
}
/**
* Checks if the user is in one of the given groups. If not, an AccessException will be thrown.
*/
public void checkIsLoggedInUserMemberOfGroup(final ProjectForgeGroup... groups)
{
checkIsUserMemberOfGroup(PFUserContext.getUser(), groups);
}
/**
* Checks if the user is in one of the given groups. If not, an AccessException will be thrown.
* @see #isUserMemberOfGroup(PFUserDO, ProjectForgeGroup...)
*/
public void checkIsUserMemberOfGroup(final PFUserDO user, final ProjectForgeGroup... groups)
{
if (isUserMemberOfGroup(user, groups) == false) {
throw getLoggedInUserNotMemberOfException(groups);
}
}
/**
* Checks if the user of the PFUserContext (logged in user) is member at least of one of the given groups.
*
* @param groups
*/
public boolean isLoggedInUserMemberOfGroup(final ProjectForgeGroup... groups)
{
return isLoggedInUserMemberOfGroup(false, groups);
}
/**
* Checks if the user of the PFUserContext (logged in user) is member at least of one of the given groups.
*
* @param throwException default false.
* @param groups
* @see #isUserMemberOfGroup(PFUserDO, ProjectForgeGroup...)
*/
public boolean isLoggedInUserMemberOfGroup(final boolean throwException, final ProjectForgeGroup... groups)
{
return isUserMemberOfGroup(PFUserContext.getUser(), throwException, groups);
}
/**
* Checks if the user of the PFUserContext (logged in user) is member at least of one of the given groups.
*
* @param throwException default false.
* @param groups
* @see #isUserMemberOfGroup(PFUserDO, ProjectForgeGroup...)
*/
public boolean isUserMemberOfGroup(final PFUserDO user, final boolean throwException, final ProjectForgeGroup... groups)
{
Validate.notNull(groups);
if (user == null) {
// Before user is logged in.
if (throwException == true) {
throw getLoggedInUserNotMemberOfException(groups);
}
return false;
}
if (throwException == false) {
return isUserMemberOfGroup(user, groups);
} else if (isUserMemberOfGroup(user, groups) == true) {
return true;
} else {
throw getLoggedInUserNotMemberOfException(groups);
}
}
private AccessException getLoggedInUserNotMemberOfException(final ProjectForgeGroup... groups)
{
final StringBuffer buf = new StringBuffer();
for (int i = 0; i < groups.length; i++) {
if (i > 0) {
buf.append(", ");
}
buf.append(groups[i].toString());
}
final String str = buf.toString();
log.error(I18N_KEY_VIOLATION_USER_NOT_MEMBER_OF + ": " + str);
return new AccessException(I18N_KEY_VIOLATION_USER_NOT_MEMBER_OF, str);
}
/**
* 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)
{
return userGroupCache.isUserMemberOfGroup(user, groups);
}
/**
* Compares the two given users on equality. The pk's will be compared. If one or more user's or pk's are null, false will be returned.
* @param u1
* @param u2
* @return true, if both user pk's are not null and equal.
*/
public boolean userEquals(final PFUserDO u1, final PFUserDO u2)
{
if (u1 == null || u2 == null || u1.getId() == null) {
return false;
}
return u1.getId().equals(u2.getId());
}
/**
* Gets the user from the PFUserContext and compares the both user.
* @param user
* @return
* @see AccessChecker#userEquals(PFUserDO, PFUserDO)
*/
public boolean userEqualsToContextUser(final PFUserDO user)
{
return userEquals(PFUserContext.getUser(), user);
}
/**
* Is the current context user in at minimum one group of the groups assigned to the given user?
* @param user
* @return
*/
public boolean isLoggedInUserInSameGroup(final PFUserDO user)
{
return areUsersInSameGroup(PFUserContext.getUser(), user);
}
/**
* Is the current context user in at minimum one group of the groups assigned to the given user?
* @param user2
* @return
*/
public boolean areUsersInSameGroup(final PFUserDO user1, final PFUserDO user2)
{
final Collection<Integer> userGroups = userGroupCache.getUserGroups(user2);
if (userGroups == null) {
// No groups found.
return false;
}
final Collection<Integer> currentUserGroups = userGroupCache.getUserGroups(user1);
if (currentUserGroups == null) {
// User has now associated groups.
return false;
}
for (final Integer id : currentUserGroups) {
if (userGroups.contains(id) == true) {
return true;
}
}
return false;
}
/**
* @param user Check the access for the given user instead of the logged-in user.
* @param rightId
* @param obj
* @param oldObj
* @param operationType
* @param throwException
*/
@SuppressWarnings({ "unchecked", "rawtypes"})
public boolean hasAccess(final PFUserDO user, final UserRightId rightId, final Object obj, final Object oldObj,
final OperationType operationType, final boolean throwException)
{
final UserRight right = userRights.getRight(rightId);
Validate.notNull(right);
boolean result;
if (right instanceof UserRightAccessCheck< ? >) {
Validate.notNull(user);
switch (operationType) {
case SELECT:
if (obj != null) {
result = ((UserRightAccessCheck) right).hasSelectAccess(user, obj);
} else {
result = ((UserRightAccessCheck) right).hasSelectAccess(user);
}
break;
case INSERT:
if (obj != null) {
result = ((UserRightAccessCheck) right).hasInsertAccess(user, obj);
} else {
result = ((UserRightAccessCheck) right).hasInsertAccess(user);
}
break;
case UPDATE:
result = ((UserRightAccessCheck) right).hasUpdateAccess(user, obj, oldObj);
break;
case DELETE:
result = ((UserRightAccessCheck) right).hasDeleteAccess(user, obj);
break;
default:
throw new UnsupportedOperationException("Oups, value not supported for OperationType: " + operationType);
}
if (result == false && throwException == true) {
throw new AccessException(user, "access.exception.userHasNotRight", rightId, operationType);
}
return result;
}
if (operationType == OperationType.SELECT) {
return hasRight(user, rightId, throwException, UserRightValue.READONLY, UserRightValue.READWRITE);
} else {
return hasRight(user, rightId, throwException, UserRightValue.READWRITE);
}
}
/**
* Use context user (logged-in user).
* @param rightId
* @param obj
* @param oldObj
* @param operationType
* @param throwException
* @see #hasAccess(PFUserDO, UserRightId, Object, Object, OperationType, boolean)
*/
public boolean hasLoggedInUserAccess(final UserRightId rightId, final Object obj, final Object oldObj, final OperationType operationType,
final boolean throwException)
{
return hasAccess(PFUserContext.getUser(), rightId, obj, oldObj, operationType, throwException);
}
/**
* Use context user (logged-in user).
* @param rightId
* @param throwException
* @see #hasSelectAccess(PFUserDO, UserRightId, boolean)
*/
public boolean hasLoggedInUserSelectAccess(final UserRightId rightId, final boolean throwException)
{
return hasSelectAccess(PFUserContext.getUser(), rightId, throwException);
}
/**
* Calls {@link #hasAccess(PFUserDO, UserRightId, Object, Object, OperationType, boolean)} with {@link OperationType#SELECT} and both Objects as
* null.
* @param user Check the access for the given user instead of the logged-in user.
* @param rightId
* @param throwException
* @see #hasAccess(PFUserDO, UserRightId, Object, Object, OperationType, boolean)
*/
public boolean hasSelectAccess(final PFUserDO user, final UserRightId rightId, final boolean throwException)
{
return hasAccess(user, rightId, null, null, OperationType.SELECT, throwException);
}
/**
* Calls {@link #hasLoggedInUserAccess(UserRightId, Object, Object, OperationType, boolean)} with {@link OperationType#SELECT}.
* @param rightId
* @param obj
* @param throwException
* @see #hasLoggedInUserAccess(UserRightId, Object, Object, OperationType, boolean)
*/
public boolean hasLoggedInUserSelectAccess(final UserRightId rightId, final Object obj, final boolean throwException)
{
return hasLoggedInUserAccess(rightId, obj, null, OperationType.SELECT, throwException);
}
/**
* Calls {@link #hasLoggedInUserAccess(UserRightId, Object, Object, OperationType, boolean)} with {@link OperationType#INSERT}.
* @param rightId
* @param obj
* @param throwException
* @see #hasLoggedInUserAccess(UserRightId, Object, Object, OperationType, boolean)
*/
public boolean hasLoggedInUserInsertAccess(final UserRightId rightId, final Object obj, final boolean throwException)
{
return hasLoggedInUserAccess(rightId, obj, null, OperationType.INSERT, throwException);
}
/**
* Calls {@link #hasLoggedInUserAccess(UserRightId, Object, Object, OperationType, boolean)} with {@link OperationType#INSERT}.
* @param rightId
* @param throwException
* @see #hasLoggedInUserAccess(UserRightId, Object, Object, OperationType, boolean)
*/
public boolean hasLoggedInUserInsertAccess(final UserRightId rightId, final boolean throwException)
{
return hasLoggedInUserAccess(rightId, null, null, OperationType.INSERT, throwException);
}
/**
* Calls {@link #hasAccess(PFUserDO, UserRightId, Object, Object, OperationType, boolean)} with {@link OperationType#INSERT}.
* @param user
* @param rightId
* @param throwException
* @see #hasAccess(PFUserDO, UserRightId, Object, Object, OperationType, boolean)
*/
public boolean hasInsertAccess(final PFUserDO user, final UserRightId rightId, final boolean throwException)
{
return hasAccess(user, rightId, null, null, OperationType.INSERT, throwException);
}
/**
* Calls {@link #hasLoggedInUserAccess(UserRightId, Object, Object, OperationType, boolean)} with {@link OperationType#UPDATE}.
* @param rightId
* @param obj
* @param oldObj
* @param throwException
* @see #hasLoggedInUserAccess(UserRightId, Object, Object, OperationType, boolean)
*/
public boolean hasLoggedInUserUpdateAccess(final UserRightId rightId, final Object obj, final Object oldObj, final boolean throwException)
{
return hasLoggedInUserAccess(rightId, obj, oldObj, OperationType.UPDATE, throwException);
}
/**
* Calls {@link #hasLoggedInUserAccess(UserRightId, Object, Object, OperationType, boolean)} with {@link OperationType#DELETE}.
* @param rightId
* @param obj
* @param oldObj
* @param throwException
* @see #hasLoggedInUserAccess(UserRightId, Object, Object, OperationType, boolean)
*/
public boolean hasLoggedInUserDeleteAccess(final UserRightId rightId, final Object oldObj, final Object obj, final boolean throwException)
{
return hasLoggedInUserAccess(rightId, obj, oldObj, OperationType.DELETE, throwException);
}
/**
* Throws now exception if the right check fails.
* @see #hasRight(PFUserDO, UserRightId, boolean, UserRightValue...)
*/
public boolean hasLoggedInUserRight(final UserRightId rightId, final UserRightValue... values)
{
return hasRight(PFUserContext.getUser(), rightId, false, values);
}
/**
* Throws now exception if the right check fails.
* @see #hasRight(PFUserDO, UserRightId, boolean, UserRightValue...)
*/
public boolean hasRight(final PFUserDO user, final UserRightId rightId, final UserRightValue... values)
{
return hasRight(user, rightId, false, values);
}
/**
* Checks the availability and the demanded value of the right for the context user. The right will be checked itself on required
* constraints, e. g. if assigned groups required.
* @param rightId
* @param values At least one of the values should match.
* @param throwException
*/
public boolean hasLoggedInUserRight(final UserRightId rightId, final boolean throwException, final UserRightValue... values)
{
return hasRight(PFUserContext.getUser(), rightId, throwException, values);
}
/**
* Checks the availability and the demanded value of the right for the context user. The right will be checked itself on required
* constraints, e. g. if assigned groups required.
* @param user Check the access for the given user instead of the logged-in user.
* @param rightId
* @param values At least one of the values should match.
* @param throwException
* @return
*/
public boolean hasRight(final PFUserDO user, final UserRightId rightId, final boolean throwException, final UserRightValue... values)
{
Validate.notNull(user);
Validate.notNull(values);
final UserRightDO rightDO = user.getRight(rightId);
final UserRight right = userRights.getRight(rightId);
for (final UserRightValue value : values) {
if ((rightDO == null || rightDO.getValue() == null) && right.matches(userGroupCache, user, value) == true) {
return true;
}
if (rightDO != null && rightDO.getValue() == value) {
if (right != null && right.isAvailable(userGroupCache, user, value) == true) {
return true;
}
}
}
if (throwException == true) {
throw new AccessException("access.exception.userHasNotRight", rightId, StringHelper.listToString(", ", (Object[]) values));
}
return false;
}
/**
* @param rightId
* @param throwException
* @see #hasReadAccess(PFUserDO, UserRightId, boolean)
*/
public boolean hasLoggedInUserReadAccess(final UserRightId rightId, final boolean throwException)
{
return hasReadAccess(PFUserContext.getUser(), rightId, throwException);
}
/**
* @param rightId
* @param throwException
* @see #hasRight(PFUserDO, UserRightId, boolean, UserRightValue...)
*/
public boolean hasReadAccess(final PFUserDO user, final UserRightId rightId, final boolean throwException)
{
return hasRight(user, rightId, throwException, UserRightValue.READONLY, UserRightValue.READWRITE);
}
/**
* Calls {@link #hasLoggedInUserReadAccess(UserRightId, boolean)} with throwException = true.
* @param rightId
* @see #hasLoggedInUserReadAccess(UserRightId, boolean)
*/
public boolean checkLoggedInUserReadAccess(final UserRightId rightId)
{
return hasLoggedInUserReadAccess(rightId, true);
}
/**
* @param rightId
* @param throwException
* @see #hasLoggedInUserRight(UserRightId, boolean, UserRightValue...)
*/
public boolean hasLoggedInUserWriteAccess(final UserRightId rightId, final boolean throwException)
{
return hasLoggedInUserRight(rightId, throwException, UserRightValue.READWRITE);
}
/**
* Calls {@link #hasLoggedInUserWriteAccess(UserRightId, boolean)} with throwException = true.
* @param rightId
* @see #hasLoggedInUserWriteAccess(UserRightId, boolean)
*/
public boolean checkLoggedInUserWriteAccess(final UserRightId rightId)
{
return hasLoggedInUserWriteAccess(rightId, true);
}
public boolean hasLoggedInUserHistoryAccess(final UserRightId rightId, final Object obj, final boolean throwException)
{
return hasHistoryAccess(PFUserContext.getUser(), rightId, obj, throwException);
}
@SuppressWarnings({ "unchecked", "rawtypes"})
public boolean hasHistoryAccess(final PFUserDO user, final UserRightId rightId, final Object obj, final boolean throwException)
{
final UserRight right = userRights.getRight(rightId);
Validate.notNull(right);
if (right instanceof UserRightAccessCheck< ? >) {
Validate.notNull(user);
if (((UserRightAccessCheck) right).hasHistoryAccess(user, obj) == true) {
return true;
} else if (throwException == true) {
throw new AccessException("access.exception.userHasNotRight", rightId, "history");
} else {
return false;
}
} else {
return hasRight(user, rightId, throwException, UserRightValue.READONLY, UserRightValue.READWRITE);
}
}
/**
* Calls {@link #hasLoggedInUserRight(UserRightId, boolean, UserRightValue...)} with throwException = true.
* @param rightId
* @param values
* @see #hasLoggedInUserRight(UserRightId, boolean, UserRightValue...)
*/
public boolean checkLoggedInUserRight(final UserRightId rightId, final UserRightValue... values)
{
return hasLoggedInUserRight(rightId, true, values);
}
/**
* Calls {@link #hasRight(PFUserDO, UserRightId, boolean, UserRightValue...)} with throwException = true.
* @param user
* @param rightId
* @param values
* @see #hasRight(PFUserDO, UserRightId, boolean, UserRightValue...)
*/
public boolean checkUserRight(final PFUserDO user, final UserRightId rightId, final UserRightValue... values)
{
return hasRight(user, rightId, true, values);
}
/**
* Gets the UserRight and calls {@link UserRight#isAvailable(UserGroupCache, PFUserDO)}.
* @param rightId
* @return
*/
public boolean isAvailable(final UserRightId rightId)
{
return isAvailable(PFUserContext.getUser(), rightId);
}
/**
* Gets the UserRight and calls {@link UserRight#isAvailable(UserGroupCache, PFUserDO)}.
* @param rightId
* @return
*/
public boolean isAvailable(final PFUserDO user, final UserRightId rightId)
{
final UserRight right = userRights.getRight(rightId);
return right != null && right.isAvailable(userGroupCache, user) == true;
}
public boolean isDemoUser()
{
final PFUserDO user = PFUserContext.getUser();
if (user == null) {
return false;
}
return isDemoUser(user);
}
public boolean isDemoUser(final Integer userId)
{
final PFUserDO user = userGroupCache.getUser(userId);
return isDemoUser(user);
}
public boolean isDemoUser(final PFUserDO user)
{
if (user == null) {
return false;
}
if ("demo".equals(user.getUsername()) == false) {
return false;
}
return true;
}
public boolean isRestrictedUser()
{
final PFUserDO user = PFUserContext.getUser();
if (user == null) {
return true;
}
return isRestrictedUser(user);
}
public boolean isRestrictedUser(final Integer userId)
{
final PFUserDO user = userGroupCache.getUser(userId);
return isDemoUser(user);
}
public boolean isRestrictedUser(final PFUserDO user)
{
if (user == null) {
return false;
}
return user.isRestrictedUser();
}
/**
* Throws an exception if the current logged-in user is a demo user.
*/
public void checkRestrictedUser()
{
if (isRestrictedUser() == true) {
throw new AccessException("access.exception.demoUserHasNoAccess");
}
}
public boolean isRestrictedOrDemoUser()
{
final PFUserDO user = PFUserContext.getUser();
if (user == null) {
return false;
}
return isRestrictedOrDemoUser(user);
}
public boolean isRestrictedOrDemoUser(final Integer userId)
{
final PFUserDO user = userGroupCache.getUser(userId);
return isRestrictedOrDemoUser(user);
}
public boolean isRestrictedOrDemoUser(final PFUserDO user)
{
if (user == null) {
return false;
}
return isRestrictedUser(user) || isDemoUser(user);
}
/**
* Throws an exception if the current logged-in user is a demo user.
*/
public void checkRestrictedOrDemoUser()
{
if (isDemoUser() == true) {
throw new AccessException("access.exception.demoUserHasNoAccess");
}
if (isRestrictedUser() == true) {
throw new AccessException("access.exception.demoUserHasNoAccess");
}
}
/**
* @return true if logged-in-user is member of {@link ProjectForgeGroup#FINANCE_GROUP}, {@link ProjectForgeGroup#CONTROLLING_GROUP} or
* {@link ProjectForgeGroup#PROJECT_MANAGER}. Returns also true if user is member of {@link ProjectForgeGroup#ORGA_TEAM} and has
* the
*/
public boolean hasLoggedInUserAccessToTimesheetsOfOtherUsers()
{
final PFUserDO loggedInUser = PFUserContext.getUser();
Validate.notNull(loggedInUser);
if (isUserMemberOfGroup(loggedInUser, ProjectForgeGroup.FINANCE_GROUP, ProjectForgeGroup.CONTROLLING_GROUP,
ProjectForgeGroup.PROJECT_MANAGER) == true) {
return true;
}
if (isUserMemberOfGroup(loggedInUser, ProjectForgeGroup.ORGA_TEAM) == true
&& hasRight(loggedInUser, UserRightId.PM_HR_PLANNING, UserRightValue.READONLY, UserRightValue.READWRITE)) {
return true;
}
return false;
}
}