libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit cdc8f9b1df3367b098ad1456f1426d514575fd6f
parent a6661dffe23dbf5157167204ccda8855dc1d1d21
Author: ms <ms@taler.net>
Date:   Fri,  5 Nov 2021 23:15:33 +0100

Finish basic auth implementation.

Implementing check for non-admin users.

Diffstat:
Msandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt | 44++++++++++++++++++++++++++++++++++++++++++++
Msandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt | 2+-
Mutil/src/main/kotlin/HTTP.kt | 33+--------------------------------
3 files changed, 46 insertions(+), 33 deletions(-)

diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt @@ -21,6 +21,7 @@ package tech.libeufin.sandbox import io.ktor.application.* import io.ktor.http.HttpStatusCode +import io.ktor.request.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.transactions.transaction @@ -40,6 +41,42 @@ data class SandboxCamt( val creationTime: Long ) +/** + * Return: + * - null if the authentication is disabled (during tests, for example). + * This facilitates tests because allows requests to lack entirely a + * Authorization header. + * - the name of the authenticated user + * - throw exception when the authentication fails + * + * Note: at this point it is ONLY checked whether the user provided + * a valid password for the username mentioned in the Authorization header. + * The actual access to the resources must be later checked by each handler. + */ +fun ApplicationRequest.basicAuth(): String? { + val withAuth = this.call.ensureAttribute(WITH_AUTH_ATTRIBUTE_KEY) + if (!withAuth) { + logger.info("Authentication is disabled - assuming tests currently running.") + return null + } + val credentials = getHTTPBasicAuthCredentials(this) + if (credentials.first == "admin") { + // env must contain the admin password, because --with-auth is true. + val adminPassword: String = this.call.ensureAttribute(ADMIN_PASSWORD_ATTRIBUTE_KEY) + if (credentials.second != adminPassword) throw unauthorized( + "Admin authentication failed" + ) + return credentials.first + } + val passwordHash = transaction { + val customer = getCustomer(credentials.first) + customer.passwordHash + } + if (!CryptoUtil.checkPwOrThrow(credentials.second, passwordHash)) + throw unauthorized("Customer '${credentials.first}' gave wrong credentials") + return credentials.first +} + fun SandboxAssert(condition: Boolean, reason: String) { if (!condition) throw SandboxError(HttpStatusCode.InternalServerError, reason) } @@ -85,6 +122,13 @@ fun getHistoryElementFromTransactionRow( return getHistoryElementFromTransactionRow(dbRow.transactionRef) } +// Need to be called within a transaction {} block. +fun getCustomer(username: String): DemobankCustomerEntity { + return DemobankCustomerEntity.find { + DemobankCustomersTable.username eq username + }.firstOrNull() ?: throw notFound("Customer '${username}' not found") +} + /** * Get person name from a customer's username. */ diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt @@ -80,7 +80,7 @@ import java.security.interfaces.RSAPublicKey import javax.xml.bind.JAXBContext import kotlin.system.exitProcess -private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.sandbox") +val logger: Logger = LoggerFactory.getLogger("tech.libeufin.sandbox") private val currencyEnv: String? = System.getenv("LIBEUFIN_SANDBOX_CURRENCY") const val SANDBOX_DB_ENV_VAR_NAME = "LIBEUFIN_SANDBOX_DB_CONNECTION" private val adminPassword: String? = System.getenv("LIBEUFIN_SANDBOX_ADMIN_PASSWORD") diff --git a/util/src/main/kotlin/HTTP.kt b/util/src/main/kotlin/HTTP.kt @@ -6,6 +6,7 @@ import io.ktor.http.* import io.ktor.request.* import io.ktor.util.* import logger +import org.jetbrains.exposed.sql.transactions.transaction import java.net.URLDecoder fun unauthorized(msg: String): UtilError { @@ -117,38 +118,6 @@ fun ApplicationCall.getUriComponent(name: String): String { if (ret == null) throw internalServerError("Component $name not found in URI") return ret } -/** - * Return: - * - null if the authentication is disabled (during tests, for example). - * This facilitates tests because allows requests to lack entirely a - * Authorization header. - * - the name of the authenticated user - * - throw exception when the authentication fails - * - * Note: at this point it is ONLY checked whether the user provided - * a valid password for the username mentioned in the Authorization header. - * The actual access to the resources must be later checked by each handler. - */ -fun ApplicationRequest.basicAuth(): String? { - val withAuth = this.call.ensureAttribute(WITH_AUTH_ATTRIBUTE_KEY) - if (!withAuth) { - logger.info("Authentication is disabled - assuming tests currently running.") - return null - } - val credentials = getHTTPBasicAuthCredentials(this) - if (credentials.first == "admin") { - // env must contain the admin password, because --with-auth is true. - val adminPassword: String = this.call.ensureAttribute(ADMIN_PASSWORD_ATTRIBUTE_KEY) - if (credentials.second != adminPassword) throw unauthorized( - "Admin authentication failed" - ) - return credentials.first - } - throw unauthorized("Demobank customers not implemented yet!") - /** - * TODO: extract customer hashed password from the database and check. - */ -} /** * Throw "unauthorized" if the request is not