diff --git a/server-core/src/main/kotlin/com/lightningkite/lightningserver/auth/proof/PasswordProofEndpoints.kt b/server-core/src/main/kotlin/com/lightningkite/lightningserver/auth/proof/PasswordProofEndpoints.kt index 3bd2d99c..65960a8c 100644 --- a/server-core/src/main/kotlin/com/lightningkite/lightningserver/auth/proof/PasswordProofEndpoints.kt +++ b/server-core/src/main/kotlin/com/lightningkite/lightningserver/auth/proof/PasswordProofEndpoints.kt @@ -35,7 +35,7 @@ class PasswordProofEndpoints( val database: () -> Database, val cache: () -> Cache, val proofHasher: () -> SecureHasher = secretBasis.hasher("proof"), - val evaluatePassword: (String) -> Unit = { } + val evaluatePassword: (String) -> Unit = { } ) : ServerPathGroup(path), Authentication.DirectProofMethod { init { path.docName = "PasswordProof" @@ -66,28 +66,36 @@ class PasswordProofEndpoints( Tasks.onSettingsReady { Authentication.subjects.forEach { @Suppress("UNCHECKED_CAST") - ModelRestEndpoints, PasswordSecret>, Comparable>(path("secrets/${it.value.name.lowercase()}"), modelInfo< HasId<*>, PasswordSecret>, Comparable>( - serialization = ModelSerializationInfo(PasswordSecret.serializer(it.value.idSerializer as KSerializer>), it.value.idSerializer as KSerializer>), - authOptions = Authentication.isAdmin as AuthOptions>, - getBaseCollection = { table(it.value) }, - getCollection = { c -> - c.withPermissions(ModelPermissions( - create = Condition.Always(), - read = Condition.Always(), - readMask = Mask( - listOf( - Condition.Never>>() to Modification.OnField( - PasswordSecret_hash(it.value.idSerializer as KSerializer>), - Modification.Assign("") - ) + ModelRestEndpoints, PasswordSecret>, Comparable>( + path("secrets/${it.value.name.lowercase()}"), + modelInfo, PasswordSecret>, Comparable>( + serialization = ModelSerializationInfo( + PasswordSecret.serializer(it.value.idSerializer as KSerializer>), + it.value.idSerializer as KSerializer> + ), + authOptions = Authentication.isAdmin as AuthOptions>, + getBaseCollection = { table(it.value) }, + getCollection = { c -> + c.withPermissions( + ModelPermissions( + create = Condition.Always(), + read = Condition.Always(), + readMask = Mask( + listOf( + Condition.Never>>() to Modification.OnField( + PasswordSecret_hash(it.value.idSerializer as KSerializer>), + Modification.Assign("") + ) + ) + ), + update = Condition.Always(), + delete = Condition.Always(), ) - ), - update = Condition.Always(), - delete = Condition.Always(), - )) - }, - modelName = "PasswordSecret For ${it.value.name}" - )) + ) + }, + modelName = "PasswordSecret For ${it.value.name}" + ) + ) } } } @@ -102,6 +110,53 @@ class PasswordProofEndpoints( return handler to Serialization.json.decodeUnwrappingString(handler.idSerializer, id.substringAfter('|')) } + val establishAdmin = + path("admin/establish").arg("subject", String.serializer()).arg("id", String.serializer()).post.api( + summary = "Set Other Password", + inputType = EstablishPassword.serializer(), + outputType = Unit.serializer(), + description = "Generates a new One Time Password configuration.", + authOptions = Authentication.isAdmin, + errorCases = listOf(), + examples = listOf(), + implementation = { value: EstablishPassword -> + val subject = Authentication.subjects.values.find { it.name == path1 } + ?: throw BadRequestException("No such subject; subjects are ${Authentication.subjects.values.joinToString { it.name }}") + @Suppress("UNCHECKED_CAST") + val id = Serialization.fromString(path2, subject.idSerializer) as Comparable> + @Suppress("UNCHECKED_CAST") + establish( + subject as Authentication.SubjectHandler>>, Comparable>>, + id, + value + ) + Unit + } + ) + + suspend fun , ID : Comparable> establish( + subject: Authentication.SubjectHandler, + id: ID, + password: EstablishPassword + ) { + val value = password + evaluatePassword(value.password) + if (value.hint?.contains( + value.password, + true + ) == true + ) throw BadRequestException("Hint cannot contain the password itself!") + @Suppress("UNCHECKED_CAST") + val secret = PasswordSecret( + _id = id as Comparable, + hash = value.password.secureHash(), + hint = value.hint, + ) + @Suppress("UNCHECKED_CAST") + table(subject).deleteOneById(id as Comparable) + table(subject).insertOne(secret) + } + val establish = path("establish").post.api( summary = "Establish a Password", inputType = EstablishPassword.serializer(), @@ -111,17 +166,12 @@ class PasswordProofEndpoints( errorCases = listOf(), examples = listOf(), implementation = { value: EstablishPassword -> - evaluatePassword(value.password) - if(value.hint?.contains(value.password, true) == true) throw BadRequestException("Hint cannot contain the password itself!") @Suppress("UNCHECKED_CAST") - val secret = PasswordSecret( - _id = auth.rawId as Comparable, - hash = value.password.secureHash(), - hint = value.hint, + establish( + auth.subject as Authentication.SubjectHandler>>, Comparable>>, + auth.rawId as Comparable>, + value ) - @Suppress("UNCHECKED_CAST") - table(auth.subject).deleteOneById(auth.rawId as Comparable) - table(auth.subject).insertOne(secret) Unit } ) @@ -216,6 +266,7 @@ class PasswordProofEndpoints( @Suppress("UNCHECKED_CAST") return table(handler).get(item._id as Comparable) != null } + suspend fun > established(handler: Authentication.SubjectHandler<*, ID>, id: ID): Boolean { @Suppress("UNCHECKED_CAST") return table(handler).get(id as Comparable) != null diff --git a/server-core/src/main/kotlin/com/lightningkite/lightningserver/jsonschema/JsonSchemaBuilder.kt b/server-core/src/main/kotlin/com/lightningkite/lightningserver/jsonschema/JsonSchemaBuilder.kt index cc5dc573..87a9e592 100644 --- a/server-core/src/main/kotlin/com/lightningkite/lightningserver/jsonschema/JsonSchemaBuilder.kt +++ b/server-core/src/main/kotlin/com/lightningkite/lightningserver/jsonschema/JsonSchemaBuilder.kt @@ -490,8 +490,8 @@ class JsonSchemaBuilder( properties = mapOf() ).applyAnnotations(annos) - PolymorphicKind.SEALED -> TODO() - PolymorphicKind.OPEN -> TODO() + PolymorphicKind.SEALED -> throw Error("Cannot generate JSON Schema for polymorphic type ${ser.descriptor.serialName}") + PolymorphicKind.OPEN -> throw Error("Cannot generate JSON Schema for polymorphic type ${ser.descriptor.serialName}") SerialKind.CONTEXTUAL -> throw Error("This should not be reachable - ${ser.descriptor.serialName} could be unwrapped no further") } } catch(e: Exception) {