Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.env*
.gitignore
Dockerfile*

.git/
log/
5 changes: 4 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,15 @@ dependencies {
implementation("io.springfox:springfox-boot-starter:3.0.0")
implementation("io.springfox:springfox-swagger-ui:3.0.0")


testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
exclude(module = "mockito-core")
}
testImplementation("org.springframework.security:spring-security-test")

// mail
implementation("org.springframework.boot:spring-boot-starter-mail")
implementation("org.springframework.boot:spring-boot-starter-security")
}

tasks.withType<KotlinCompile> {
Expand Down
5 changes: 5 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ services:
ports:
- 3306:3306
restart: on-failure
mailcatcher:
image: schickling/mailcatcher
ports:
- 1025:1025
- 1080:1080
volumes:
data:
driver: local
9 changes: 8 additions & 1 deletion src/main/kotlin/com/devh/vitstore/VitstoreApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@ package com.devh.vitstore

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import java.util.*
import javax.annotation.PostConstruct

@SpringBootApplication
class VitstoreApplication
class VitstoreApplication {
@PostConstruct
fun postConstruct() {
TimeZone.setDefault(TimeZone.getTimeZone("JST"))
}
}

fun main(args: Array<String>) {
runApplication<VitstoreApplication>(*args)
Expand Down
45 changes: 45 additions & 0 deletions src/main/kotlin/com/devh/vitstore/controller/UserController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.devh.vitstore.controller

import com.devh.vitstore.model.User
import com.devh.vitstore.service.mail.OnRegistrationCompleteEvent
import com.devh.vitstore.service.UserService
import org.springframework.context.ApplicationEventPublisher
import org.springframework.web.bind.annotation.*
import java.io.UnsupportedEncodingException
import java.util.*
import javax.servlet.http.HttpServletRequest
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.core.env.Environment

@RestController
class UserController(
private val userService: UserService,
private val eventPublisher: ApplicationEventPublisher,
) {
@Value("\${api.hostUrl}")
private val hostUrl: String = ""

@PostMapping("registration")
fun registration(@RequestBody user: User, request: HttpServletRequest): String? {
val email = user.email ?: return "Please enter email."
if (userService.emailExist(email)) return "An account for that username/email already exists."
val registered = userService.registerNewUserAccount(user)
// val appUrl: String = request.contextPath
// val appUrl: String = "http://localhost:8080"
eventPublisher.publishEvent(OnRegistrationCompleteEvent(registered,
request.locale, hostUrl))
return "ok"
}

@GetMapping("/registrationConfirm")
@Throws(UnsupportedEncodingException::class)
fun confirmRegistration(@RequestParam("activeToken") activeToken: String): String {
val user = userService.getUser(activeToken) ?: return "InvalidToken or user activated"
if (user.activeTokenExpiredAt!! <= Calendar.getInstance().time) {
return "Expired!!!"
}
userService.activeUser(user)
return "ok"
}
}
33 changes: 33 additions & 0 deletions src/main/kotlin/com/devh/vitstore/model/User.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.devh.vitstore.model

import java.util.*
import javax.persistence.*

@Entity
@Table(name = "users")
data class User(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private val id: Long = 0,

@Column
var username: String? = "",

@Column
var email: String? = null,

@Column
var password: String? = null,

@Column(name = "phone_number")
var phoneNumber: String? = null,

@Column
var status: Int = 0,

@Column(name = "active_token")
var activeToken: String? = null,

@Column(name = "active_token_expired_at")
var activeTokenExpiredAt: Date? = Date(System.currentTimeMillis())
)
11 changes: 11 additions & 0 deletions src/main/kotlin/com/devh/vitstore/repository/UserRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.devh.vitstore.repository

import com.devh.vitstore.model.User
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface UserRepository : JpaRepository<User, Long?> {
fun findByActiveToken(activeToken: String): User?
fun findByEmail(email: String): User?
}
18 changes: 18 additions & 0 deletions src/main/kotlin/com/devh/vitstore/security/WebSecurityConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.devh.vitstore.security

import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity

import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter

import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity


@EnableWebSecurity
@Configuration
class WebSecurityConfig : WebSecurityConfigurerAdapter() {
@Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http.csrf().disable()
}
}
55 changes: 55 additions & 0 deletions src/main/kotlin/com/devh/vitstore/service/UserService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.devh.vitstore.service

import com.devh.vitstore.model.User
import com.devh.vitstore.repository.UserRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
import java.util.*
import javax.transaction.Transactional


@Service
@Transactional
class UserService {
@Autowired
private val repository: UserRepository? = null

@Bean
private fun bcryptEncoder(): PasswordEncoder {
return BCryptPasswordEncoder()
}

fun emailExist(email: String): Boolean {
return repository?.findByEmail(email) != null
}

fun getUser(verificationToken: String): User? {
return repository!!.findByActiveToken(verificationToken)
}

fun activeUser(user: User) {
user.status = 1
user.activeToken = null
repository!!.save(user)
}

fun saveActiveToken(user: User?, token: String?) {
val calendar = Calendar.getInstance()
calendar.add(Calendar.DAY_OF_YEAR, 1)
val tomorrow = calendar.time
user?.activeToken = token
user?.activeTokenExpiredAt = tomorrow
repository!!.save(user!!)
}

fun registerNewUserAccount(user: User): User {
val newUser = User()
newUser.password = bcryptEncoder().encode(user.password)
newUser.email = user.email

return repository!!.save(newUser)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.devh.vitstore.service.mail

import com.devh.vitstore.model.User
import org.springframework.context.ApplicationEvent
import java.util.*

class OnRegistrationCompleteEvent(
val user: User, val locale: Locale, val appUrl: String,
) : ApplicationEvent(user)
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.devh.vitstore.service.mail

import com.devh.vitstore.model.User
import com.devh.vitstore.service.UserService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationListener
import org.springframework.context.MessageSource
import org.springframework.mail.SimpleMailMessage
import org.springframework.mail.javamail.JavaMailSender
import org.springframework.stereotype.Component
import java.util.*


@Component
class RegistrationListener : ApplicationListener<OnRegistrationCompleteEvent> {
@Autowired
private val service: UserService? = null

@Autowired
private val messages: MessageSource? = null

@Autowired
private val mailSender: JavaMailSender? = null

// API
override fun onApplicationEvent(event: OnRegistrationCompleteEvent) {
confirmRegistration(event)
}

private fun confirmRegistration(event: OnRegistrationCompleteEvent) {
val user: User = event.user
val token = UUID.randomUUID().toString() // TODO su dung cach genate token khac
service?.saveActiveToken(user, token)
val email: SimpleMailMessage = constructEmailMessage(event, user, token)
mailSender?.send(email)
}

private fun constructEmailMessage(event: OnRegistrationCompleteEvent, user: User, token: String): SimpleMailMessage {
val recipientAddress: String = user.email!!
val subject = "Registration Confirmation"
val confirmationUrl: String = event.appUrl + "/registrationConfirm?token=" + token
val message = messages!!.getMessage("message.regSuccLink", null, "You registered successfully. To confirm your registration, please click on the below link.", event.locale)
val email = SimpleMailMessage()
email.setTo(recipientAddress)
email.setSubject(subject)
email.setText("$message \r\n$confirmationUrl")
return email
}
}
9 changes: 9 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ spring:
dialect: org.hibernate.dialect.MariaDB103Dialect
hibernate:
ddl-auto: update
mail:
host: smtp.gmail.com
port: 465
protocol: smtps
username: example@xxx.com
password: xxx

api:
hostUrl: http://localhost:8080
logging:
level:
org.springframework.security: DEBUG
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/db/migration/V1__CreateTableUsers.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
CREATE TABLE IF NOT EXISTS users(
id bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
username varchar(100) NOT NULL,
username varchar(100),
email varchar(255) NOT NULL,
password varchar(255) NOT NULL,
status tinyint(1),
Expand Down