even better ktor server integration

This commit is contained in:
Sergey Chernov 2022-12-09 11:51:00 +01:00
parent 5cd4f00b1d
commit 652950633f
4 changed files with 85 additions and 14 deletions

View File

@ -14,6 +14,12 @@ It also contains useful tools for this type of application:
- `AccessControlObject` to contain recoverable password-protected data with a backup `secret` word to restore access. - `AccessControlObject` to contain recoverable password-protected data with a backup `secret` word to restore access.
Will be published under MIT when become usable. ## Usage
Currently, complie it and publisj into mavenLocal, and use from there. Soon will be published to some public maven repo.
## License
Will be published under MIT as soon as first RC will be published.

View File

@ -60,7 +60,11 @@ kotlin {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
} }
} }
val jvmMain by getting val jvmMain by getting {
dependencies {
implementation("io.ktor:ktor-server-core:$ktor_version")
}
}
val jvmTest by getting { val jvmTest by getting {
dependencies { dependencies {
implementation("io.ktor:ktor-server-core:$ktor_version") implementation("io.ktor:ktor-server-core:$ktor_version")

View File

@ -1,8 +1,10 @@
package net.sergeych.superlogin.server package net.sergeych.superlogin.server
import io.ktor.server.application.*
import net.sergeych.parsec3.AdapterBuilder import net.sergeych.parsec3.AdapterBuilder
import net.sergeych.parsec3.CommandHost import net.sergeych.parsec3.CommandHost
import net.sergeych.parsec3.WithAdapter import net.sergeych.parsec3.WithAdapter
import net.sergeych.parsec3.parsec3TransportServer
import net.sergeych.superlogin.* import net.sergeych.superlogin.*
import net.sergeych.unikrypto.SignedRecord import net.sergeych.unikrypto.SignedRecord
import kotlin.random.Random import kotlin.random.Random
@ -12,9 +14,61 @@ fun randomACOLike(): ByteArray {
} }
/** /**
* Create a superlogin server on a curretn adapter builder, using some session builder lambda. * Create superlogin server using default parsec 3 server transport providing session creation
* lambda and a block to set up server-specific api. The session must inherit [SLServerSession]
* with application-specific data, and implement all of its abstract methods at list (also it
* might need t override some default methods, see docs. For example:
* ~~~
*
* // Application-specific API declaration
* class TestApiServer<T : WithAdapter> : CommandHost<T>() {
* val foobar by command<Unit, String?>()
* }
*
* // application data to be included in the "user" data transfered to and from the client:
* data class TestData(clientData: String)
*
* // session state, is kept until the user connection (e.g. session) is closed, in the RAM:
* class TestSession(var sessionData: String) : SLServerSession<TestData>()
*
* // Create a server in a ktor context:
* fun Application.serverModule() {
* SuperloginServer(TestApiServer<TestSession>(), { TestSession("foobar") }) {
* // application API implementation
* on(api.foobar) {
* "Session data: ${sessionData}, loginName: $currentLoginName user data: $superloginData"
* }
* }
* }
* ~~~
*
* @param api service specific API declaration (usually in the shared code)
* @param sessionBuilder function that creates new session. Session is created on every incoming
* connection so try not to do any time and resource consuming operations there
* @param adapterBuilder block that should implement service-specific api.
* @param D application specific data (included in the session and transferred with registration
* and login)
* @param T service-specific session, class implementing any session-based code specific to the
* application but also implement necessary `superlogin` methods (implementing abstract
* ones) that implement user login data persistence and search.
*/
inline fun <reified D, T : SLServerSession<D>,> Application.SuperloginServer(
api: CommandHost<T>,
crossinline sessionBuilder: suspend AdapterBuilder<T, CommandHost<T>>.()->T,
crossinline adapterBuilder: AdapterBuilder<T, CommandHost<T>>.()->Unit
) {
parsec3TransportServer(api) {
setupSuperloginServer { sessionBuilder() }
adapterBuilder()
}
}
/**
* Set up a superlogin server manually, on a current adapter builder, using some session builder lambda.
* Note that session must inherit [SLServerSession] and implement necessary abstract methods * Note that session must inherit [SLServerSession] and implement necessary abstract methods
* that do store and retrieve user data. It sould be used like: * that do store and retrieve user data. Usually you can do it easier with [SuperloginServer] call.
* If you want to do it manually (fr example using a custom transport), do it like this:
* ``` * ```
* parsec3TransportServer(TestApiServer<TestSession>()) { * parsec3TransportServer(TestApiServer<TestSession>()) {
* // the session extends SLServerSession and contain app-specific data and * // the session extends SLServerSession and contain app-specific data and
@ -31,8 +85,8 @@ fun randomACOLike(): ByteArray {
* *
* @param sessionBuilder code that builds a new session over the adapter. * @param sessionBuilder code that builds a new session over the adapter.
*/ */
inline fun <reified D, T : SLServerSession<D>, H : CommandHost<T>> AdapterBuilder<T, H>.superloginServer( inline fun <reified D, T : SLServerSession<D>, H : CommandHost<T>> AdapterBuilder<T, H>.setupSuperloginServer(
crossinline sessionBuilder: suspend AdapterBuilder<T,H>.()->T crossinline sessionBuilder: suspend AdapterBuilder<T, H>.() -> T,
) { ) {
newSession { sessionBuilder() } newSession { sessionBuilder() }
addErrors(SuperloginExceptionsRegistry) addErrors(SuperloginExceptionsRegistry)

View File

@ -5,16 +5,13 @@ import io.ktor.server.engine.*
import io.ktor.server.netty.* import io.ktor.server.netty.*
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import net.sergeych.parsec3.CommandHost import net.sergeych.parsec3.*
import net.sergeych.parsec3.Parsec3WSClient
import net.sergeych.parsec3.WithAdapter
import net.sergeych.parsec3.parsec3TransportServer
import net.sergeych.superlogin.* import net.sergeych.superlogin.*
import net.sergeych.superlogin.client.LoginState import net.sergeych.superlogin.client.LoginState
import net.sergeych.superlogin.client.Registration import net.sergeych.superlogin.client.Registration
import net.sergeych.superlogin.client.SuperloginClient import net.sergeych.superlogin.client.SuperloginClient
import net.sergeych.superlogin.server.SLServerSession import net.sergeych.superlogin.server.SLServerSession
import net.sergeych.superlogin.server.superloginServer import net.sergeych.superlogin.server.setupSuperloginServer
import net.sergeych.unikrypto.PublicKey import net.sergeych.unikrypto.PublicKey
import superlogin.assertThrowsAsync import superlogin.assertThrowsAsync
import kotlin.random.Random import kotlin.random.Random
@ -193,6 +190,7 @@ internal class WsServerKtTest {
assertTrue { slc.isLoggedIn } assertTrue { slc.isLoggedIn }
assertEquals("foo", slc.call(api.loginName)) assertEquals("foo", slc.call(api.loginName))
} }
} }
@ -212,10 +210,19 @@ internal class WsServerKtTest {
} }
inline fun <reified D, T : SLServerSession<D>, H : CommandHost<T>> Application.SuperloginServer(
api: H,
crossinline sessionBuilder: suspend AdapterBuilder<T, H>.()->T,
crossinline adapterBuilder: AdapterBuilder<T, H>.()->Unit
) {
parsec3TransportServer(api) {
setupSuperloginServer { sessionBuilder() }
adapterBuilder()
}
}
fun Application.testServerModule() { fun Application.testServerModule() {
parsec3TransportServer(TestApiServer<TestSession>()) { SuperloginServer(TestApiServer<TestSession>(), { TestSession() }) {
// superloginServer(TestServerTraits,TestApiServer<SLServerSession<TestData>>()) {
superloginServer { TestSession() }
on(api.loginName) { on(api.loginName) {
println("login name called. now we have $currentLoginName : $superloginData") println("login name called. now we have $currentLoginName : $superloginData")
currentLoginName currentLoginName