/* * Computoser is a music-composition algorithm and a website to present the results * Copyright (C) 2012-2014 Bozhidar Bozhanov * * Computoser is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * Computoser 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Computoser. If not, see <http://www.gnu.org/licenses/>. */ package com.music.service; import java.util.List; import java.util.UUID; import javax.inject.Inject; import org.hibernate.StaleStateException; import org.joda.time.DateTime; import org.joda.time.Period; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.social.connect.Connection; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.music.dao.UserDao; import com.music.model.persistent.SocialAuthentication; import com.music.model.persistent.User; import com.music.service.auth.JpaConnectionRepository; import com.music.util.SecurityUtils; @Service public class UserService { private static final Logger logger = LoggerFactory.getLogger(UserService.class); @Inject private UserDao userDao; @Value("${hmac.key}") private String hmacKey; @Transactional public void connect(Long userId, SocialAuthentication auth) { List<SocialAuthentication> existingAuths = userDao.getSocialAuthentications(auth.getProviderId(), auth.getProviderUserId()); if (existingAuths.isEmpty()) { User user = userDao.getById(User.class, userId); auth.setUser(user); userDao.persist(auth); } else { SocialAuthentication existingAuth = existingAuths.get(0); existingAuth.setExpirationTime(auth.getExpirationTime()); existingAuth.setRefreshToken(auth.getRefreshToken()); existingAuth.setImageUrl(auth.getImageUrl()); userDao.persist(existingAuth); } } @Transactional public void deleteSocialAuthentication(Long userId, String providerId) { userDao.deleteSocialAuthentication(userId, providerId); } @Transactional(readOnly=true) public User getUser(long id) { return userDao.getById(User.class, id); } @Transactional(readOnly=true) public User getUserByEmail(String email) { return userDao.getByPropertyValue(User.class, "email", email); } @Transactional public User completeUserRegistration(String email, String username, String names, Connection<?> connection, boolean loginAutomatically, boolean receiveDailyDigest) { User user = new User(); user.setEmail(email); user.setNames(names); user.setUsername(username); user.setLoginAutomatically(loginAutomatically); user.setRegistrationTime(new DateTime()); user.setReceiveDailyDigest(receiveDailyDigest); user = userDao.persist(user); if (connection != null) { SocialAuthentication auth = JpaConnectionRepository.connectionToAuth(connection); auth.setUser(user); userDao.persist(auth); } return user; } @Transactional public void unsubscribe(long id, String hash) { User user = userDao.getById(User.class, id); if (hash.equals(SecurityUtils.hmac(user.getEmail(), hmacKey))) { user.setReceiveDailyDigest(false); userDao.persist(user); } } /** * http://jaspan.com/improved_persistent_login_cookie_best_practice */ @Transactional(rollbackFor=StaleStateException.class) public User rememberMeLogin(String token, String series) { User existingLogin = userDao.getLoginFromAuthToken(token, series); if (existingLogin == null) { User loginBySeries = userDao.getByPropertyValue(User.class, "loginSeries", series); // if a login series exists, assume the previous token was stolen, so deleting all persistent logins. // An exception is a request made within a few seconds from the last login time // which may mean request from the same browser that is not yet aware of the renewed cookie if (loginBySeries != null && new Period(loginBySeries.getLastLoginTime(), new DateTime()).getSeconds() < 5) { logger.info("Assuming login cookies theft; deleting all sessions for user " + loginBySeries); loginBySeries.setLoginSeries(null); loginBySeries.setLoginToken(null); userDao.persist(loginBySeries); } else if (logger.isDebugEnabled()) { logger.debug("No existing login found for token=" + token + ", series=" + series); } return null; } if (logger.isDebugEnabled()) { logger.debug("Existing login found for token=" + token + " and series=" + series); } fillUserWithNewTokens(existingLogin, series); return existingLogin; } @Transactional public void fillUserWithNewTokens(User user, String series) { user.setLoginToken(UUID.randomUUID().toString()); user.setLoginSeries(series != null ? series : UUID.randomUUID().toString()); user.setLastLoginTime(new DateTime()); userDao.persist(user); } @Transactional(readOnly=true) public SocialAuthentication getTwitterAuthentication(User user) { if (user == null) { return null; } SocialAuthentication auth = userDao.getTwitterAuthentication(user); return auth; } }