UserService.kt
package delta.codecharacter.server.user
import delta.codecharacter.dtos.CompleteProfileRequestDto
import delta.codecharacter.dtos.RegisterUserRequestDto
import delta.codecharacter.dtos.UpdatePasswordRequestDto
import delta.codecharacter.server.exception.CustomException
import delta.codecharacter.server.user.activate_user.ActivateUserService
import delta.codecharacter.server.user.public_user.PublicUserService
import delta.codecharacter.server.user.rating_history.RatingHistoryService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Lazy
import org.springframework.dao.DuplicateKeyException
import org.springframework.http.HttpStatus
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetailsService
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.stereotype.Service
import java.util.Optional
import java.util.UUID
@Service
class UserService(
@Autowired private val userRepository: UserRepository,
@Autowired private val publicUserService: PublicUserService,
@Autowired private val ratingHistoryService: RatingHistoryService,
@Autowired private val activateUserService: ActivateUserService
) : UserDetailsService {
@Lazy @Autowired private lateinit var passwordEncoder: BCryptPasswordEncoder
override fun loadUserByUsername(email: String?): UserEntity {
if (email == null) {
throw UsernameNotFoundException("User not found")
}
val user = userRepository.findFirstByEmail(email)
if (user.isEmpty) {
throw UsernameNotFoundException("User not found")
} else if (!user.get().isEnabled) {
throw CustomException(HttpStatus.UNAUTHORIZED, "Email not verified")
} else if (!user.get().isAccountNonExpired) {
throw CustomException(HttpStatus.UNAUTHORIZED, "Account expired")
} else {
return user.get()
}
}
private fun createUserWithPassword(userId: UUID, password: String, email: String) {
val user =
UserEntity(
id = userId,
password = passwordEncoder.encode(password),
email = email,
loginType = LoginType.PASSWORD,
isProfileComplete = true,
isEnabled = false,
isAccountNonExpired = true,
isAccountNonLocked = true,
isCredentialsNonExpired = true,
userAuthorities = mutableListOf(SimpleGrantedAuthority("ROLE_USER"))
)
userRepository.save(user)
}
fun createUserWithOAuth(email: String, oauthProvider: LoginType): UserEntity {
val userId = UUID.randomUUID()
val user =
UserEntity(
id = userId,
password = passwordEncoder.encode(UUID.randomUUID().toString()),
email = email,
loginType = oauthProvider,
isProfileComplete = false,
isEnabled = true,
isAccountNonExpired = true,
isAccountNonLocked = true,
isCredentialsNonExpired = true,
userAuthorities = mutableListOf(SimpleGrantedAuthority("ROLE_USER_INCOMPLETE_PROFILE"))
)
ratingHistoryService.create(userId)
return userRepository.save(user)
}
fun getUserByEmail(email: String): Optional<UserEntity> {
return userRepository.findFirstByEmail(email)
}
fun updateUserPassword(userId: UUID, password: String) {
val user = userRepository.findById(userId).get()
userRepository.save(user.copy(password = passwordEncoder.encode(password)))
}
fun verifyUserPassword(userId: UUID, password: String): Boolean {
val user = userRepository.findById(userId)
return passwordEncoder.matches(password, user.get().password)
}
fun updatePassword(userId: UUID, updatePasswordRequestDto: UpdatePasswordRequestDto) {
val (oldPassword, password, passwordConfirmation) = updatePasswordRequestDto
if (password != passwordConfirmation) {
throw CustomException(HttpStatus.BAD_REQUEST, "Passwords do not match")
}
if (verifyUserPassword(userId, oldPassword)) {
val user = userRepository.findById(userId).get()
userRepository.save(user.copy(password = passwordEncoder.encode(password)))
} else {
throw CustomException(HttpStatus.BAD_REQUEST, "Old password is incorrect")
}
}
fun registerUser(registerUserRequestDto: RegisterUserRequestDto) {
val (username, name, email, password, passwordConfirmation, country, college, avatarId) =
registerUserRequestDto
if (password != passwordConfirmation) {
throw CustomException(
HttpStatus.BAD_REQUEST, "Password and password confirmation don't match"
)
}
if (!publicUserService.isUsernameUnique(username)) {
throw CustomException(HttpStatus.BAD_REQUEST, "Username already taken")
}
val userId = UUID.randomUUID()
try {
createUserWithPassword(userId, password, email)
publicUserService.create(userId, username, name, country, college, avatarId)
ratingHistoryService.create(userId)
activateUserService.sendActivationToken(userId, name, email)
} catch (duplicateError: DuplicateKeyException) {
throw CustomException(HttpStatus.BAD_REQUEST, "Username/Email already exists")
}
}
fun activateUser(userId: UUID, token: String) {
activateUserService.processActivationToken(userId, token)
val user = userRepository.findFirstById((userId)).get()
val activatedUser = user.copy(isEnabled = true)
userRepository.save(activatedUser)
}
fun completeUserProfile(userId: UUID, completeProfileRequestDto: CompleteProfileRequestDto) {
val (username, name, country, college, avatarId) = completeProfileRequestDto
val user = userRepository.findFirstById(userId).get()
publicUserService.create(userId, username, name, country, college, avatarId)
userRepository.save(
user.copy(
isProfileComplete = true,
userAuthorities = mutableListOf(SimpleGrantedAuthority("ROLE_USER"))
)
)
}
}