+login by token and server library improvements

This commit is contained in:
Sergey Chernov 2022-11-26 17:52:09 +01:00
parent 5df6709983
commit 449de2e504
5 changed files with 70 additions and 20 deletions

View File

@ -44,7 +44,7 @@ kotlin {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3")
api("net.sergeych:unikrypto:1.2.1-SNAPSHOT")
api("net.sergeych:parsec3:0.3.1-SNAPSHOT")
api("net.sergeych:parsec3:0.3.2-SNAPSHOT")
api("net.sergeych:boss-serialization-mp:0.2.4-SNAPSHOT")
3 }
}

View File

@ -65,7 +65,7 @@ class SuperloginClient<D, S : WithAdapter>(
adapterReady = CompletableDeferred()
}
globalLaunch {
transport.adapter().invokeCommand(api.slLogout)
transport.adapter().invokeCommand(serverApi.slLogout)
adapterReady.complete(Unit)
}
} else {
@ -102,12 +102,12 @@ class SuperloginClient<D, S : WithAdapter>(
private var jobs = listOf<Job>()
private val api = SuperloginServerApi<WithAdapter>()
private val serverApi = SuperloginServerApi<WithAdapter>()
private suspend fun tryRestoreLogin() {
slData?.loginToken?.let { token ->
try {
val ar = transport.adapter().invokeCommand(api.slLoginByToken, token)
val ar = transport.adapter().invokeCommand(serverApi.slLoginByToken, token)
slData = if (ar is AuthenticationResult.Success) {
val data: D? = ar.applicationData?.let { BossDecoder.decodeFrom(dataType, it) }
SuperloginData(ar.loginToken, data)
@ -170,11 +170,14 @@ class SuperloginClient<D, S : WithAdapter>(
return rn.registerWithData(loginName, password, extraData = BossEncoder.encode(dataType, data))
.also { rr ->
if (rr is Registration.Result.Success) {
slData = SuperloginData(rr.loginToken, rr.encodedData?.let { BossDecoder.decodeFrom(dataType, it) })
slData = SuperloginData(rr.loginToken, extractData(rr.encodedData))
}
}
}
private fun extractData(rr: ByteArray?): D?
= rr?.let { BossDecoder.decodeFrom(dataType, it) }
private fun mustBeLoggedOut() {
if (isLoggedIn)
throw IllegalStateException("please log out first")
@ -190,14 +193,24 @@ class SuperloginClient<D, S : WithAdapter>(
slData = null
}
suspend fun LoginByToken(token: ByteArray): SuperloginData<D> {
/**
* Try to log in by specified token, returned by [Registration.Result.Success.loginToken] or
* [SuperloginData.loginToken] respectively.
*
* @return updated login data (and new token value) or null if token is not (or not anymore)
* available for logging in.
*/
suspend fun loginByToken(token: ByteArray): SuperloginData<D>? {
mustBeLoggedOut()
val r = invoke(api.slLoginByToken,token)
when(r) {
val r = invoke(serverApi.slLoginByToken,token)
return when(r) {
AuthenticationResult.LoginIdUnavailable -> TODO()
AuthenticationResult.LoginUnavailable -> TODO()
AuthenticationResult.LoginUnavailable -> null
AuthenticationResult.RestoreIdUnavailable -> TODO()
is AuthenticationResult.Success -> TODO()
is AuthenticationResult.Success -> SuperloginData(
r.loginToken,
extractData(r.applicationData)
)
}
}

View File

@ -23,5 +23,20 @@ interface SLServerTraits {
*/
suspend fun register(registrationArgs: RegistrationArgs): AuthenticationResult
/**
* Logging out procedure does not need any extra logic unless reuired by application
* server software. Default implementation does nothing.
*/
suspend fun logout() {}
/**
* Try to log in using an authentication token, which normally is returned in
* [AuthenticationResult.Success.loginToken]. If the server implementation
* does not support login token, don't implement it, use the default implementation.
*
* Otherwise, implement login by token and return either [AuthenticationResult.Success]
* or [AuthenticationResult.LoginUnavailable]. So not return anything else.
*/
suspend fun loginByToken(token: ByteArray): AuthenticationResult
= AuthenticationResult.LoginUnavailable
}

View File

@ -25,4 +25,7 @@ inline fun <reified D, T : SLServerSession<D>, H : CommandHost<T>> AdapterBuilde
loginName = null
traits.logout()
}
on(a2.slLoginByToken) { token ->
traits.loginByToken(token)
}
}

View File

@ -4,16 +4,15 @@ import io.ktor.server.engine.*
import io.ktor.server.netty.*
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable
import net.sergeych.parsec3.CommandHost
import net.sergeych.parsec3.Parsec3WSClient
import net.sergeych.parsec3.WithAdapter
import net.sergeych.parsec3.parsec3TransportServer
import net.sergeych.superlogin.AuthenticationResult
import net.sergeych.superlogin.RegistrationArgs
import net.sergeych.superlogin.SuperloginServerApi
import net.sergeych.superlogin.client.LoginState
import net.sergeych.superlogin.client.Registration
import net.sergeych.superlogin.client.SuperloginClient
import net.sergeych.superlogin.server.SLServerApiBase
import net.sergeych.superlogin.server.SLServerSession
import net.sergeych.superlogin.server.SLServerTraits
import net.sergeych.superlogin.server.superloginServer
@ -22,11 +21,12 @@ import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlin.test.assertNotNull
data class TestSession(var buzz: String = "BuZZ") : SLServerSession<TestData>()
object TestApiServer : SLServerApiBase<TestData>() {
class TestApiServer<T : WithAdapter> : CommandHost<T>() {
val loginName by command<Unit, String?>()
}
@ -56,6 +56,12 @@ object TestServerTraits : SLServerTraits {
}
}
override suspend fun loginByToken(token: ByteArray): AuthenticationResult {
return byToken[token.toList()]?.let {
AuthenticationResult.Success(token, it.extraData)
} ?: AuthenticationResult.LoginUnavailable
}
}
@Serializable
@ -63,6 +69,16 @@ data class TestData(
val foo: String,
)
//fun <S: SLServerSession<*>,A: CommandHost<S>> Application.superloginServer(
// traits: SLServerTraits,
// api: A,
// f: AdapterBuilder<S,A>.()->Unit) {
// parsec3TransportServer(api) {
// superloginServer(traits)
// f()
// }
//}
internal class WsServerKtTest {
@ -71,7 +87,8 @@ internal class WsServerKtTest {
fun testWsServer() {
embeddedServer(Netty, port = 8080) {
parsec3TransportServer(TestApiServer) {
parsec3TransportServer(TestApiServer<SLServerSession<TestData>>()) {
// superloginServer(TestServerTraits,TestApiServer<SLServerSession<TestData>>()) {
newSession { TestSession() }
superloginServer(TestServerTraits)
on(api.loginName) {
@ -80,11 +97,10 @@ internal class WsServerKtTest {
}
}.start(wait = false)
val client = Parsec3WSClient("ws://localhost:8080/api/p3", SuperloginServerApi<WithAdapter>()) {
}
val client = Parsec3WSClient("ws://localhost:8080/api/p3")
runBlocking {
val api = TestApiServer<WithAdapter>()
val slc = SuperloginClient<TestData, WithAdapter>(client)
assertEquals(LoginState.LoggedOut, slc.state.value)
var rt = slc.register("foo", "passwd", TestData("bar!"))
@ -94,7 +110,7 @@ internal class WsServerKtTest {
println(rt.secret)
assertEquals("bar!", rt.data<TestData>()?.foo)
assertEquals("foo", slc.call(TestApiServer.loginName))
assertEquals("foo", slc.call(api.loginName))
val s = slc.state.value
assertIs<LoginState.LoggedIn<TestData>>(s)
@ -105,12 +121,15 @@ internal class WsServerKtTest {
}
slc.logout()
assertIs<LoginState.LoggedOut>(slc.state.value)
assertEquals(null, slc.call(TestApiServer.loginName))
assertEquals(null, slc.call(api.loginName))
rt = slc.register("foo", "passwd", TestData("nobar"))
assertIs<Registration.Result.InvalidLogin>(rt)
// slc.loginByToken()
var ar = slc.loginByToken(token)
assertNotNull(ar)
assertEquals("bar!", ar.data?.foo)
assertEquals("foo", slc.call(api.loginName))
}
}