/*
* Copyright (C) 2014 Toshiaki Maki
*
* Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package am.ik.categolj2.domain.service.user;
import am.ik.categolj2.core.message.MessageKeys;
import am.ik.categolj2.domain.model.Role;
import am.ik.categolj2.domain.model.User;
import am.ik.categolj2.domain.repository.role.RoleRepository;
import am.ik.categolj2.domain.repository.user.UserRepository;
import org.dozer.Mapper;
import org.joda.time.DateTime;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.terasoluna.gfw.common.date.DateFactory;
import org.terasoluna.gfw.common.exception.BusinessException;
import org.terasoluna.gfw.common.exception.ResourceNotFoundException;
import org.terasoluna.gfw.common.message.ResultMessages;
import javax.inject.Inject;
import java.util.HashSet;
import java.util.Set;
@Service
public class UserServiceImpl implements UserService {
@Inject
UserRepository userRepository;
@Inject
PasswordEncoder passwordEncoder;
@Inject
Mapper beanMapper;
@Inject
DateFactory dateFactory;
@Inject
RoleRepository roleRepository;
@Override
public User findOne(String username) {
User user = userRepository.findDetails(username);
if (user == null) {
ResultMessages messages = ResultMessages.error()
.add(MessageKeys.E_CT_US_8101, username);
throw new ResourceNotFoundException(messages);
}
return user;
}
@Override
public User findOneByUsernameOrEmail(String usernameOrEmail) {
User user = userRepository.findOneByEmail(usernameOrEmail);
if (user == null) {
user = userRepository.findDetails(usernameOrEmail);
}
if (user == null) {
ResultMessages messages = ResultMessages.error()
.add(MessageKeys.E_CT_US_8102, usernameOrEmail);
throw new ResourceNotFoundException(messages);
}
return user;
}
@Override
public Page<User> findPage(Pageable pageable) {
return userRepository.findAll(pageable);
}
@Transactional
@Override
public User create(User user, String rawPassword) {
Assert.notNull(user, "user must not be null");
String username = user.getUsername();
if (userRepository.exists(username)) {
ResultMessages messages = ResultMessages.error()
.add(MessageKeys.E_CT_US_8103, username);
throw new BusinessException(messages);
}
String email = user.getEmail();
if (userRepository.countByEmail(email) > 0) {
ResultMessages messages = ResultMessages.error()
.add(MessageKeys.E_CT_US_8104, email);
throw new BusinessException(messages);
}
// check roles
checkRoles(user);
String encodedPassword = passwordEncoder.encode(rawPassword);
user.setPassword(encodedPassword);
DateTime now = dateFactory.newDateTime();
user.setCreatedDate(now);
user.setLastModifiedDate(now);
// createdBy,lastModifiedBy are set by AuditingEntityListener
userRepository.save(user);
return user;
}
@Transactional
@Override
public User update(String username, User updatedUser,
String updatedRawPassword) {
Assert.notNull(updatedUser, "user must not be null");
preUpdate(username, updatedUser);
// encode raw password
String encodedPassword = passwordEncoder.encode(updatedRawPassword);
updatedUser.setPassword(encodedPassword);
return doUpdate(username, updatedUser);
}
@Transactional
@Override
public User updateWithoutPassword(String username, User updatedUser) {
Assert.notNull(updatedUser, "user must not be null");
Assert.notNull(updatedUser.getPassword(), "(old) password must not be null");
preUpdate(username, updatedUser);
return doUpdate(username, updatedUser);
}
private void preUpdate(String username, User updatedUser) {
String email = updatedUser.getEmail();
// check roles
checkRoles(updatedUser);
// check the given user is existing
if (!userRepository.exists(username)) {
ResultMessages messages = ResultMessages.error()
.add(MessageKeys.E_CT_US_8105, username);
throw new BusinessException(messages);
}
// check the given email is not used by others
long countUsingEmail = userRepository.countByEmailOtherThanMe(email, username);
if (countUsingEmail > 0) {
ResultMessages messages = ResultMessages.error()
.add(MessageKeys.E_CT_US_8104, email);
throw new BusinessException(messages);
}
boolean notExistAdminOtherThanMe = userRepository.countActiveAdminOtherThanMe(username) == 0;
boolean isAdmin = updatedUser.isAdmin();
// in case updated user is not admin or removed admin role
if (!isAdmin && notExistAdminOtherThanMe) {
ResultMessages messages = ResultMessages.error()
.add(MessageKeys.E_CT_US_8106);
throw new BusinessException(messages);
}
// if single admin is to be locked.
if (isAdmin && notExistAdminOtherThanMe && updatedUser.isLocked()) {
ResultMessages messages = ResultMessages.error()
.add(MessageKeys.E_CT_US_8107);
throw new BusinessException(messages);
}
// if single admin is to be disabled
if (isAdmin && notExistAdminOtherThanMe && !updatedUser.isEnabled()) {
ResultMessages messages = ResultMessages.error()
.add(MessageKeys.E_CT_US_8108);
throw new BusinessException(messages);
}
}
private void checkRoles(User user) {
Set<Role> rolesToReplace = new HashSet<>();
Set<Role> roles = user.getRoles();
if (roles != null) {
for (Role role : roles) {
Integer roleId = role.getRoleId();
Role loadedRole = roleRepository.findOne(roleId);
if (loadedRole == null) {
ResultMessages messages = ResultMessages.error()
.add(MessageKeys.E_CT_US_8109, roleId);
throw new BusinessException(messages);
}
rolesToReplace.add(loadedRole);
}
}
user.setRoles(rolesToReplace); // replace
}
private User doUpdate(String username, User updatedUser) {
DateTime now = dateFactory.newDateTime();
updatedUser.setLastModifiedDate(now);
// lastModifiedBy are set by AuditingEntityListener
User user = findOne(username);
// copy new values to user
beanMapper.map(updatedUser, user);
userRepository.save(user);
return user;
}
@Transactional
@Override
public void delete(String username) {
User user = findOne(username);
// check admin count
if (user.isAdmin() && userRepository.countActiveAdminOtherThanMe(username) == 0) {
ResultMessages messages = ResultMessages.error()
.add(MessageKeys.E_CT_US_8106);
throw new BusinessException(messages);
}
userRepository.delete(user);
}
}