package authentication import java.math.BigInteger import java.security.SecureRandom import authentication.UserAuthResult.{UserNotExists, UserExists, Success, InvalidData} import core.authentication.Identity import org.joda.time.DateTime import org.mindrot.jbcrypt.BCrypt import redis.RedisClient import token.TokenRepository import user.{UserRepository, User} import core.authentication.UserSerializer._ import scala.concurrent.{Future, ExecutionContext} class UserAuthService(tokenRepo: TokenRepository, userAuthRepo: UserAuthRepository, userRepo: UserRepository) (implicit ec: ExecutionContext, redisClient: RedisClient) { def register(request: UserRegistrationRequest): Future[UserAuthResult] = { if (!RegistrationValidator.isDataValid(request.email, request.password)) { Future.successful(InvalidData("E-mail or password is invalid")) } else { userAuthRepo.findByUserEmail(request.email).flatMap { case Some(userAuth) => Future.successful(UserExists("E-mail already in use")) case None => val now = DateTime.now().getMillis val token = generateToken for { user <- userRepo.insert(User(None, now, request.firstName, request.lastName, request.gender, None, None, None, request.role)) _ <- userAuthRepo.insert(UserAuth(None, request.email, hashPassword(request.password), user.id.get)) _ <- tokenRepo.insert(token, user.id.get) _ <- redisClient.setnx(token, Identity.User(user.id.get, user.role)) } yield { Success(token) } } } } def login(request: UserLoginRequest): Future[UserAuthResult] = { userAuthRepo.findByUserEmail(request.email).flatMap { case Some(userAuth) if checkPassword(request.password, userAuth.password) => val token = generateToken for { Some(user) <- userRepo.findByUserId(userAuth.userId) _ <- tokenRepo.insert(token, user.id.get) _ <- redisClient.setnx(token, Identity.User(user.id.get, user.role)) } yield { Success(token) } case Some(_) => Future.successful(InvalidData("Password is invalid")) case None => Future.successful(UserNotExists("E-mail does not exist")) } } private lazy val random = new SecureRandom() private def generateToken: String = new BigInteger(255, random).toString(32) private def hashPassword(password: String): String = BCrypt.hashpw(password, BCrypt.gensalt(12)) private def checkPassword(password: String, passwordHash: String): Boolean = BCrypt.checkpw(password, passwordHash) } trait UserAuthResult object UserAuthResult { case class InvalidData(msg: String) extends UserAuthResult case class UserExists(msg: String) extends UserAuthResult case class UserNotExists(msg: String) extends UserAuthResult case class Success(token: String) extends UserAuthResult }