libeufin

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

commit d20f0f7953c5594180f0311f646a1846bcbb30d7
parent ce3de29eb1a657cfa10d55e228d8ddf82478ec24
Author: MS <ms@taler.net>
Date:   Mon, 16 Jan 2023 19:20:07 +0100

Implementing TAN command execution.

Diffstat:
Mnexus/src/test/kotlin/SandboxCircuitApiTest.kt | 21++++++++++++++++++++-
Msandbox/src/main/kotlin/tech/libeufin/sandbox/CircuitApi.kt | 42++++++++++++++++++++++++++++++++++++++++++
2 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/nexus/src/test/kotlin/SandboxCircuitApiTest.kt b/nexus/src/test/kotlin/SandboxCircuitApiTest.kt @@ -10,8 +10,8 @@ import io.ktor.util.* import kotlinx.coroutines.runBlocking import org.jetbrains.exposed.sql.transactions.transaction import org.junit.Test -import tech.libeufin.nexus.server.client import tech.libeufin.sandbox.* +import java.io.File class SandboxCircuitApiTest { // Get /config, fails if != 200. @@ -281,4 +281,23 @@ class SandboxCircuitApiTest { } } } + + @Test + fun tanCommandTest() { + /** + * 'tee' allows to test the SMS/e-mail command execution + * because it relates to STDIN and the first command line argument + * in the same way the SMS/e-mail command is expected to. + */ + val tanLocation = File("/tmp/libeufin-tan-cmd-test.txt") + val tanContent = "libeufin" + if (tanLocation.exists()) tanLocation.delete() + runTanCommand( + command = "tee", + address = tanLocation.path, + message = tanContent + ) + val maybeTan = tanLocation.readText() + assert(maybeTan == tanContent) + } } \ No newline at end of file diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/CircuitApi.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/CircuitApi.kt @@ -5,13 +5,19 @@ import io.ktor.http.* import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* +import io.ktor.utils.io.core.* import org.jetbrains.exposed.sql.transactions.transaction import tech.libeufin.sandbox.CashoutOperationsTable.uuid import tech.libeufin.util.* +import java.io.BufferedWriter import java.io.File +import java.io.InputStreamReader +import java.io.OutputStreamWriter import java.math.BigDecimal import java.math.MathContext import java.util.* +import java.util.concurrent.TimeUnit +import kotlin.text.toByteArray // CIRCUIT API TYPES data class CircuitCashoutRequest( @@ -132,6 +138,42 @@ fun isTanChannelSupported(tanChannel: String): Boolean { return false } +/** + * Runs the command and returns True/False if that succeeded/failed. + * A failed command causes "500 Internal Server Error" to be responded + * along a "/confirm" call. 'address' is a phone number or a e-mail address, + * according to which TAN channel is used. 'message' carries the TAN. + * + * The caller should try and catch this function. + */ +fun runTanCommand(command: String, address: String, message: String): Boolean { + val prep = ProcessBuilder(command, address) + prep.redirectErrorStream(true) // merge STDOUT and STDERR + val proc = prep.start() + proc.outputStream.write(message.toByteArray()) + proc.outputStream.flush(); proc.outputStream.close() + var isSuccessful = false + // Wait the command to finish. + proc.waitFor(10L, TimeUnit.SECONDS) + // Check if timed out. Kill if so. + if (proc.isAlive) { + logger.error("TAN command '$command' timed out, killing it.") + proc.destroy() + // Check if exited gracefully. Kill forcibly if not. + proc.waitFor(5L, TimeUnit.SECONDS) + if (proc.isAlive) { + logger.error("TAN command '$command' didn't terminate after killing it. Try forcefully.") + proc.destroyForcibly() + } + } + // Check if successful. Switch the state if so. + if (proc.exitValue() == 0) isSuccessful = true + // Log STDOUT and STDERR if failed. + if (!isSuccessful) + logger.error(InputStreamReader(proc.inputStream).readText()) + return isSuccessful +} + fun circuitApi(circuitRoute: Route) { // Abort a cash-out operation. circuitRoute.post("/cashouts/{uuid}/abort") {