/**
* Copyright (C) 2011 JTalks.org Team
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library 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
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jtalks.jcommune.service.transactional;
import org.apache.commons.lang.RandomStringUtils;
import org.joda.time.DateTime;
import org.joda.time.Period;
import org.jtalks.common.model.dao.GroupDao;
import org.jtalks.common.model.entity.User;
import org.jtalks.jcommune.model.dao.PostDao;
import org.jtalks.jcommune.model.dao.UserDao;
import org.jtalks.jcommune.model.dto.LoginUserDto;
import org.jtalks.jcommune.model.entity.*;
import org.jtalks.jcommune.plugin.api.exceptions.NoConnectionException;
import org.jtalks.jcommune.plugin.api.exceptions.NotFoundException;
import org.jtalks.jcommune.plugin.api.exceptions.UnexpectedErrorException;
import org.jtalks.jcommune.plugin.api.service.UserReader;
import org.jtalks.jcommune.service.Authenticator;
import org.jtalks.jcommune.service.UserService;
import org.jtalks.jcommune.service.dto.UserInfoContainer;
import org.jtalks.jcommune.service.dto.UserNotificationsContainer;
import org.jtalks.jcommune.service.dto.UserSecurityContainer;
import org.jtalks.jcommune.service.exceptions.MailingFailedException;
import org.jtalks.jcommune.service.nontransactional.Base64Wrapper;
import org.jtalks.jcommune.service.nontransactional.EncryptionService;
import org.jtalks.jcommune.service.nontransactional.MailService;
import org.jtalks.jcommune.service.nontransactional.MentionedUsers;
import org.jtalks.jcommune.service.security.SecurityService;
import org.jtalks.jcommune.service.util.AuthenticationStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.prepost.PreAuthorize;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
/**
* User service class. This class contains method needed to manipulate with User persistent entity.
* Note that this class also encrypts passwords during account creation, password changing, generating
* a random password.
*
* @author Osadchuck Eugeny
* @author Kirill Afonin
* @author Alexandre Teterin
* @author Evgeniy Naumenko
* @author Mikhail Zaitsev
* @author Andrei Alikov
* @author Andrey Pogorelov
*/
public class TransactionalUserService extends AbstractTransactionalEntityService<JCUser, UserDao>
implements UserService, UserReader {
private static final Logger LOGGER = LoggerFactory.getLogger(TransactionalUserService.class);
protected static final int MAX_SEARCH_USER_COUNT=20;
private final PostDao postDao;
private final Authenticator authenticator;
private final GroupDao groupDao;
private final SecurityService securityService;
private final MailService mailService;
private final Base64Wrapper base64Wrapper;
//Important, use for every password creation.
private final EncryptionService encryptionService;
/**
* Create an instance of User entity based service
*
* @param dao for operations with data storage
* @param groupDao for user group operations with data storage
* @param securityService for security
* @param mailService to send e-mails
* @param base64Wrapper for avatar image-related operations
* @param encryptionService encodes user password before store
* @param postDao for operations with posts
* @param authenticator for user authentication
*/
public TransactionalUserService(UserDao dao,
GroupDao groupDao,
SecurityService securityService,
MailService mailService,
Base64Wrapper base64Wrapper,
EncryptionService encryptionService,
PostDao postDao,
Authenticator authenticator) {
super(dao);
this.groupDao = groupDao;
this.securityService = securityService;
this.mailService = mailService;
this.base64Wrapper = base64Wrapper;
this.encryptionService = encryptionService;
this.postDao = postDao;
this.authenticator = authenticator;
}
/**
* {@inheritDoc}
*/
@Override
public JCUser getByUsername(String username) throws NotFoundException {
JCUser user = this.getDao().getByUsername(username);
if (user == null) {
String msg = "JCUser [" + username + "] not found.";
LOGGER.info(msg);
throw new NotFoundException(msg);
}
return user;
}
/**
* {@inheritDoc}
*/
@Override
public User getCommonUserByUsername(String username) throws NotFoundException {
User user = this.getDao().getCommonUserByUsername(username);
if (user == null) {
String msg = "Common User [" + username + "] not found.";
LOGGER.info(msg);
throw new NotFoundException(msg);
}
return user;
}
/**
* {@inheritDoc}
*/
@Override
public List<String> getUsernames(String pattern) {
int usernameCount = 10;
return getDao().getUsernames(pattern, usernameCount);
}
/**
* {@inheritDoc}
*/
@Override
public JCUser getCurrentUser() {
UserInfo userInfo = securityService.getCurrentUserBasicInfo();
return userInfo != null ? this.getDao().loadById(userInfo.getId()) : new AnonymousUser();
}
/**
* {@inheritDoc}
*/
@Override
public void updateLastLoginTime(JCUser user) {
user.updateLastLoginTime();
this.getDao().saveOrUpdate(user);
}
/**
* {@inheritDoc}
*/
@Override
public JCUser saveEditedUserProfile(
long editedUserId, UserInfoContainer editedUserProfileInfo) throws NotFoundException {
JCUser editedUser = this.get(editedUserId);
byte[] decodedAvatar = base64Wrapper.decodeB64Bytes(editedUserProfileInfo.getB64EncodedAvatar());
editedUser.setEmail(editedUserProfileInfo.getEmail());
if (!Arrays.equals(editedUser.getAvatar(), decodedAvatar)) {
editedUser.setAvatarLastModificationTime(new DateTime());
}
editedUser.setAvatar(decodedAvatar);
editedUser.setSignature(editedUserProfileInfo.getSignature());
editedUser.setFirstName(editedUserProfileInfo.getFirstName());
editedUser.setLastName(editedUserProfileInfo.getLastName());
editedUser.setPageSize(editedUserProfileInfo.getPageSize());
editedUser.setLocation(editedUserProfileInfo.getLocation());
this.getDao().saveOrUpdate(editedUser);
LOGGER.info("Updated user profile. Username: {}", editedUser.getUsername());
return editedUser;
}
/**
* {@inheritDoc}
*/
@Override
public JCUser saveEditedUserSecurity(long editedUserId, UserSecurityContainer userSecurityInfo)
throws NotFoundException {
JCUser editedUser = this.get(editedUserId);
String newPassword = userSecurityInfo.getNewPassword();
if (newPassword != null) {
String encryptedPassword = encryptionService.encryptPassword(newPassword);
editedUser.setPassword(encryptedPassword);
}
this.getDao().saveOrUpdate(editedUser);
LOGGER.info("Updated user security settings. Username: {}", editedUser.getUsername());
return editedUser;
}
/**
* {@inheritDoc}
*/
@Override
public JCUser saveEditedUserNotifications(long editedUserId, UserNotificationsContainer userNotificationsInfo)
throws NotFoundException {
JCUser editedUser = this.get(editedUserId);
editedUser.setMentioningNotificationsEnabled(userNotificationsInfo.isMentioningNotificationsEnabled());
editedUser.setSendPmNotification(userNotificationsInfo.isSendPmNotification());
editedUser.setAutosubscribe(userNotificationsInfo.isAutosubscribe());
this.getDao().saveOrUpdate(editedUser);
LOGGER.info("Updated user notification settings. Username: {}", editedUser.getUsername());
return editedUser;
}
/**
* {@inheritDoc}
*/
@Override
public void restorePassword(String email) throws MailingFailedException {
JCUser user = this.getDao().getByEmail(email);
String randomPassword = RandomStringUtils.randomAlphanumeric(6);
// first - mail attempt, then - database changes
mailService.sendPasswordRecoveryMail(user, randomPassword);
String encryptedRandomPassword = encryptionService.encryptPassword(randomPassword);
user.setPassword(encryptedRandomPassword);
this.getDao().saveOrUpdate(user);
LOGGER.info("New random password was set for user {}", user.getUsername());
}
/**
* {@inheritDoc}
*/
@Override
public JCUser getByUuid(String uuid) throws NotFoundException {
JCUser user = this.getDao().getByUuid(uuid);
if (user == null) {
throw new NotFoundException();
} else {
return user;
}
}
/**
* {@inheritDoc}
*/
@Override
// Temporarily disabled. Until we find the bug due to which activated users become not activated.
// @Scheduled(cron = "0 * * * * *") // cron expression: invoke every hour at :00 min, e.g. 11:00, 12:00 and so on
public void deleteUnactivatedAccountsByTimer() {
DateTime today = new DateTime();
for (JCUser user : this.getDao().getNonActivatedUsers()) {
Period period = new Period(user.getRegistrationDate(), today);
if (period.getDays() > 0) {
this.getDao().delete(user);
}
}
}
/**
* {@inheritDoc}
*/
@Override
@PreAuthorize("hasPermission(#userId, 'USER', 'ProfilePermission.EDIT_OTHERS_PROFILE')")
public void checkPermissionToEditOtherProfiles(Long userId) {
LOGGER.debug("Check permission to edit other profiles for user - " + userId);
}
/**
* {@inheritDoc}
*/
@Override
@PreAuthorize("hasPermission(#userId, 'USER', 'ProfilePermission.EDIT_OWN_PROFILE')")
public void checkPermissionToEditOwnProfile(Long userId) {
LOGGER.debug("Check permission to edit own profile for user - " + userId);
}
/**
* {@inheritDoc}
*/
@Override
@PreAuthorize("hasPermission(#userId, 'USER', 'ProfilePermission.CREATE_FORUM_FAQ')")
public void checkPermissionToCreateAndEditSimplePage(Long userId) {
LOGGER.debug("Check permission to create or edit simple(static) pages - " + userId);
}
/**
* {@inheritDoc}
*/
@Override
public AuthenticationStatus loginUser(LoginUserDto loginUserDto, HttpServletRequest request, HttpServletResponse response)
throws UnexpectedErrorException, NoConnectionException {
return authenticator.authenticate(loginUserDto, request, response);
}
/**
* {@inheritDoc}
*/
@Override
public String processUserBbCodesInPost(String postContent) {
MentionedUsers mentionedUsers = MentionedUsers.parse(postContent);
return mentionedUsers.getTextWithProcessedUserTags(getDao());
}
/**
* {@inheritDoc}
*/
@Override
public void notifyAndMarkNewlyMentionedUsers(Post post) {
MentionedUsers mentionedUsers = MentionedUsers.parse(post);
List<JCUser> usersToNotify = mentionedUsers.getNewUsersToNotify(getDao());
for (JCUser user : usersToNotify) {
mailService.sendUserMentionedNotification(user, post.getId());
}
mentionedUsers.markUsersAsAlreadyNotified(postDao);
}
/**
* {@inheritDoc}
*/
@Override
public void changeLanguage(JCUser jcUser, Language newLang) {
jcUser.setLanguage(newLang);
this.getDao().saveOrUpdate(jcUser);
}
@Override
@PreAuthorize("hasPermission(#forumComponentId, 'COMPONENT', 'GeneralPermission.ADMIN')")
public List<JCUser> findByUsernameOrEmail(long forumComponentId, String searchKey) {
return getDao().findByUsernameOrEmail(searchKey, MAX_SEARCH_USER_COUNT);
}
@Override
@PreAuthorize("hasPermission(#forumComponentId, 'COMPONENT', 'GeneralPermission.ADMIN')")
public List<Long> getUserGroupIDs(long forumComponentId, long userID) throws NotFoundException {
JCUser jcUser = getDao().get(userID);
return jcUser.getGroupsIDs();
}
@Override
@PreAuthorize("hasPermission(#forumComponentId, 'COMPONENT', 'GeneralPermission.ADMIN')")
public void addUserToGroup(long forumComponentId, long userID, long groupID) throws NotFoundException {
JCUser jcUser = getDao().get(userID);
jcUser.addGroup(groupDao.get(groupID));
this.getDao().saveOrUpdate(jcUser);
}
@Override
@PreAuthorize("hasPermission(#forumComponentId, 'COMPONENT', 'GeneralPermission.ADMIN')")
public void deleteUserFromGroup(long forumComponentId, long userID, long groupID) throws NotFoundException {
JCUser jcUser = getDao().get(userID);
jcUser.deleteGroup(groupDao.get(groupID));
this.getDao().saveOrUpdate(jcUser);
}
}