230 lines
7.0 KiB
Kotlin
230 lines
7.0 KiB
Kotlin
package integration.routing
|
|
|
|
import be.vandewalleh.Config
|
|
import be.vandewalleh.auth.SimpleJWT
|
|
import be.vandewalleh.entities.User
|
|
import be.vandewalleh.features.PasswordHash
|
|
import be.vandewalleh.mainModule
|
|
import be.vandewalleh.module
|
|
import be.vandewalleh.services.UserService
|
|
import com.auth0.jwt.JWT
|
|
import com.auth0.jwt.algorithms.Algorithm
|
|
import io.ktor.http.*
|
|
import io.ktor.server.testing.*
|
|
import io.mockk.coEvery
|
|
import io.mockk.coVerify
|
|
import io.mockk.mockk
|
|
import org.amshove.kluent.*
|
|
import org.json.JSONObject
|
|
import org.junit.jupiter.api.*
|
|
import org.kodein.di.DI
|
|
import org.kodein.di.bind
|
|
import org.kodein.di.instance
|
|
import utils.*
|
|
import java.util.*
|
|
|
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
|
class AuthControllerKtTest {
|
|
|
|
private val userService = mockk<UserService>()
|
|
|
|
private val kodein = DI {
|
|
import(mainModule, allowOverride = true)
|
|
bind<UserService>(overrides = true) with instance(userService)
|
|
}
|
|
|
|
private val passwordHash by kodein.instance<PasswordHash>()
|
|
|
|
|
|
init {
|
|
|
|
val user = User {
|
|
password = passwordHash.crypt("password")
|
|
username = "existing"
|
|
}
|
|
user["id"] = 1
|
|
|
|
coEvery { userService.find("existing") } returns user
|
|
coEvery { userService.exists(1) } returns true
|
|
coEvery { userService.find(1) } returns User {
|
|
username = "existing"
|
|
}
|
|
|
|
val user2 = User {
|
|
password = passwordHash.crypt("right password")
|
|
username = "wrong"
|
|
}
|
|
user["id"] = 2
|
|
coEvery { userService.find("wrong") } returns user2
|
|
|
|
coEvery { userService.find("notExisting") } returns null
|
|
|
|
coEvery { userService.exists(3) } returns false
|
|
coEvery { userService.find(3) } returns null
|
|
}
|
|
|
|
|
|
private val testEngine = TestApplicationEngine().apply {
|
|
start()
|
|
application.module(kodein)
|
|
}
|
|
|
|
@Nested
|
|
inner class Login {
|
|
@Test
|
|
fun `login existing user with valid password`() {
|
|
val res = testEngine.post("/user/login") {
|
|
json {
|
|
it["username"] = "existing"
|
|
it["password"] = "password"
|
|
}
|
|
}
|
|
|
|
coVerify { userService.find("existing") }
|
|
|
|
res.status() `should be equal to` HttpStatusCode.OK
|
|
val jsonObject = JSONObject(res.content)
|
|
|
|
val hasToken = jsonObject.has("token")
|
|
hasToken `should be equal to` true
|
|
|
|
jsonObject.keyList() `should be equal to` listOf("token", "refreshToken")
|
|
|
|
val authJwt by kodein.instance<SimpleJWT>(tag = "auth")
|
|
val token = jsonObject.getString("token")
|
|
authJwt.verifier.verify(token)
|
|
|
|
val refreshJwt by kodein.instance<SimpleJWT>(tag = "refresh")
|
|
val refreshToken = jsonObject.getString("refreshToken")
|
|
refreshJwt.verifier.verify(refreshToken)
|
|
}
|
|
|
|
@Test
|
|
fun `login existing user with invalid password`() {
|
|
val res = testEngine.post("/user/login") {
|
|
json {
|
|
it["username"] = "wrong"
|
|
it["password"] = "not this"
|
|
}
|
|
}
|
|
|
|
coVerify { userService.find("wrong") }
|
|
|
|
res.status() `should be equal to` HttpStatusCode.Unauthorized
|
|
res.content `should strictly be equal to json` """{msg: "Unauthorized"}"""
|
|
}
|
|
|
|
@Test
|
|
fun `login not existing user`() {
|
|
val res = testEngine.post("/user/login") {
|
|
json {
|
|
it["username"] = "notExisting"
|
|
it["password"] = "babababa"
|
|
}
|
|
}
|
|
|
|
coVerify { userService.find("notExisting") }
|
|
|
|
res.status() `should be equal to` HttpStatusCode.Unauthorized
|
|
res.content `should strictly be equal to json` """{msg: "Unauthorized"}"""
|
|
}
|
|
|
|
@Test
|
|
fun `login without body`() {
|
|
val res = testEngine.post("/user/login") {
|
|
addHeader(HttpHeaders.ContentType, "application/json")
|
|
}
|
|
res.status() `should be equal to` HttpStatusCode.BadRequest
|
|
}
|
|
|
|
}
|
|
|
|
@Nested
|
|
inner class Refresh {
|
|
|
|
@Test
|
|
fun `test valid refresh token`() {
|
|
val refreshJwt by kodein.instance<SimpleJWT>(tag = "refresh")
|
|
val refreshToken = refreshJwt.sign(1)
|
|
|
|
val res = testEngine.post("/user/refresh_token") {
|
|
json {
|
|
it["refreshToken"] = refreshToken
|
|
}
|
|
}
|
|
|
|
val jsonObject = JSONObject(res.content)
|
|
jsonObject.keyList() `should be equal to` listOf("token", "refreshToken")
|
|
|
|
coVerify { userService.exists(1) }
|
|
res.status() `should be equal to` HttpStatusCode.OK
|
|
}
|
|
|
|
@Test
|
|
fun `test valid refresh token for deleted user`() {
|
|
val refreshJwt by kodein.instance<SimpleJWT>(tag = "refresh")
|
|
val refreshToken = refreshJwt.sign(3)
|
|
|
|
val res = testEngine.post("/user/refresh_token") {
|
|
json {
|
|
it["refreshToken"] = refreshToken
|
|
}
|
|
}
|
|
|
|
coVerify { userService.exists(3) }
|
|
res.status() `should be equal to` HttpStatusCode.Unauthorized
|
|
res.content `should strictly be equal to json` """{msg: "Unauthorized"}"""
|
|
}
|
|
|
|
@Test
|
|
fun `test expired refresh token for existing user`() {
|
|
val config by kodein.instance<Config>()
|
|
val algorithm = Algorithm.HMAC256(config.jwt.refresh.secret.value)
|
|
|
|
val expiredToken = JWT.create()
|
|
.withClaim("id", 1)
|
|
.withExpiresAt(Date(0)) // January 1, 1970, 00:00:00 GMT
|
|
.sign(algorithm)
|
|
|
|
val res = testEngine.post("/user/refresh_token") {
|
|
json {
|
|
it["refreshToken"] = expiredToken
|
|
}
|
|
}
|
|
|
|
res.status() `should be equal to` HttpStatusCode.Unauthorized
|
|
res.content `should strictly be equal to json` """{msg: "Unauthorized"}"""
|
|
}
|
|
|
|
}
|
|
|
|
@Nested
|
|
inner class UserInfo {
|
|
|
|
@Test
|
|
fun `test user info for existing user`() {
|
|
val authJwt by kodein.instance<SimpleJWT>(tag = "auth")
|
|
val token = authJwt.sign(1)
|
|
val res = testEngine.get("/user/me") {
|
|
setToken(token)
|
|
}
|
|
res.content `should strictly be equal to json` """{user:{username:"existing"}}"""
|
|
res.status() `should be equal to` HttpStatusCode.OK
|
|
}
|
|
|
|
@Test
|
|
fun `test user info on deleted user`() {
|
|
val authJwt by kodein.instance<SimpleJWT>(tag = "auth")
|
|
val token = authJwt.sign(3)
|
|
val res = testEngine.get("/user/me") {
|
|
setToken(token)
|
|
}
|
|
res.status()!!.value `should not be in range` (200..299)
|
|
val jsonObject = JSONObject(res.content)
|
|
jsonObject.keyList() `should be equal to` listOf("msg")
|
|
}
|
|
}
|
|
|
|
|
|
}
|