diff --git a/build.gradle.kts b/build.gradle.kts index df0f0b533..8d13add8b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,6 +15,7 @@ plugins { val kotlinVersion = "2.0.20" kotlin("multiplatform") version kotlinVersion apply false kotlin("jvm") version kotlinVersion + kotlin("plugin.power-assert") version kotlinVersion apply false kotlin("plugin.compose") version kotlinVersion apply false diff --git a/waltid-libraries/auth/waltid-ktor-authnz/build.gradle.kts b/waltid-libraries/auth/waltid-ktor-authnz/build.gradle.kts index f15b1572a..a4cadeeb1 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/build.gradle.kts +++ b/waltid-libraries/auth/waltid-ktor-authnz/build.gradle.kts @@ -1,5 +1,8 @@ +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi + plugins { kotlin("jvm") + kotlin("plugin.power-assert") kotlin("plugin.serialization") id("io.ktor.plugin") version "2.3.12" id("maven-publish") @@ -130,3 +133,15 @@ publishing { } } } + +@OptIn(ExperimentalKotlinGradlePluginApi::class) +powerAssert { + includedSourceSets = listOf("test") + functions = listOf( + // kotlin.test + "kotlin.assert", "kotlin.test.assertTrue", "kotlin.test.assertEquals", "kotlin.test.assertNull", + + // checks + "kotlin.require", "kotlin.check" + ) +} diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/AccountIdentifierRegistry.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/AccountIdentifierManager.kt similarity index 96% rename from waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/AccountIdentifierRegistry.kt rename to waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/AccountIdentifierManager.kt index e724bfbf2..6f8a057f1 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/AccountIdentifierRegistry.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/AccountIdentifierManager.kt @@ -2,7 +2,7 @@ package id.walt.ktorauthnz.accounts.identifiers import id.walt.ktorauthnz.accounts.identifiers.methods.* -object AccountIdentifierRegistry { +object AccountIdentifierManager { private val defaultIdentifiers = listOf(EmailIdentifier, JWTIdentifier, LDAPIdentifier, OIDCIdentifier, RADIUSIdentifier, UsernameIdentifier) diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/AccountIdentifier.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/AccountIdentifier.kt index 7548c1ada..3b635b147 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/AccountIdentifier.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/AccountIdentifier.kt @@ -5,14 +5,19 @@ import kotlinx.serialization.Serializable @Suppress("EqualsOrHashCode") @Serializable -abstract class AccountIdentifier(val identifierName: String) { +sealed class AccountIdentifier { - override fun toString(): String = "[$identifierName: ${toDataString()}]" + internal abstract fun identifierName(): String + + val accountIdentifierName + get() = identifierName() + + override fun toString(): String = "[$accountIdentifierName: ${toDataString()}]" abstract fun toDataString(): String override fun equals(other: Any?): Boolean { if (other !is AccountIdentifier) return false - if (other.identifierName != identifierName) return false + if (other.accountIdentifierName != accountIdentifierName) return false if (other.hashCode() != hashCode()) return false return true diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/EmailIdentifier.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/EmailIdentifier.kt index 860f9477c..1b2670c11 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/EmailIdentifier.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/EmailIdentifier.kt @@ -1,12 +1,18 @@ package id.walt.ktorauthnz.accounts.identifiers.methods +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class EmailIdentifier(val email: String) : AccountIdentifier("email") { +@SerialName("email") +data class EmailIdentifier(val email: String) : AccountIdentifier() { + override fun identifierName() = "email" + override fun toDataString() = email companion object : AccountIdentifierFactory("email") { override fun fromAccountIdentifierDataString(dataString: String): EmailIdentifier = EmailIdentifier(dataString) } + + } diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/JWTIdentifier.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/JWTIdentifier.kt index e2bc0ff1b..fa3b94981 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/JWTIdentifier.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/JWTIdentifier.kt @@ -1,9 +1,12 @@ package id.walt.ktorauthnz.accounts.identifiers.methods +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class JWTIdentifier(val subject: String) : AccountIdentifier("jwt") { +@SerialName("jwt") +data class JWTIdentifier(val subject: String) : AccountIdentifier() { + override fun identifierName() = "jwt" override fun toDataString() = subject companion object : AccountIdentifierFactory("jwt") { diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/LDAPIdentifier.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/LDAPIdentifier.kt index 903d5cfb2..279a643ad 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/LDAPIdentifier.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/LDAPIdentifier.kt @@ -1,11 +1,14 @@ package id.walt.ktorauthnz.accounts.identifiers.methods +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @Serializable -data class LDAPIdentifier(val host: String, val name: String) : AccountIdentifier("ldap") { +@SerialName("ldap") +data class LDAPIdentifier(val host: String, val name: String) : AccountIdentifier() { + override fun identifierName() = "ldap" override fun toDataString() = Json.encodeToString(this) companion object : AccountIdentifierFactory("ldap") { diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/OIDCIdentifier.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/OIDCIdentifier.kt index 0b0cb9fa7..e9a91b883 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/OIDCIdentifier.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/OIDCIdentifier.kt @@ -1,9 +1,14 @@ package id.walt.ktorauthnz.accounts.identifiers.methods +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -data class OIDCIdentifier(val host: String, val name: String) : AccountIdentifier("oidc") { +@Serializable +@SerialName("oidc") +data class OIDCIdentifier(val host: String, val name: String) : AccountIdentifier() { + override fun identifierName() = "oidc" override fun toDataString() = Json.encodeToString(this) companion object : AccountIdentifierFactory("oidc") { diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/RADIUSIdentifier.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/RADIUSIdentifier.kt index b91fa0497..077618771 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/RADIUSIdentifier.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/RADIUSIdentifier.kt @@ -1,11 +1,14 @@ package id.walt.ktorauthnz.accounts.identifiers.methods +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @Serializable -data class RADIUSIdentifier(val host: String, val name: String) : AccountIdentifier("radius") { +@SerialName("radius") +data class RADIUSIdentifier(val host: String, val name: String) : AccountIdentifier() { + override fun identifierName() = "radius" override fun toDataString() = Json.encodeToString(this) companion object : AccountIdentifierFactory("radius") { diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/UsernameIdentifier.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/UsernameIdentifier.kt index 7c0fc55dd..f541d5b46 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/UsernameIdentifier.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/accounts/identifiers/methods/UsernameIdentifier.kt @@ -1,12 +1,15 @@ package id.walt.ktorauthnz.accounts.identifiers.methods +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class UsernameIdentifier(val name: String) : AccountIdentifier("username") { +@SerialName("userpass") +data class UsernameIdentifier(val name: String) : AccountIdentifier() { + override fun identifierName() = "userpass" override fun toDataString() = name - companion object : AccountIdentifierFactory("username") { + companion object : AccountIdentifierFactory("userpass") { override fun fromAccountIdentifierDataString(dataString: String) = UsernameIdentifier(dataString) } } diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/AuthenticationMethod.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/AuthenticationMethod.kt index b0dc998e8..729433ecc 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/AuthenticationMethod.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/AuthenticationMethod.kt @@ -3,6 +3,7 @@ package id.walt.ktorauthnz.methods import id.walt.ktorauthnz.AuthContext import id.walt.ktorauthnz.KtorAuthnzManager import id.walt.ktorauthnz.accounts.identifiers.methods.AccountIdentifier +import id.walt.ktorauthnz.methods.config.AuthMethodConfiguration import id.walt.ktorauthnz.methods.data.AuthMethodStoredData import id.walt.ktorauthnz.sessions.AuthSession import id.walt.ktorauthnz.sessions.AuthSessionStatus @@ -12,6 +13,22 @@ import io.ktor.server.application.* import io.ktor.server.response.* import io.ktor.server.routing.* import io.ktor.util.pipeline.* +import kotlinx.serialization.EncodeDefault +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.Serializable +import kotlin.reflect.KClass + +@OptIn(ExperimentalSerializationApi::class) +@Serializable +sealed interface MethodInstance { + @EncodeDefault(EncodeDefault.Mode.NEVER) + val data: AuthMethodStoredData? + + @EncodeDefault(EncodeDefault.Mode.NEVER) + val config: AuthMethodConfiguration? + + fun authMethod(): AuthenticationMethod +} abstract class AuthenticationMethod(open val id: String) { abstract fun Route.register(authContext: PipelineContext.() -> AuthContext) @@ -54,6 +71,9 @@ abstract class AuthenticationMethod(open val id: String) { return session } + + open val relatedAuthMethodStoredData: KClass? = null + open val relatedAuthMethodConfiguration: KClass? = null } diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/EmailPass.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/EmailPass.kt index 43f11a2b7..a273954d3 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/EmailPass.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/EmailPass.kt @@ -1,9 +1,12 @@ package id.walt.ktorauthnz.methods import id.walt.ktorauthnz.AuthContext +import id.walt.ktorauthnz.accounts.identifiers.AccountIdentifierManager import id.walt.ktorauthnz.accounts.identifiers.methods.AccountIdentifier import id.walt.ktorauthnz.accounts.identifiers.methods.EmailIdentifier import id.walt.ktorauthnz.exceptions.authCheck +import id.walt.ktorauthnz.methods.config.AuthMethodConfiguration +import id.walt.ktorauthnz.methods.data.AuthMethodStoredData import id.walt.ktorauthnz.methods.data.EmailPassStoredData import id.walt.ktorauthnz.sessions.AuthSession import id.walt.ktorauthnz.sessions.AuthSessionInformation @@ -13,10 +16,28 @@ import io.ktor.server.application.* import io.ktor.server.auth.* import io.ktor.server.routing.* import io.ktor.util.pipeline.* +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import org.intellij.lang.annotations.Language + +@Serializable +@SerialName("email") +data class EmailPassMethodInstance( + override val data: EmailPassStoredData, +) : MethodInstance { + override val config = null + + override fun authMethod() = EmailPass +} object EmailPass : UserPassBasedAuthMethod("email", usernameName = "email") { + override val relatedAuthMethodStoredData = EmailPassStoredData::class + override suspend fun auth(session: AuthSession, credential: UserPasswordCredential, context: ApplicationCall): AccountIdentifier { val identifier = EmailIdentifier(credential.name) @@ -44,5 +65,4 @@ object EmailPass : UserPassBasedAuthMethod("email", usernameName = "email") { context.handleAuthSuccess(session, identifier.resolveToAccountId()) } } - } diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/TOTP.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/TOTP.kt index 75c6b42a6..a09c5cb26 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/TOTP.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/TOTP.kt @@ -21,6 +21,7 @@ import kotlinx.serialization.Serializable object TOTP : AuthenticationMethod("totp") { + override val relatedAuthMethodStoredData = TOTPStoredData::class fun auth(session: AuthSession, code: String) { val storedData = lookupStoredMultiData(session /* context() */) diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/UserPass.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/UserPass.kt index b4727634d..28a2c1320 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/UserPass.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/UserPass.kt @@ -20,6 +20,7 @@ data class UserPassCredentials(val username: String, val password: String) object UserPass : UserPassBasedAuthMethod("userpass") { + override val relatedAuthMethodStoredData = UserPassStoredData::class override suspend fun auth(session: AuthSession, credential: UserPasswordCredential, context: ApplicationCall): AccountIdentifier { val identifier = UsernameIdentifier(credential.name) diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/AuthMethodConfiguration.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/AuthMethodConfiguration.kt index 67e044fe1..49cbf5cc2 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/AuthMethodConfiguration.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/AuthMethodConfiguration.kt @@ -1,6 +1,9 @@ package id.walt.ktorauthnz.methods.config +import id.walt.ktorauthnz.methods.AuthenticationMethod import kotlinx.serialization.Serializable @Serializable -sealed interface AuthMethodConfiguration +sealed interface AuthMethodConfiguration { + fun authMethod(): AuthenticationMethod +} diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/JwtAuthConfiguration.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/JwtAuthConfiguration.kt index fd26a6cb9..456d7ed25 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/JwtAuthConfiguration.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/JwtAuthConfiguration.kt @@ -1,5 +1,6 @@ package id.walt.ktorauthnz.methods.config +import id.walt.ktorauthnz.methods.JWT import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -8,4 +9,6 @@ import kotlinx.serialization.Serializable data class JwtAuthConfiguration( val verifyKey: String, val identifyClaim: String = "sub", -) : AuthMethodConfiguration +) : AuthMethodConfiguration { + override fun authMethod() = JWT +} diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/LDAPConfiguration.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/LDAPConfiguration.kt index a0ff888da..d5059164e 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/LDAPConfiguration.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/LDAPConfiguration.kt @@ -1,5 +1,6 @@ package id.walt.ktorauthnz.methods.config +import id.walt.ktorauthnz.methods.LDAP import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -8,4 +9,6 @@ import kotlinx.serialization.Serializable data class LDAPConfiguration( val ldapServerUrl: String, val userDNFormat: String, -) : AuthMethodConfiguration +) : AuthMethodConfiguration { + override fun authMethod() = LDAP +} diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/OidcAuthConfiguration.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/OidcAuthConfiguration.kt index 09686e359..9d5431ee9 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/OidcAuthConfiguration.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/OidcAuthConfiguration.kt @@ -33,4 +33,6 @@ data class OidcAuthConfiguration( init { runBlocking { init() } } + + override fun authMethod() = OIDC } diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/RADIUSConfiguration.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/RADIUSConfiguration.kt index 090209486..4b791d2b3 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/RADIUSConfiguration.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/RADIUSConfiguration.kt @@ -1,5 +1,6 @@ package id.walt.ktorauthnz.methods.config +import id.walt.ktorauthnz.methods.RADIUS import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -10,4 +11,6 @@ data class RADIUSConfiguration( val radiusServerPort: Int, val radiusServerSecret: String, val radiusNasIdentifier: String, -) : AuthMethodConfiguration +) : AuthMethodConfiguration { + override fun authMethod() = RADIUS +} diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/VerifiableCredentialAuthConfiguration.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/VerifiableCredentialAuthConfiguration.kt index 596a4e50e..66dfd1373 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/VerifiableCredentialAuthConfiguration.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/config/VerifiableCredentialAuthConfiguration.kt @@ -1,5 +1,6 @@ package id.walt.ktorauthnz.methods.config +import id.walt.ktorauthnz.methods.VerifiableCredential import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonElement @@ -10,4 +11,6 @@ data class VerifiableCredentialAuthConfiguration( val verification: Map, //val claimMappings: Map? = null, //val redirectUrl: String? = null, -) : AuthMethodConfiguration +) : AuthMethodConfiguration { + override fun authMethod() = VerifiableCredential +} diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/AuthMethodStoredData.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/AuthMethodStoredData.kt index 6befde567..7a6b11e47 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/AuthMethodStoredData.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/AuthMethodStoredData.kt @@ -1,6 +1,9 @@ package id.walt.ktorauthnz.methods.data +import id.walt.ktorauthnz.methods.AuthenticationMethod import kotlinx.serialization.Serializable @Serializable -sealed interface AuthMethodStoredData +sealed interface AuthMethodStoredData { + fun authMethod(): AuthenticationMethod +} diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/EmailPassStoredData.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/EmailPassStoredData.kt index 7523aa5a1..bc42433d5 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/EmailPassStoredData.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/EmailPassStoredData.kt @@ -1,10 +1,13 @@ package id.walt.ktorauthnz.methods.data +import id.walt.ktorauthnz.methods.EmailPass import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -@SerialName("emailpass-data") +@SerialName("email") data class EmailPassStoredData( val password: String, -) : AuthMethodStoredData +) : AuthMethodStoredData { + override fun authMethod() = EmailPass +} diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/FlowAmendmentData.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/FlowAmendmentData.kt index 9fa6026f5..3ed8850a4 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/FlowAmendmentData.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/FlowAmendmentData.kt @@ -1,12 +1,15 @@ package id.walt.ktorauthnz.methods.data import id.walt.ktorauthnz.flows.AuthFlow +import id.walt.ktorauthnz.methods.virtual.GlobalIdentify import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -@SerialName("flowamendment-data") +@SerialName("flowamendment") data class FlowAmendmentData( val appendFlow: Set? = null, var replaceFlow: Set? = null, -) : AuthMethodStoredData +) : AuthMethodStoredData { + override fun authMethod() = GlobalIdentify +} diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/TOTPStoredData.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/TOTPStoredData.kt index c67196d70..4f818d26b 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/TOTPStoredData.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/TOTPStoredData.kt @@ -1,10 +1,13 @@ package id.walt.ktorauthnz.methods.data +import id.walt.ktorauthnz.methods.TOTP import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -@SerialName("totp-data") +@SerialName("totp") data class TOTPStoredData( val secret: String, -) : AuthMethodStoredData +) : AuthMethodStoredData { + override fun authMethod() = TOTP +} diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/UserPassStoredData.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/UserPassStoredData.kt index 65d8980ce..5ca12164b 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/UserPassStoredData.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/data/UserPassStoredData.kt @@ -1,10 +1,13 @@ package id.walt.ktorauthnz.methods.data +import id.walt.ktorauthnz.methods.UserPass import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -@SerialName("userpass-data") +@SerialName("userpass") data class UserPassStoredData( val password: String, -) : AuthMethodStoredData +) : AuthMethodStoredData { + override fun authMethod() = UserPass +} diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/initalauth/AuthMethodRegistration.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/initalauth/AuthMethodRegistration.kt new file mode 100644 index 000000000..f83774d45 --- /dev/null +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/initalauth/AuthMethodRegistration.kt @@ -0,0 +1,50 @@ +package id.walt.ktorauthnz.methods.initalauth + +import id.walt.ktorauthnz.accounts.identifiers.methods.AccountIdentifier +import id.walt.ktorauthnz.methods.config.AuthMethodConfiguration +import id.walt.ktorauthnz.methods.data.AuthMethodStoredData +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.* + +@Serializable +data class AuthMethodRegistration( + val type: String, + val identifier: AccountIdentifier, + val data: AuthMethodStoredData? = null, + val config: AuthMethodConfiguration? = null, +) { + fun getAuthMethod() = + data?.authMethod() ?: config?.authMethod() ?: error("Neither data nor config was provided for AuthMethodRegistration") +} + +@Serializable +data class AuthMethodRegistrationWrapper( + val type: String, + var identifier: JsonObject, // AccountIdentifier + + var data: JsonObject? = null, // AuthMethodStoredData + var config: JsonObject? = null, // AuthMethodConfiguration +) { + + companion object { + fun parseWrapperFromJsonRequest(registrationJson: String) = + Json.decodeFromString(registrationJson) + } + + /** Transform wrapper into actual data class */ + fun transformWrapperToRegistration(): AuthMethodRegistration { + identifier = setInitialAuthJsonObjectType(identifier, type) + + if (data != null) { + data = setInitialAuthJsonObjectType(data!!, type) + } + if (config != null) { + data = setInitialAuthJsonObjectType(config!!, type) + } + return Json.decodeFromJsonElement(Json.encodeToJsonElement(this)) + } + +} + +fun setInitialAuthJsonObjectType(jsonObject: JsonObject, type: String): JsonObject = + JsonObject(jsonObject.toMutableMap().apply { set("type", JsonPrimitive(type)) }) diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/virtual/GlobalIdentify.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/virtual/GlobalIdentify.kt index ba76aa08c..54e8d0172 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/virtual/GlobalIdentify.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/main/kotlin/id/walt/ktorauthnz/methods/virtual/GlobalIdentify.kt @@ -1,12 +1,9 @@ package id.walt.ktorauthnz.methods.virtual import id.walt.ktorauthnz.AuthContext -import id.walt.ktorauthnz.flows.AuthFlow -import id.walt.ktorauthnz.methods.data.AuthMethodStoredData import io.ktor.server.application.* import io.ktor.server.routing.* import io.ktor.util.pipeline.* -import kotlinx.serialization.Serializable object GlobalIdentify : IdentifyVirtualAuth("identify-global") { diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/test/kotlin/id/walt/AccountIdentifierRegistryTest.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/test/kotlin/id/walt/AccountIdentifierManagerTest.kt similarity index 77% rename from waltid-libraries/auth/waltid-ktor-authnz/src/test/kotlin/id/walt/AccountIdentifierRegistryTest.kt rename to waltid-libraries/auth/waltid-ktor-authnz/src/test/kotlin/id/walt/AccountIdentifierManagerTest.kt index 1cb7100a0..00c6dc9d8 100644 --- a/waltid-libraries/auth/waltid-ktor-authnz/src/test/kotlin/id/walt/AccountIdentifierRegistryTest.kt +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/test/kotlin/id/walt/AccountIdentifierManagerTest.kt @@ -1,13 +1,13 @@ package id.walt -import id.walt.ktorauthnz.accounts.identifiers.AccountIdentifierRegistry +import id.walt.ktorauthnz.accounts.identifiers.AccountIdentifierManager import id.walt.ktorauthnz.accounts.identifiers.methods.EmailIdentifier import id.walt.ktorauthnz.accounts.identifiers.methods.JWTIdentifier import id.walt.ktorauthnz.accounts.identifiers.methods.RADIUSIdentifier import id.walt.ktorauthnz.accounts.identifiers.methods.UsernameIdentifier import kotlin.test.Test -class AccountIdentifierRegistryTest { +class AccountIdentifierManagerTest { @Test fun testIdentifierRegistry() { @@ -16,8 +16,10 @@ class AccountIdentifierRegistryTest { JWTIdentifier("subject1"), RADIUSIdentifier("example.host", "username1"), UsernameIdentifier("alice1") - ).associateWith { it.identifierName to it.toDataString() } - .map { (k, v) -> k to AccountIdentifierRegistry.getAccountIdentifier(v.first, v.second) } + ).associateWith { it.accountIdentifierName to it.toDataString() } + .map { (k, v) -> + k to AccountIdentifierManager.getAccountIdentifier(v.first, v.second) + } .forEach { println("${it.first} == ${it.second}") check(it.first == it.second) { "${it.first} does not match ${it.second}" } diff --git a/waltid-libraries/auth/waltid-ktor-authnz/src/test/kotlin/id/walt/InitialAuthRegistrationTest.kt b/waltid-libraries/auth/waltid-ktor-authnz/src/test/kotlin/id/walt/InitialAuthRegistrationTest.kt new file mode 100644 index 000000000..fac994984 --- /dev/null +++ b/waltid-libraries/auth/waltid-ktor-authnz/src/test/kotlin/id/walt/InitialAuthRegistrationTest.kt @@ -0,0 +1,99 @@ +package id.walt + +import id.walt.ktorauthnz.accounts.identifiers.AccountIdentifierManager +import id.walt.ktorauthnz.accounts.identifiers.methods.AccountIdentifier +import id.walt.ktorauthnz.methods.data.AuthMethodStoredData +import id.walt.ktorauthnz.methods.initalauth.AuthMethodRegistrationWrapper +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.* +import org.intellij.lang.annotations.Language +import kotlin.test.Test + +class InitialAuthRegistrationTest { + + @Language("JSON") + val myAuthMethodRegistration = """ + { + "type": "email", + "identifier": { + "email": "user@email.com" + }, + "data": { + "password": "pass1234" + } + } + """.trimIndent().let { Json.parseToJsonElement(it).jsonObject } + + @Test + fun test() { + // Our HTTP request + val accountId = "account110011" + + val _email = myAuthMethodRegistration["identifier"]!!.jsonObject["email"]!!.jsonPrimitive.content + + println("HTTP request: $myAuthMethodRegistration") + val wrapper = Json.decodeFromJsonElement(myAuthMethodRegistration) + println("In wrapper: $wrapper") + + val registration = wrapper.transformWrapperToRegistration() + println("Actual registration data: $registration") + check(_email in registration.toString()) + + // Data class is ready + + val identifier = registration.identifier + check(_email in identifier.toString()) + + //val dbAccountIdentifier = (identifier.accountIdentifierName to identifier.toDataString()) + val dbAccountIdentifier = AccountIdentifierManager.getAccountIdentifier(identifier.accountIdentifierName, identifier.toDataString()) + check(identifier == dbAccountIdentifier) + + val accountIdentifierAccountMapping = hashMapOf(dbAccountIdentifier to accountId) + println("(Map account identifier type=${identifier.accountIdentifierName} data=${identifier.toDataString()} to account $accountId)") + + val authMethod = registration.getAuthMethod() + check(authMethod.id == myAuthMethodRegistration["type"]?.jsonPrimitive?.content) + + // usually there could be multiple, this is single one now + val accountIdentifierAuthMethodDataMapping = HashMap() + + if (registration.data != null) { + val registrationDataJson = Json.encodeToString(registration.data) + println("(Map auth method data $registrationDataJson to above mentioned account identifier, auth method is ${authMethod.id})") + accountIdentifierAuthMethodDataMapping[dbAccountIdentifier] = registrationDataJson + } + + // --- Login + val loginMethod = "email" + val loginId = "user@email.com" + val loginPassword = "pass1234" + + println(""" + -- Login -- + Login method: $loginMethod + Login id: $loginId + Login password: $loginPassword + """.trimIndent()) + + // Resolve account identifier + + // this is a bit simplified now, with the data string + val accountIdentifierForLogin = AccountIdentifierManager.getAccountIdentifier(loginMethod, loginId) + println("Resolved to account identifier: $accountIdentifierForLogin") + check(accountIdentifierForLogin == identifier) + check(accountIdentifierForLogin == dbAccountIdentifier) + + // Get auth method data for identifier + val accountForLogin = accountIdentifierAccountMapping[accountIdentifierForLogin] + println("Resolved to account for login: $accountForLogin") + check(accountForLogin == accountId) + + val dataForLogin = Json.decodeFromString(accountIdentifierAuthMethodDataMapping[accountIdentifierForLogin]!!) + println("Data for login is: $dataForLogin") + check(dataForLogin == registration.data) + + println("Method handling: Authenticate $loginPassword vs data $dataForLogin") + check(loginPassword in dataForLogin.toString()) + } + +} diff --git a/waltid-services/waltid-e2e-tests/build.gradle.kts b/waltid-services/waltid-e2e-tests/build.gradle.kts index f9dd9b4b5..8f7652be8 100644 --- a/waltid-services/waltid-e2e-tests/build.gradle.kts +++ b/waltid-services/waltid-e2e-tests/build.gradle.kts @@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi plugins { kotlin("jvm") - kotlin("plugin.power-assert") version "2.0.0" + kotlin("plugin.power-assert") } group = "id.walt" diff --git a/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/db/KotlinxUUIDTable.kt b/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/db/KotlinxUUIDTable.kt index f1f8af2c9..0af2d8067 100644 --- a/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/db/KotlinxUUIDTable.kt +++ b/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/db/KotlinxUUIDTable.kt @@ -28,8 +28,6 @@ import kotlin.uuid.toKotlinUuid // override val id: Column> = kotlinxUuid(columnName) // .autoGenerate() // .entityId() -// val x = uuid("y").autoGenerate().entityId() -// // // override val primaryKey: PrimaryKey by lazy { super.primaryKey ?: PrimaryKey(id) } //} diff --git a/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/db/models/X5CLogins.kt b/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/db/models/X5CLogins.kt index 2cf0838da..2b73e82a9 100644 --- a/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/db/models/X5CLogins.kt +++ b/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/db/models/X5CLogins.kt @@ -1,10 +1,13 @@ package id.walt.webwallet.db.models +import id.walt.webwallet.db.kotlinxUuid import org.jetbrains.exposed.sql.Table +import kotlin.uuid.ExperimentalUuidApi +@OptIn(ExperimentalUuidApi::class) object X5CLogins : Table("x5clogins") { val tenant = varchar("tenant", 128).default("") - val accountId = uuid("accountId") + val accountId = kotlinxUuid("accountId") val x5cId = varchar("x5cId", 256).uniqueIndex() diff --git a/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/service/account/x5c/X5CAccountStrategy.kt b/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/service/account/x5c/X5CAccountStrategy.kt index 03fd6f13b..6966dbd26 100644 --- a/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/service/account/x5c/X5CAccountStrategy.kt +++ b/waltid-services/waltid-wallet-api/src/main/kotlin/id/walt/webwallet/service/account/x5c/X5CAccountStrategy.kt @@ -86,7 +86,7 @@ object X5CAccountStrategy : PasswordlessAccountStrategy() { // add x5c logins record X5CLogins.insert { it[X5CLogins.tenant] = tenant - it[X5CLogins.accountId] = accountId.toJavaUuid() + it[X5CLogins.accountId] = accountId it[x5cId] = thumbprint } return accountId