aboutsummaryrefslogtreecommitdiff
path: root/bank
diff options
context:
space:
mode:
authorAntoine A <>2024-02-07 19:02:27 +0100
committerAntoine A <>2024-02-07 19:02:27 +0100
commit4ddcada57dc252c4ccdb79d573228e25bc2811ed (patch)
treec920b9f61ec17231d06308844f26b16da6429157 /bank
parente3a62fa46e75cff879b59725165490943d0d0c1f (diff)
downloadlibeufin-4ddcada57dc252c4ccdb79d573228e25bc2811ed.tar.gz
libeufin-4ddcada57dc252c4ccdb79d573228e25bc2811ed.tar.bz2
libeufin-4ddcada57dc252c4ccdb79d573228e25bc2811ed.zip
Generate withdraw URI using request base url
Diffstat (limited to 'bank')
-rw-r--r--bank/build.gradle1
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/BankIntegrationApi.kt27
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/Config.kt8
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt4
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/Main.kt2
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/helpers.kt37
-rw-r--r--bank/src/test/kotlin/BankIntegrationApiTest.kt32
-rw-r--r--bank/src/test/kotlin/CoreBankApiTest.kt4
8 files changed, 34 insertions, 81 deletions
diff --git a/bank/build.gradle b/bank/build.gradle
index 1e9558ba..75f46bf8 100644
--- a/bank/build.gradle
+++ b/bank/build.gradle
@@ -33,6 +33,7 @@ dependencies {
implementation("io.ktor:ktor-server-status-pages:$ktor_version")
implementation("io.ktor:ktor-server-netty:$ktor_version")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
+ implementation("io.ktor:ktor-server-forwarded-header:$ktor_version")
// UNIX domain sockets support (used to connect to PostgreSQL)
implementation("com.kohlschutter.junixsocket:junixsocket-core:$junixsocket_version")
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/BankIntegrationApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/BankIntegrationApi.kt
index 3440bd54..757fc0b3 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/BankIntegrationApi.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/BankIntegrationApi.kt
@@ -49,24 +49,19 @@ fun Routing.bankIntegrationApi(db: Database, ctx: BankConfig) {
)
call.respond(op.copy(
suggested_exchange = ctx.suggestedWithdrawalExchange,
- confirm_transfer_url = ctx.spaCaptchaURL?.run {
- getWithdrawalConfirmUrl(
- baseUrl = this,
- wopId = uuid
- )
- }
+ confirm_transfer_url = if (op.status == WithdrawalStatus.selected) call.request.withdrawConfirmUrl(uuid) else null
))
}
post("/taler-integration/withdrawal-operation/{wopid}") {
- val opId = call.uuidParameter("wopid")
+ val uuid = call.uuidParameter("wopid")
val req = call.receive<BankWithdrawalOperationPostRequest>()
val res = db.withdrawal.setDetails(
- opId, req.selected_exchange, req.reserve_pub
+ uuid, req.selected_exchange, req.reserve_pub
)
when (res) {
is WithdrawalSelectionResult.UnknownOperation -> throw notFound(
- "Withdrawal operation $opId not found",
+ "Withdrawal operation '$uuid' not found",
TalerErrorCode.BANK_TRANSACTION_NOT_FOUND
)
is WithdrawalSelectionResult.AlreadySelected -> throw conflict(
@@ -86,25 +81,19 @@ fun Routing.bankIntegrationApi(db: Database, ctx: BankConfig) {
TalerErrorCode.BANK_ACCOUNT_IS_NOT_EXCHANGE
)
is WithdrawalSelectionResult.Success -> {
- val confirmUrl: String? = if (ctx.spaCaptchaURL !== null && res.status == WithdrawalStatus.selected) {
- getWithdrawalConfirmUrl(
- baseUrl = ctx.spaCaptchaURL,
- wopId = opId
- )
- } else null
call.respond(BankWithdrawalOperationPostResponse(
transfer_done = res.status == WithdrawalStatus.confirmed,
status = res.status,
- confirm_transfer_url = confirmUrl
+ confirm_transfer_url = if (res.status == WithdrawalStatus.selected) call.request.withdrawConfirmUrl(uuid) else null
))
}
}
}
post("/taler-integration/withdrawal-operation/{wopid}/abort") {
- val opId = call.uuidParameter("wopid")
- when (db.withdrawal.abort(opId)) {
+ val uuid = call.uuidParameter("wopid")
+ when (db.withdrawal.abort(uuid)) {
AbortResult.UnknownOperation -> throw notFound(
- "Withdrawal operation $opId not found",
+ "Withdrawal operation '$uuid' not found",
TalerErrorCode.BANK_TRANSACTION_NOT_FOUND
)
AbortResult.AlreadyConfirmed -> throw conflict(
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Config.kt b/bank/src/main/kotlin/tech/libeufin/bank/Config.kt
index 8ec6bc8d..2c15f43b 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Config.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Config.kt
@@ -42,13 +42,6 @@ data class BankConfig(
val defaultDebtLimit: TalerAmount,
val registrationBonus: TalerAmount,
val suggestedWithdrawalExchange: String?,
- /**
- * URL where the user should be redirected to complete the captcha.
- * It can contain the substring "{woid}" that is going to be replaced
- * with the withdrawal operation id and should point where the bank
- * SPA is located.
- */
- val spaCaptchaURL: String?,
val allowConversion: Boolean,
val fiatCurrency: String?,
val fiatCurrencySpec: CurrencySpecification?,
@@ -142,7 +135,6 @@ fun TalerConfig.loadBankConfig(): BankConfig {
defaultDebtLimit = amount("libeufin-bank", "default_debt_limit", regionalCurrency) ?: TalerAmount(0, 0, regionalCurrency),
registrationBonus = amount("libeufin-bank", "registration_bonus", regionalCurrency) ?: TalerAmount(0, 0, regionalCurrency),
suggestedWithdrawalExchange = lookupString("libeufin-bank", "suggested_withdrawal_exchange"),
- spaCaptchaURL = lookupString("libeufin-bank", "spa_captcha_url"),
spaPath = lookupPath("libeufin-bank", "spa"),
allowConversion = allowConversion,
fiatCurrency = fiatCurrency,
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
index f1c5cc94..6dab5571 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
@@ -503,12 +503,10 @@ private fun Routing.coreBankWithdrawalApi(db: Database, ctx: BankConfig) {
TalerErrorCode.BANK_UNALLOWED_DEBIT
)
WithdrawalCreationResult.Success -> {
- val bankBaseUrl = call.request.getBaseUrl()
- ?: throw internalServerError("Bank could not find its own base URL")
call.respond(
BankAccountCreateWithdrawalResponse(
withdrawal_id = opId.toString(),
- taler_withdraw_uri = getTalerWithdrawUri(bankBaseUrl, opId.toString())
+ taler_withdraw_uri = call.request.talerWithdrawUri(opId)
)
)
}
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
index cee9d822..65efa43b 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
@@ -36,6 +36,7 @@ import io.ktor.server.plugins.callloging.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.cors.routing.*
import io.ktor.server.plugins.statuspages.*
+import io.ktor.server.plugins.forwardedheaders.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
@@ -120,6 +121,7 @@ fun Application.corebankWebApp(db: Database, ctx: BankConfig) {
}
}
}
+ install(XForwardedHeaders)
install(CORS) {
anyHost()
allowHeader(HttpHeaders.Authorization)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt b/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt
index 24dc4168..e0ea90d5 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt
@@ -59,32 +59,29 @@ suspend fun ApplicationCall.bankInfo(db: Database, ctx: BankPaytoCtx): BankInfo
*
* https://$BANK_URL/taler-integration
*/
-fun getTalerWithdrawUri(baseUrl: String, woId: String) = url {
- val baseUrlObj = URI(baseUrl).toURL()
+fun ApplicationRequest.talerWithdrawUri(id: UUID) = url {
protocol = URLProtocol(
- name = "taler".plus(if (baseUrlObj.protocol.lowercase() == "http") "+http" else ""), defaultPort = -1
+ name = if (origin.scheme == "http") "taler+http" else "taler",
+ defaultPort = -1
)
host = "withdraw"
- val pathSegments = mutableListOf(
- // adds the hostname(+port) of the actual bank that will serve the withdrawal request.
- baseUrlObj.host.plus(
- if (baseUrlObj.port != -1) ":${baseUrlObj.port}"
- else ""
- )
- )
- // Removing potential double slashes.
- baseUrlObj.path.split("/").forEach {
- if (it.isNotEmpty()) pathSegments.add(it)
+ appendPathSegments(origin.serverHost)
+ headers["X-Forward-Prefix"]?.let {
+ appendPathSegments(it)
}
- pathSegments.add("taler-integration/${woId}")
- this.appendPathSegments(pathSegments)
+ appendPathSegments("taler-integration", id.toString())
}
-// Builds a withdrawal confirm URL.
-fun getWithdrawalConfirmUrl(
- baseUrl: String, wopId: UUID
-): String {
- return baseUrl.replace("{woid}", wopId.toString())
+fun ApplicationRequest.withdrawConfirmUrl(id: UUID) = url {
+ protocol = URLProtocol(
+ name = origin.scheme,
+ defaultPort = -1
+ )
+ host = origin.serverHost
+ headers["X-Forward-Prefix"]?.let {
+ appendPathSegments(it)
+ }
+ appendEncodedPathSegments("webui", "#", "operation", id.toString())
}
fun ApplicationCall.uuidParameter(name: String): UUID {
diff --git a/bank/src/test/kotlin/BankIntegrationApiTest.kt b/bank/src/test/kotlin/BankIntegrationApiTest.kt
index 73d6a700..90ea13f9 100644
--- a/bank/src/test/kotlin/BankIntegrationApiTest.kt
+++ b/bank/src/test/kotlin/BankIntegrationApiTest.kt
@@ -99,12 +99,14 @@ class BankIntegrationApiTest {
json(req)
}.assertOkJson<BankWithdrawalOperationPostResponse> {
assertEquals(WithdrawalStatus.selected, it.status)
+ assertEquals("http://localhost/webui/#/operation/$uuid", it.confirm_transfer_url)
}
// Check idempotence
client.post("/taler-integration/withdrawal-operation/$uuid") {
json(req)
}.assertOkJson<BankWithdrawalOperationPostResponse> {
assertEquals(WithdrawalStatus.selected, it.status)
+ assertEquals("http://localhost/webui/#/operation/$uuid", it.confirm_transfer_url)
}
// Check already selected
client.post("/taler-integration/withdrawal-operation/$uuid") {
@@ -188,34 +190,4 @@ class BankIntegrationApiTest {
client.postA("/taler-integration/withdrawal-operation/${UUID.randomUUID()}/abort")
.assertNotFound(TalerErrorCode.BANK_TRANSACTION_NOT_FOUND)
}
-
- // Testing the generation of taler://withdraw-URIs.
- @Test
- fun testWithdrawUri() {
- // Checking the taler+http://-style.
- val withHttp = getTalerWithdrawUri(
- "http://example.com",
- "my-id"
- )
- assertEquals(withHttp, "taler+http://withdraw/example.com/taler-integration/my-id")
- // Checking the taler://-style
- val onlyTaler = getTalerWithdrawUri(
- "https://example.com/",
- "my-id"
- )
- // Note: this tests as well that no double slashes belong to the result
- assertEquals(onlyTaler, "taler://withdraw/example.com/taler-integration/my-id")
- // Checking the removal of subsequent slashes
- val manySlashes = getTalerWithdrawUri(
- "https://www.example.com//////",
- "my-id"
- )
- assertEquals(manySlashes, "taler://withdraw/www.example.com/taler-integration/my-id")
- // Checking with specified port number
- val withPort = getTalerWithdrawUri(
- "https://www.example.com:9876",
- "my-id"
- )
- assertEquals(withPort, "taler://withdraw/www.example.com:9876/taler-integration/my-id")
- }
} \ No newline at end of file
diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt
index 4dda7872..1afb05cd 100644
--- a/bank/src/test/kotlin/CoreBankApiTest.kt
+++ b/bank/src/test/kotlin/CoreBankApiTest.kt
@@ -990,7 +990,9 @@ class CoreBankWithdrawalApiTest {
// Check OK
client.postA("/accounts/merchant/withdrawals") {
json { "amount" to "KUDOS:9.0" }
- }.assertOk()
+ }.assertOkJson<BankAccountCreateWithdrawalResponse> {
+ assertEquals("taler+http://withdraw/localhost/taler-integration/${it.withdrawal_id}", it.taler_withdraw_uri)
+ }
// Check exchange account
client.postA("/accounts/exchange/withdrawals") {