/*
* This program 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; either version 2
* of the License, or (at your option) any later version.
*
* 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 General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package net.rrm.ehour.user.service;
import com.google.common.collect.Lists;
import net.rrm.ehour.data.DateRange;
import net.rrm.ehour.domain.ProjectAssignment;
import net.rrm.ehour.domain.User;
import net.rrm.ehour.domain.UserDepartment;
import net.rrm.ehour.domain.UserRole;
import net.rrm.ehour.exception.ObjectNotFoundException;
import net.rrm.ehour.exception.ObjectNotUniqueException;
import net.rrm.ehour.persistence.user.dao.UserDao;
import net.rrm.ehour.persistence.user.dao.UserDepartmentDao;
import net.rrm.ehour.persistence.user.dao.UserRoleDao;
import net.rrm.ehour.project.service.ProjectAssignmentManagementService;
import net.rrm.ehour.report.reports.element.AssignmentAggregateReportElement;
import net.rrm.ehour.report.reports.util.ReportUtil;
import net.rrm.ehour.report.service.AggregateReportService;
import net.rrm.ehour.timesheet.service.IDeleteTimesheetEntry;
import net.rrm.ehour.util.DateUtil;
import net.rrm.ehour.util.DomainUtil;
import org.apache.commons.lang.Validate;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.encoding.MessageDigestPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
/**
* @author Thies Edeling (thies@te-con.nl)
*/
@Service("userService")
public class UserServiceImpl implements UserService {
private static final Logger LOGGER = Logger.getLogger(UserServiceImpl.class);
@Autowired
private UserDao userDAO;
@Autowired
private UserDepartmentDao userDepartmentDAO;
@Autowired
private UserRoleDao userRoleDAO;
@Autowired
private ProjectAssignmentManagementService projectAssignmentManagementService;
@Autowired
private AggregateReportService aggregateReportService;
@Autowired
private IDeleteTimesheetEntry deleteTimesheetEntryService;
@Autowired
private MessageDigestPasswordEncoder passwordEncoder;
@Transactional(readOnly = true)
public User getUser(Integer userId) throws ObjectNotFoundException {
User user = userDAO.findById(userId);
Set<ProjectAssignment> inactiveAssignments = new HashSet<>();
if (user != null && user.getProjectAssignments() != null) {
splitActiveAndInactiveAssignments(user, inactiveAssignments);
} else {
throw new ObjectNotFoundException("User not found");
}
return user;
}
private void splitActiveAndInactiveAssignments(User user, Set<ProjectAssignment> inactiveAssignments) {
Date currentDate = new Date();
for (ProjectAssignment assignment : user.getProjectAssignments()) {
DateRange assignmentRange = new DateRange(assignment.getDateStart(), assignment.getDateEnd());
if ((!DateUtil.isDateWithinRange(currentDate, assignmentRange)) ||
(assignment.getProject() == null || !assignment.getProject().isActive())) {
inactiveAssignments.add(assignment);
}
}
user.getProjectAssignments().removeAll(inactiveAssignments);
user.setInactiveProjectAssignments(inactiveAssignments);
}
@Transactional(readOnly = true)
public User getUserAndCheckDeletability(Integer userId) throws ObjectNotFoundException {
User user = getUser(userId);
if ((!user.getProjectAssignments().isEmpty()) &&
(!user.getInactiveProjectAssignments().isEmpty())) {
user.setDeletable(true);
} else {
// bummer, we need to check if the user booked any hours on the assignments
List<Integer> assignmentIds = new ArrayList<>();
assignmentIds.addAll(DomainUtil.getIdsFromDomainObjects(user.getProjectAssignments()));
assignmentIds.addAll(DomainUtil.getIdsFromDomainObjects(user.getInactiveProjectAssignments()));
List<AssignmentAggregateReportElement> aggregates = aggregateReportService.getHoursPerAssignment(assignmentIds);
user.setDeletable(ReportUtil.isEmptyAggregateList(aggregates));
}
LOGGER.info("Retrieved user " + user.getUsername() + ", deletable: " + user.isDeletable());
return user;
}
@Transactional(readOnly = true)
public User getUser(String username) {
return userDAO.findByUsername(username);
}
@Transactional(readOnly = true)
public List<UserDepartment> getUserDepartments() {
return userDepartmentDAO.findAll();
}
@Transactional
public UserDepartment persistUserDepartment(UserDepartment department) throws ObjectNotUniqueException {
UserDepartment otherDept;
otherDept = userDepartmentDAO.findOnNameAndCode(department.getName(), department.getCode());
if (otherDept == null) {
userDepartmentDAO.persist(department);
} else if (otherDept.getDepartmentId().equals(department.getDepartmentId())) {
userDepartmentDAO.merge(department);
} else {
throw new ObjectNotUniqueException("name/code not unique");
}
return department;
}
@Transactional(readOnly = true)
public UserDepartment getUserDepartment(Integer departmentId) throws ObjectNotFoundException {
UserDepartment userDepartment = userDepartmentDAO.findById(departmentId);
if (userDepartment == null) {
throw new ObjectNotFoundException("Department not found");
}
userDepartment.setDeletable(!userDepartment.getUsers().isEmpty());
return userDepartment;
}
@Transactional(readOnly = true)
public List<User> getUsers() {
return userDAO.findUsers(false);
}
@Transactional(readOnly = true)
public List<User> getActiveUsers() {
return userDAO.findActiveUsers();
}
@Transactional(readOnly = true)
public List<UserRole> getUserRoles() {
return Lists.newArrayList(UserRole.ROLES.values());
}
@Transactional
public User persistEditedUser(User user) throws ObjectNotUniqueException {
// check username uniqueness
User dbUser = userDAO.findByUsername(user.getUsername());
if (dbUser != null && !dbUser.getUserId().equals(user.getUserId())) {
throw new ObjectNotUniqueException("Username already in use");
} else if (dbUser == null) {
dbUser = findUserOnId(user);
}
dbUser.setActive(user.isActive());
dbUser.setEmail(user.getEmail());
dbUser.setFirstName(user.getFirstName());
dbUser.setLastName(user.getLastName());
dbUser.getUserDepartments().clear();
dbUser.getUserDepartments().addAll(user.getUserDepartments());
dbUser.setUsername(user.getUsername());
boolean reAddPm = dbUser.getUserRoles().contains(UserRole.PROJECTMANAGER);
dbUser.setUserRoles(user.getUserRoles());
if (reAddPm && !user.getUserRoles().contains(UserRole.PROJECTMANAGER)) {
dbUser.addUserRole(UserRole.PROJECTMANAGER);
}
userDAO.persist(dbUser);
return dbUser;
}
private User findUserOnId(User user) {
User dbUser = userDAO.findById(user.getUserId());
if (dbUser == null) {
throw new IllegalArgumentException(String.format("%d user ID not found", user.getUserId()));
}
return dbUser;
}
@Override
@Transactional
public void persistNewUser(User user, String password) throws ObjectNotUniqueException {
// check username uniqueness
User dbUser = userDAO.findByUsername(user.getUsername());
if (dbUser != null && !dbUser.getUserId().equals(user.getUserId())) {
throw new ObjectNotUniqueException("Username already in use");
}
// encrypt password
user.setSalt((int) (Math.random() * 10000));
user.setPassword(encryptPassword(password, user.getSalt()));
userDAO.persist(user);
// assign new users to default projects
projectAssignmentManagementService.assignUserToDefaultProjects(user);
}
@Override
@Transactional
public void changePassword(String username, String currentPassword, String newUnencryptedPassword) throws BadCredentialsException {
User user = userDAO.findByUsername(username);
Validate.notNull(user, String.format("Can't find user with username %s", username));
String encryptedCurrentPassword = encryptPassword(currentPassword, user.getSalt());
if (!user.getPassword().equals(encryptedCurrentPassword)) {
throw new BadCredentialsException("Invalid current password");
}
changePassword(user, newUnencryptedPassword);
}
@Override
@Transactional
public void changePassword(String username, String newUnencryptedPassword) {
User user = userDAO.findByUsername(username);
Validate.notNull(user, String.format("Can't find user with username %s", username));
changePassword(user, newUnencryptedPassword);
}
private void changePassword(User user, String newUnencryptedPassword) {
int salt = (int) (Math.random() * 10000);
user.setSalt(salt);
user.setPassword(encryptPassword(newUnencryptedPassword, salt));
userDAO.persist(user);
}
private String encryptPassword(String plainPassword, Object salt) {
return passwordEncoder.encodePassword(plainPassword, salt);
}
public List<User> getUsersWithEmailSet() {
return userDAO.findAllActiveUsersWithEmailSet();
}
@Override
@Transactional
public User validateProjectManagementRoles(Integer userId) {
User user = null;
try {
if (userId != null) {
user = getAndAddPmRole(userId);
}
userDAO.deletePmWithoutProject();
} catch (ObjectNotUniqueException e) {
// won't happen
LOGGER.error("Account already exists", e);
}
return user;
}
/**
* Find user on id and add PM role
*/
private User getAndAddPmRole(Integer userId) throws ObjectNotUniqueException {
User user = userDAO.findById(userId);
UserRole userRole = userRoleDAO.findById(UserRole.ROLE_PROJECTMANAGER);
user.getUserRoles().add(userRole);
userDAO.persist(user);
return user;
}
@Override
@Transactional(readOnly = true)
public List<User> getUsers(UserRole userRole) {
return UserUtil.filterUserOnRole(userDAO.findActiveUsers(), userRole);
}
@Transactional
public void deleteUser(Integer userId) {
User user = userDAO.findById(userId);
deleteTimesheetEntryService.deleteAllTimesheetDataForUser(user);
userDAO.delete(user);
}
@Transactional
public void deleteDepartment(Integer departmentId) {
UserDepartment department = userDepartmentDAO.findById(departmentId);
LOGGER.info("Deleting department: " + department);
for (User user : department.getUsers()) {
LOGGER.info("Deleting user: " + user);
deleteUser(user.getUserId());
}
userDepartmentDAO.delete(department);
}
public void setPasswordEncoder(MessageDigestPasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
public void setProjectAssignmentManagementService(ProjectAssignmentManagementService projectAssignmentManagementService) {
this.projectAssignmentManagementService = projectAssignmentManagementService;
}
public void setUserDAO(UserDao dao) {
userDAO = dao;
}
public void setUserDepartmentDAO(UserDepartmentDao dao) {
userDepartmentDAO = dao;
}
public void setUserRoleDAO(UserRoleDao dao) {
userRoleDAO = dao;
}
}