libeufin

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

commit 1b4fee7d989d7b595588cc28e5d8c7494d9b83d3
parent 06a063594836a2e15d68c29de293db8a0d86c2f1
Author: Antoine A <>
Date:   Thu, 10 Oct 2024 12:35:29 +0200

common: update to Ktor 3 and Kotlin 2.0.21

Diffstat:
Mbank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt | 50+++++++++++++++++++++++++-------------------------
Mbank/src/main/kotlin/tech/libeufin/bank/api/RevenueApi.kt | 2+-
Mbank/src/main/kotlin/tech/libeufin/bank/api/WireGatewayApi.kt | 26+++++++++++++-------------
Mbank/src/main/kotlin/tech/libeufin/bank/auth/auth.kt | 16++++++----------
Mbuild.gradle | 6+++---
Mcommon/src/main/kotlin/api/route.kt | 19++++++++++---------
Mcommon/src/main/kotlin/api/server.kt | 35++++++++++++++++++-----------------
Mnexus/src/main/kotlin/tech/libeufin/nexus/api/RevenueApi.kt | 2+-
Mnexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt | 14+++++++-------
Mnexus/src/main/kotlin/tech/libeufin/nexus/api/helpers.kt | 4++--
10 files changed, 86 insertions(+), 88 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt @@ -118,7 +118,7 @@ private fun Routing.coreBankTokenApi(db: Database, cfg: BankConfig) { } } if (!db.token.create( - username = username, + username = call.username, content = token.raw, creationTime = creationTime, expirationTime = expirationTimestamp, @@ -144,7 +144,7 @@ private fun Routing.coreBankTokenApi(db: Database, cfg: BankConfig) { } get("/accounts/{USERNAME}/tokens") { val params = PageParams.extract(call.request.queryParameters) - val tokens = db.token.page(params, username) + val tokens = db.token.page(params, call.username) if (tokens.isEmpty()) { call.respond(HttpStatusCode.NoContent) } else { @@ -307,7 +307,7 @@ private fun Routing.coreBankAccountsApi(db: Database, ctx: BankConfig) { authAdmin(db, ctx.pwCrypto, TokenScope.readwrite, !ctx.allowRegistration) { post("/accounts") { val req = call.receive<RegisterAccountRequest>() - when (val result = createAccount(db, ctx, req, isAdmin)) { + when (val result = createAccount(db, ctx, req, call.isAdmin)) { AccountCreationResult.BonusBalanceInsufficient -> throw conflict( "Insufficient admin funds to grant bonus", TalerErrorCode.BANK_UNALLOWED_DEBIT @@ -335,19 +335,19 @@ private fun Routing.coreBankAccountsApi(db: Database, ctx: BankConfig) { val challenge = call.checkChallenge(db, Operation.account_delete) // Not deleting reserved names. - if (RESERVED_ACCOUNTS.contains(username)) + if (RESERVED_ACCOUNTS.contains(call.username)) throw conflict( "Cannot delete reserved accounts", TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT ) - if (username == "exchange" && ctx.allowConversion) + if (call.username == "exchange" && ctx.allowConversion) throw conflict( "Cannot delete 'exchange' accounts when conversion is enabled", TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT ) - when (db.account.delete(username, isAdmin || challenge != null)) { - AccountDeletionResult.UnknownAccount -> throw unknownAccount(username) + when (db.account.delete(call.username, call.isAdmin || challenge != null)) { + AccountDeletionResult.UnknownAccount -> throw unknownAccount(call.username) AccountDeletionResult.BalanceNotZero -> throw conflict( "Account balance is not zero.", TalerErrorCode.BANK_ACCOUNT_BALANCE_NOT_ZERO @@ -360,13 +360,13 @@ private fun Routing.coreBankAccountsApi(db: Database, ctx: BankConfig) { auth(db, ctx.pwCrypto, TokenScope.readwrite, allowAdmin = true) { patch("/accounts/{USERNAME}") { val (req, challenge) = call.receiveChallenge<AccountReconfiguration>(db, Operation.account_reconfig) - val res = patchAccount(db, ctx, req, username, isAdmin, challenge != null, challenge?.channel, challenge?.info) + val res = patchAccount(db, ctx, req, call.username, call.isAdmin, challenge != null, challenge?.channel, challenge?.info) when (res) { AccountPatchResult.Success -> call.respond(HttpStatusCode.NoContent) is AccountPatchResult.TanRequired -> { call.respondChallenge(db, Operation.account_reconfig, req, res.channel, res.info) } - AccountPatchResult.UnknownAccount -> throw unknownAccount(username) + AccountPatchResult.UnknownAccount -> throw unknownAccount(call.username) AccountPatchResult.NonAdminName -> throw conflict( "non-admin user cannot change their legal name", TalerErrorCode.BANK_NON_ADMIN_PATCH_LEGAL_NAME @@ -392,16 +392,16 @@ private fun Routing.coreBankAccountsApi(db: Database, ctx: BankConfig) { patch("/accounts/{USERNAME}/auth") { val (req, challenge) = call.receiveChallenge<AccountPasswordChange>(db, Operation.account_auth_reconfig) - if (!isAdmin && req.old_password == null) { + if (!call.isAdmin && req.old_password == null) { throw conflict( "non-admin user cannot change password without providing old password", TalerErrorCode.BANK_NON_ADMIN_PATCH_MISSING_OLD_PASSWORD ) } - when (db.account.reconfigPassword(username, req.new_password, req.old_password, isAdmin || challenge != null, ctx.pwCrypto)) { + when (db.account.reconfigPassword(call.username, req.new_password, req.old_password, call.isAdmin || challenge != null, ctx.pwCrypto)) { AccountPatchAuthResult.Success -> call.respond(HttpStatusCode.NoContent) AccountPatchAuthResult.TanRequired -> call.respondChallenge(db, Operation.account_auth_reconfig, req) - AccountPatchAuthResult.UnknownAccount -> throw unknownAccount(username) + AccountPatchAuthResult.UnknownAccount -> throw unknownAccount(call.username) AccountPatchAuthResult.OldPasswordMismatch -> throw conflict( "old password does not match", TalerErrorCode.BANK_PATCH_BAD_OLD_PASSWORD @@ -431,7 +431,7 @@ private fun Routing.coreBankAccountsApi(db: Database, ctx: BankConfig) { } auth(db, ctx.pwCrypto, TokenScope.readonly, allowAdmin = true) { get("/accounts/{USERNAME}") { - val account = db.account.get(username, ctx.payto) ?: throw unknownAccount(username) + val account = db.account.get(call.username, ctx.payto) ?: throw unknownAccount(call.username) call.respond(account) } } @@ -453,7 +453,7 @@ private fun Routing.coreBankTransactionsApi(db: Database, ctx: BankConfig) { } get("/accounts/{USERNAME}/transactions/{T_ID}") { val tId = call.longPath("T_ID") - val tx = db.transaction.get(tId, username, ctx.payto) ?: throw notFound( + val tx = db.transaction.get(tId, call.username, ctx.payto) ?: throw notFound( "Bank transaction '$tId' not found", TalerErrorCode.BANK_TRANSACTION_NOT_FOUND ) @@ -471,7 +471,7 @@ private fun Routing.coreBankTransactionsApi(db: Database, ctx: BankConfig) { val res = db.transaction.create( creditAccountPayto = req.payto_uri, - debitAccountUsername = username, + debitAccountUsername = call.username, subject = subject, amount = amount, timestamp = Instant.now(), @@ -482,7 +482,7 @@ private fun Routing.coreBankTransactionsApi(db: Database, ctx: BankConfig) { maxAmount = ctx.maxAmount ) when (res) { - BankTransactionResult.UnknownDebtor -> throw unknownAccount(username) + BankTransactionResult.UnknownDebtor -> throw unknownAccount(call.username) BankTransactionResult.TanRequired -> { call.respondChallenge(db, Operation.bank_transaction, req) } @@ -521,7 +521,7 @@ private fun Routing.coreBankWithdrawalApi(db: Database, ctx: BankConfig) { req.suggested_amount?.run(ctx::checkRegionalCurrency) val opId = UUID.randomUUID() when (db.withdrawal.create( - username, + call.username, opId, req.amount, req.suggested_amount, @@ -530,7 +530,7 @@ private fun Routing.coreBankWithdrawalApi(db: Database, ctx: BankConfig) { ctx.minAmount, ctx.maxAmount )) { - WithdrawalCreationResult.UnknownAccount -> throw unknownAccount(username) + WithdrawalCreationResult.UnknownAccount -> throw unknownAccount(call.username) WithdrawalCreationResult.AccountIsExchange -> throw conflict( "Exchange account cannot perform withdrawal operation", TalerErrorCode.BANK_ACCOUNT_IS_EXCHANGE @@ -558,7 +558,7 @@ private fun Routing.coreBankWithdrawalApi(db: Database, ctx: BankConfig) { val (req, challenge) = call.receiveChallenge<BankAccountConfirmWithdrawalRequest>(db, Operation.withdrawal, BankAccountConfirmWithdrawalRequest()) req.amount?.run(ctx::checkRegionalCurrency) when (db.withdrawal.confirm( - username, + call.username, id, Instant.now(), req.amount, @@ -640,7 +640,7 @@ private fun Routing.coreBankCashoutApi(db: Database, ctx: BankConfig) = conditio ctx.checkFiatCurrency(req.amount_credit) val res = db.cashout.create( - username = username, + username = call.username, requestUid = req.request_uid, amountDebit = req.amount_debit, amountCredit = req.amount_credit, @@ -649,7 +649,7 @@ private fun Routing.coreBankCashoutApi(db: Database, ctx: BankConfig) = conditio is2fa = challenge != null ) when (res) { - CashoutCreationResult.AccountNotFound -> throw unknownAccount(username) + CashoutCreationResult.AccountNotFound -> throw unknownAccount(call.username) CashoutCreationResult.BadConversion -> throw conflict( "Wrong currency conversion", TalerErrorCode.BANK_BAD_CONVERSION @@ -684,7 +684,7 @@ private fun Routing.coreBankCashoutApi(db: Database, ctx: BankConfig) = conditio auth(db, ctx.pwCrypto, TokenScope.readonly, allowAdmin = true) { get("/accounts/{USERNAME}/cashouts/{CASHOUT_ID}") { val id = call.longPath("CASHOUT_ID") - val cashout = db.cashout.get(id, username) ?: throw notFound( + val cashout = db.cashout.get(id, call.username) ?: throw notFound( "Cashout operation $id not found", TalerErrorCode.BANK_TRANSACTION_NOT_FOUND ) @@ -692,7 +692,7 @@ private fun Routing.coreBankCashoutApi(db: Database, ctx: BankConfig) = conditio } get("/accounts/{USERNAME}/cashouts") { val params = PageParams.extract(call.request.queryParameters) - val cashouts = db.cashout.pageForUser(params, username) + val cashouts = db.cashout.pageForUser(params, call.username) if (cashouts.isEmpty()) { call.respond(HttpStatusCode.NoContent) } else { @@ -719,7 +719,7 @@ private fun Routing.coreBankTanApi(db: Database, ctx: BankConfig) { val id = call.longPath("CHALLENGE_ID") val res = db.tan.send( id = id, - username = username, + username = call.username, code = Tan.genCode(), timestamp = Instant.now(), retryCounter = TAN_RETRY_COUNTER, @@ -783,7 +783,7 @@ private fun Routing.coreBankTanApi(db: Database, ctx: BankConfig) { val code = req.tan.removePrefix("T-") val res = db.tan.solve( id = id, - username = username, + username = call.username, code = code, timestamp = Instant.now() ) diff --git a/bank/src/main/kotlin/tech/libeufin/bank/api/RevenueApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/api/RevenueApi.kt @@ -39,7 +39,7 @@ fun Routing.revenueApi(db: Database, ctx: BankConfig) { )) } get("/accounts/{USERNAME}/taler-revenue/history") { - val params = HistoryParams.extract(context.request.queryParameters) + val params = HistoryParams.extract(call.request.queryParameters) val bankAccount = call.bankInfo(db, ctx.payto) val items = db.transaction.revenueHistory(params, bankAccount.bankAccountId, ctx.payto) diff --git a/bank/src/main/kotlin/tech/libeufin/bank/api/WireGatewayApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/api/WireGatewayApi.kt @@ -51,13 +51,13 @@ fun Routing.wireGatewayApi(db: Database, ctx: BankConfig) { ctx.checkRegionalCurrency(req.amount) val res = db.exchange.transfer( req = req, - username = username, + username = call.username, timestamp = Instant.now() ) when (res) { - is TransferResult.UnknownExchange -> throw unknownAccount(username) + is TransferResult.UnknownExchange -> throw unknownAccount(call.username) is TransferResult.NotAnExchange -> throw conflict( - "$username is not an exchange account.", + "${call.username} is not an exchange account.", TalerErrorCode.BANK_ACCOUNT_IS_NOT_EXCHANGE ) is TransferResult.BothPartyAreExchange -> throw conflict( @@ -82,12 +82,12 @@ fun Routing.wireGatewayApi(db: Database, ctx: BankConfig) { } } auth(db, ctx.pwCrypto, TokenScope.readonly) { - suspend fun <T> PipelineContext<Unit, ApplicationCall>.historyEndpoint( + suspend fun <T> ApplicationCall.historyEndpoint( reduce: (List<T>, String) -> Any, dbLambda: suspend ExchangeDAO.(HistoryParams, Long, BankPaytoCtx) -> List<T> ) { - val params = HistoryParams.extract(context.request.queryParameters) - val bankAccount = call.bankInfo(db, ctx.payto) + val params = HistoryParams.extract(this.request.queryParameters) + val bankAccount = this.bankInfo(db, ctx.payto) if (!bankAccount.isTalerExchange) throw conflict( @@ -98,23 +98,23 @@ fun Routing.wireGatewayApi(db: Database, ctx: BankConfig) { val items = db.exchange.dbLambda(params, bankAccount.bankAccountId, ctx.payto) if (items.isEmpty()) { - call.respond(HttpStatusCode.NoContent) + this.respond(HttpStatusCode.NoContent) } else { - call.respond(reduce(items, bankAccount.payto)) + this.respond(reduce(items, bankAccount.payto)) } } get("/accounts/{USERNAME}/taler-wire-gateway/history/incoming") { - historyEndpoint(::IncomingHistory, ExchangeDAO::incomingHistory) + call.historyEndpoint(::IncomingHistory, ExchangeDAO::incomingHistory) } get("/accounts/{USERNAME}/taler-wire-gateway/history/outgoing") { - historyEndpoint(::OutgoingHistory, ExchangeDAO::outgoingHistory) + call.historyEndpoint(::OutgoingHistory, ExchangeDAO::outgoingHistory) } get("/accounts/{USERNAME}/taler-wire-gateway/transfers") { - val params = TransferParams.extract(context.request.queryParameters) + val params = TransferParams.extract(call.request.queryParameters) val bankAccount = call.bankInfo(db, ctx.payto) if (!bankAccount.isTalerExchange) throw conflict( - "$username is not an exchange account.", + "${call.username} is not an exchange account.", TalerErrorCode.BANK_ACCOUNT_IS_NOT_EXCHANGE ) @@ -134,7 +134,7 @@ fun Routing.wireGatewayApi(db: Database, ctx: BankConfig) { val bankAccount = call.bankInfo(db, ctx.payto) if (!bankAccount.isTalerExchange) throw conflict( - "$username is not an exchange account.", + "${call.username} is not an exchange account.", TalerErrorCode.BANK_ACCOUNT_IS_NOT_EXCHANGE ) diff --git a/bank/src/main/kotlin/tech/libeufin/bank/auth/auth.kt b/bank/src/main/kotlin/tech/libeufin/bank/auth/auth.kt @@ -44,13 +44,9 @@ const val TOKEN_PREFIX = "secret-token:" /** Get username of the request account */ val ApplicationCall.username: String get() = parameters.expect("USERNAME") -/** Get username of the request account */ -val PipelineContext<Unit, ApplicationCall>.username: String get() = call.username /** Check if current auth account is admin */ val ApplicationCall.isAdmin: Boolean get() = attributes.getOrNull(AUTH_IS_ADMIN) == true -/** Check if current auth account is admin */ -val PipelineContext<Unit, ApplicationCall>.isAdmin: Boolean get() = call.isAdmin /** Check auth token used for authentication */ val ApplicationCall.authToken: ByteArray? get() = attributes.getOrNull(AUTH_TOKEN) @@ -65,18 +61,18 @@ val ApplicationCall.authToken: ByteArray? get() = attributes.getOrNull(AUTH_TOKE fun Route.authAdmin(db: Database, pwCrypto: PwCrypto, scope: TokenScope, enforce: Boolean = true, callback: Route.() -> Unit): Route = intercept(callback) { if (enforce) { - val username = context.authenticateBankRequest(db, pwCrypto, scope) + val username = this.authenticateBankRequest(db, pwCrypto, scope) if (username != "admin") { throw unauthorized("Only administrator allowed") } - context.attributes.put(AUTH_IS_ADMIN, true) + this.attributes.put(AUTH_IS_ADMIN, true) } else { val username = try { - context.authenticateBankRequest(db, pwCrypto, scope) + this.authenticateBankRequest(db, pwCrypto, scope) } catch (e: Exception) { null } - context.attributes.put(AUTH_IS_ADMIN, username == "admin") + this.attributes.put(AUTH_IS_ADMIN, username == "admin") } } @@ -91,7 +87,7 @@ fun Route.authAdmin(db: Database, pwCrypto: PwCrypto, scope: TokenScope, enforce **/ fun Route.auth(db: Database, pwCrypto: PwCrypto ,scope: TokenScope, allowAdmin: Boolean = false, requireAdmin: Boolean = false, callback: Route.() -> Unit): Route = intercept(callback) { - val authUsername = context.authenticateBankRequest(db, pwCrypto, scope) + val authUsername = this.authenticateBankRequest(db, pwCrypto, scope) if (requireAdmin && authUsername != "admin") { throw unauthorized("Only administrator allowed") } else { @@ -100,7 +96,7 @@ fun Route.auth(db: Database, pwCrypto: PwCrypto ,scope: TokenScope, allowAdmin: throw unauthorized("Customer $authUsername have no right on $username account") } } - context.attributes.put(AUTH_IS_ADMIN, authUsername == "admin") + this.attributes.put(AUTH_IS_ADMIN, authUsername == "admin") } /** diff --git a/build.gradle b/build.gradle @@ -1,7 +1,7 @@ // This file is in the public domain. plugins { - id("org.jetbrains.kotlin.jvm") version "2.0.20" + id("org.jetbrains.kotlin.jvm") version "2.0.21" id("org.jetbrains.dokka") version "1.9.20" id("idea") id("java-library") @@ -19,8 +19,8 @@ if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)){ allprojects { ext { - set("kotlin_version", "2.0.20") - set("ktor_version", "2.3.12") + set("kotlin_version", "2.0.21") + set("ktor_version", "3.0.0") set("clikt_version", "5.0.1") set("coroutines_version", "1.9.0") set("postgres_version", "42.7.4") diff --git a/common/src/main/kotlin/api/route.kt b/common/src/main/kotlin/api/route.kt @@ -23,16 +23,17 @@ import io.ktor.server.application.* import io.ktor.server.routing.* import io.ktor.util.pipeline.* -fun Route.intercept(callback: Route.() -> Unit, interceptor: suspend PipelineContext<Unit, ApplicationCall>.() -> Unit): Route { +fun Route.intercept(build: Route.() -> Unit, lambda: suspend ApplicationCall.() -> Unit): Route { + val plugin = createRouteScopedPlugin( + "Interceptor" + ) { + onCall { call -> call.lambda() } + } val subRoute = createChild(object : RouteSelector() { - override fun evaluate(context: RoutingResolveContext, segmentIndex: Int): RouteSelectorEvaluation = - RouteSelectorEvaluation.Constant + override suspend fun evaluate(context: RoutingResolveContext, segmentIndex: Int): RouteSelectorEvaluation = + RouteSelectorEvaluation.Transparent }) - subRoute.intercept(ApplicationCallPipeline.Plugins) { - interceptor() - proceed() - } - - callback(subRoute) + subRoute.install(plugin) + subRoute.build() return subRoute } \ No newline at end of file diff --git a/common/src/main/kotlin/api/server.kt b/common/src/main/kotlin/api/server.kt @@ -25,7 +25,7 @@ import io.ktor.server.application.* import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.server.plugins.* -import io.ktor.server.plugins.callloging.* +import io.ktor.server.plugins.calllogging.* import io.ktor.server.plugins.contentnegotiation.* import io.ktor.server.plugins.cors.routing.* import io.ktor.server.plugins.forwardedheaders.* @@ -223,23 +223,24 @@ fun Application.talerApi(logger: Logger, routes: Routing.() -> Unit) { // Dirty local variable to stop the server in test TODO remove this ugly hack var engine: ApplicationEngine? = null -fun serve(cfg: ServerConfig, api: Application.() -> Unit) { - val env = applicationEngineEnvironment { - when (cfg) { - is ServerConfig.Tcp -> { - for (addr in InetAddress.getAllByName(cfg.addr)) { - connector { - port = cfg.port - host = addr.hostAddress +fun serve(cfg: tech.libeufin.common.ServerConfig, api: Application.() -> Unit) { + val server = embeddedServer(Netty, + configure = { + when (cfg) { + is ServerConfig.Tcp -> { + for (addr in InetAddress.getAllByName(cfg.addr)) { + connector { + port = cfg.port + host = addr.hostAddress + } } } + is ServerConfig.Unix -> + throw Exception("Can only serve via TCP") } - is ServerConfig.Unix -> - throw Exception("Can only serve via TCP") - } - module { api() } - } - val local = embeddedServer(Netty, env) - engine = local - local.start(wait = true) + }, + module = api + ) + engine = server.engine + server.start(wait = true) } \ No newline at end of file diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/api/RevenueApi.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/api/RevenueApi.kt @@ -35,7 +35,7 @@ fun Routing.revenueApi(db: Database, cfg: NexusConfig) = authApi(cfg.revenueApiC )) } get("/taler-revenue/history") { - val params = HistoryParams.extract(context.request.queryParameters) + val params = HistoryParams.extract(call.request.queryParameters) val items = db.payment.revenueHistory(params) if (items.isEmpty()) { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt @@ -67,7 +67,7 @@ fun Routing.wireGatewayApi(db: Database, cfg: NexusConfig) = authApi(cfg.wireGat } } get("/taler-wire-gateway/transfers") { - val params = TransferParams.extract(context.request.queryParameters) + val params = TransferParams.extract(call.request.queryParameters) val items = db.exchange.pageTransfer(params) if (items.isEmpty()) { @@ -84,23 +84,23 @@ fun Routing.wireGatewayApi(db: Database, cfg: NexusConfig) = authApi(cfg.wireGat ) call.respond(transfer) } - suspend fun <T> PipelineContext<Unit, ApplicationCall>.historyEndpoint( + suspend fun <T> ApplicationCall.historyEndpoint( reduce: (List<T>, String) -> Any, dbLambda: suspend ExchangeDAO.(HistoryParams) -> List<T> ) { - val params = HistoryParams.extract(context.request.queryParameters) + val params = HistoryParams.extract(this.request.queryParameters) val items = db.exchange.dbLambda(params) if (items.isEmpty()) { - call.respond(HttpStatusCode.NoContent) + this.respond(HttpStatusCode.NoContent) } else { - call.respond(reduce(items, cfg.ebics.payto)) + this.respond(reduce(items, cfg.ebics.payto)) } } get("/taler-wire-gateway/history/incoming") { - historyEndpoint(::IncomingHistory, ExchangeDAO::incomingHistory) + call.historyEndpoint(::IncomingHistory, ExchangeDAO::incomingHistory) } get("/taler-wire-gateway/history/outgoing") { - historyEndpoint(::OutgoingHistory, ExchangeDAO::outgoingHistory) + call.historyEndpoint(::OutgoingHistory, ExchangeDAO::outgoingHistory) } suspend fun ApplicationCall.addIncoming( amount: TalerAmount, diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/api/helpers.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/api/helpers.kt @@ -33,13 +33,13 @@ fun Route.authApi(cfg: ApiConfig?, callback: Route.() -> Unit): Route = if (cfg == null) { throw apiError(HttpStatusCode.NotImplemented, "API not implemented", TalerErrorCode.END) } - val header = context.request.headers[HttpHeaders.Authorization] + val header = this.request.headers[HttpHeaders.Authorization] // Basic auth challenge when (cfg.authMethod) { AuthMethod.None -> {} is AuthMethod.Bearer -> { if (header == null) { - context.response.header(HttpHeaders.WWWAuthenticate, "Bearer") + this.response.header(HttpHeaders.WWWAuthenticate, "Bearer") throw unauthorized( "Authorization header not found", TalerErrorCode.GENERIC_PARAMETER_MISSING