////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2009-2013 Denim Group, Ltd.
//
// The contents of this file are subject to the Mozilla Public License
// Version 2.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is ThreadFix.
//
// The Initial Developer of the Original Code is Denim Group, Ltd.
// Portions created by Denim Group, Ltd. are Copyright (C)
// Denim Group, Ltd. All Rights Reserved.
//
// Contributor(s): Denim Group, Ltd.
//
////////////////////////////////////////////////////////////////////////
package com.denimgroup.threadfix.service;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.denimgroup.threadfix.data.dao.AccessControlMapDao;
import com.denimgroup.threadfix.data.dao.RoleDao;
import com.denimgroup.threadfix.data.dao.UserDao;
import com.denimgroup.threadfix.data.entities.AccessControlApplicationMap;
import com.denimgroup.threadfix.data.entities.AccessControlTeamMap;
import com.denimgroup.threadfix.data.entities.Permission;
import com.denimgroup.threadfix.data.entities.Role;
import com.denimgroup.threadfix.data.entities.User;
@Service
public class UserServiceImpl implements UserService {
protected final SanitizedLogger log = new SanitizedLogger(UserService.class);
private UserDao userDao = null;
private RoleDao roleDao = null;
private AccessControlMapDao accessControlMapDao = null;
private ThreadFixPasswordEncoder encoder = new ThreadFixPasswordEncoder();
@Autowired
public UserServiceImpl(AccessControlMapDao accessControlMapDao,
UserDao userDao, RoleDao roleDao) {
this.userDao = userDao;
this.roleDao = roleDao;
this.accessControlMapDao = accessControlMapDao;
}
/**
* Transactional(readOnly = false) here means that false will be put in to
* the LDAP user field and update correctly.
*/
@Override
@Transactional(readOnly = false)
public List<User> loadAllUsers() {
return userDao.retrieveAllActive();
}
@Override
@Transactional(readOnly = true)
public User loadUser(int userId) {
return userDao.retrieveById(userId);
}
@Override
@Transactional(readOnly = true)
public User loadUser(String name) {
User user = userDao.retrieveByName(name);
if (user != null && user.getIsLdapUser()) {
return null;
} else {
return user;
}
}
@Override
@Transactional(readOnly = false)
public void storeUser(User user) {
if ((user.getUnencryptedPassword() != null) && (user.getUnencryptedPassword().length() > 0)) {
encryptPassword(user);
}
userDao.saveOrUpdate(user);
}
@Override
@Transactional(readOnly = false)
public void delete(User user) {
if (user != null) {
user.setName(user.getName() + new Date().toString());
if (user.getName().length() > User.NAME_LENGTH) {
user.setName(user.getName().substring(0, User.NAME_LENGTH - 1));
}
user.setActive(false);
userDao.saveOrUpdate(user);
}
}
@Override
@Transactional(readOnly = false)
public void createUser(User user) {
encryptPassword(user);
userDao.saveOrUpdate(user);
}
private void encryptPassword(User user) {
try {
user.setSalt(encoder.generateSalt());
user.setPassword(encoder.generatePasswordHash(user.getUnencryptedPassword(),
user.getSalt()));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
@Override
@Transactional(readOnly = true)
public boolean isCorrectPassword(User user, String password) {
if (user.getPassword() != null && user.getSalt() != null
&& password != null) {
try {
String encodedPassword = encoder.generatePasswordHash(password, user.getSalt());
return encodedPassword != null && encodedPassword.equals(user.getPassword());
} catch (NoSuchAlgorithmException e) {
// This should never happen but let's log it
log.warn("Failed to encrypt a password - something is broken.", e);
}
}
return false;
}
@Override
@Transactional(readOnly = true)
public Set<Permission> getGlobalPermissions(Integer userId) {
Set<Permission> returnList = new HashSet<Permission>();
// for now
User user = loadUser(userId);
if (user != null && user.getHasGlobalGroupAccess() && user.getGlobalRole() != null) {
returnList.addAll(user.getGlobalRole().getPermissions());
}
return returnList;
}
@Override
@Transactional(readOnly = true)
public boolean canDelete(User user) {
boolean canDelete = true;
Set<Permission> permissions = getGlobalPermissions(user.getId());
if (canDelete && permissions.contains(Permission.CAN_MANAGE_USERS) &&
!userDao.canRemovePermissionFromUser(user.getId(), "canManageUsers")) {
canDelete = false;
}
if (canDelete && permissions.contains(Permission.CAN_MANAGE_ROLES) &&
!userDao.canRemovePermissionFromUser(user.getId(), "canManageRoles")) {
canDelete = false;
}
return canDelete;
}
@Override
@Transactional(readOnly = true)
public boolean canSetRoles(int userId, List<Integer> objectIds) {
boolean canSetRoles = true;
Set<Permission> oldPermissions = getGlobalPermissions(userId);
Set<Permission> newPermissions = new HashSet<Permission>();
if (objectIds != null) {
for (Integer integer : objectIds) {
Role role = roleDao.retrieveById(integer);
if (role != null) {
newPermissions.addAll(role.getPermissions());
}
}
}
if (canSetRoles && oldPermissions.contains(Permission.CAN_MANAGE_USERS) &&
!newPermissions.contains(Permission.CAN_MANAGE_USERS) &&
!userDao.canRemovePermissionFromUser(userId, "canManageUsers")) {
canSetRoles = false;
}
if (canSetRoles && oldPermissions.contains(Permission.CAN_MANAGE_ROLES) &&
!newPermissions.contains(Permission.CAN_MANAGE_ROLES) &&
!userDao.canRemovePermissionFromUser(userId, "canManageRoles")) {
canSetRoles = false;
}
return canSetRoles;
}
@Override
@Transactional(readOnly = true)
public Map<Integer, Set<Permission>> getApplicationPermissions(
Integer userId) {
Map<Integer, Set<Permission>> applicationPermissions = new HashMap<Integer,Set<Permission>>();
List<AccessControlTeamMap> maps = accessControlMapDao.retrieveAllMapsForUser(userId);
if (maps != null) {
for (AccessControlTeamMap teamMap : maps) {
if (teamMap != null && teamMap.getAccessControlApplicationMaps() != null) {
for (AccessControlApplicationMap appMap : teamMap.getAccessControlApplicationMaps()) {
if (appMap != null && appMap.getApplication() != null &&
appMap.getApplication().getId() != null &&
appMap.getRole() != null &&
appMap.getRole().getPermissions() != null) {
applicationPermissions.put(appMap.getApplication().getId(),
appMap.getRole().getPermissions());
applicationPermissions.get(appMap.getApplication().getId()).add(Permission.READ_ACCESS);
}
}
}
}
}
return applicationPermissions;
}
@Override
@Transactional(readOnly = true)
public Map<Integer, Set<Permission>> getOrganizationPermissions(
Integer userId) {
Map<Integer, Set<Permission>> organizationPermissions = new HashMap<Integer,Set<Permission>>();
List<AccessControlTeamMap> maps = accessControlMapDao.retrieveAllMapsForUser(userId);
if (maps != null) {
for (AccessControlTeamMap map : maps) {
if (map != null && map.getOrganization() != null &&
map.getOrganization().getId() != null &&
map.getRole() != null &&
map.getRole().getPermissions() != null) {
organizationPermissions.put(map.getOrganization().getId(),
map.getRole().getPermissions());
organizationPermissions.get(map.getOrganization().getId()).add(Permission.READ_ACCESS);
}
}
}
return organizationPermissions;
}
@Override
@Transactional(readOnly = true)
public boolean hasRemovedAdminPermissions(User user) {
if (user == null || user.getId() == null) {
return true; // should never get here
}
Set<Permission> dbPerms = getGlobalPermissions(user.getId());
if (user.getGlobalRole() == null || user.getGlobalRole().getId() == null) {
return dbPerms.contains(Permission.CAN_MANAGE_USERS) ||
dbPerms.contains(Permission.CAN_MANAGE_ROLES);
}
Role newRole = roleDao.retrieveById(user.getGlobalRole().getId());
if (newRole == null) {
return false;
}
Set<Permission> newPerms = newRole.getPermissions();
if (newPerms == null) {
return false;
}
return user.getGlobalRole() != null &&
(!newPerms.contains(Permission.CAN_MANAGE_USERS) && dbPerms.contains(Permission.CAN_MANAGE_USERS)) ||
(!newPerms.contains(Permission.CAN_MANAGE_ROLES) && dbPerms.contains(Permission.CAN_MANAGE_ROLES));
}
@Override
@Transactional(readOnly = true)
public User loadLdapUser(String name) {
return userDao.retrieveLdapUser(name);
}
// This is a terrible idea, we should switch to a strategy that
// actually lets us use normal model validation
@Override
public User applyChanges(User user, Integer userId) {
if (user == null || userId == null) {
return null;
}
User returnUser = loadUser(userId);
if (returnUser == null) {
return null;
}
// returnUser.setName(user.getName());
// returnUser.setGlobalRole(user.getGlobalRole());
// returnUser.setUnencryptedPassword(user.getUnencryptedPassword());
// returnUser.setPasswordConfirm(user.getPasswordConfirm());
// returnUser.setHasGlobalGroupAccess(user.getHasGlobalGroupAccess());
// returnUser.setIsLdapUser(user.getIsLdapUser());
user.setAccessControlTeamMaps(returnUser.getAccessControlTeamMaps());
user.setActive(returnUser.isActive());
user.setApproved(returnUser.isApproved());
user.setCreatedDate(returnUser.getCreatedDate());
user.setCurrentPassword(returnUser.getCurrentPassword());
user.setFailedPasswordAttempts(returnUser.getFailedPasswordAttempts());
user.setFailedPasswordAttemptWindowStart(returnUser.getFailedPasswordAttemptWindowStart());
user.setHasChangedInitialPassword(returnUser.isHasChangedInitialPassword());
user.setId(userId);
user.setLastLoginDate(returnUser.getLastLoginDate());
user.setLastPasswordChangedDate(returnUser.getLastPasswordChangedDate());
user.setModifiedDate(returnUser.getModifiedDate());
user.setSalt(returnUser.getSalt());
user.setPassword(returnUser.getPassword());
return user;
}
}