libeufin

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

commit 0a1c5a7465418dff7d63dbcc2dccd58ca0cea6ab
parent a917619a31aa0abbedfa36def0def2b38bae3d43
Author: MS <ms@taler.net>
Date:   Fri, 29 Sep 2023 12:19:05 +0200

Testing token timing.

Diffstat:
Mbank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt | 2++
Mbank/src/main/kotlin/tech/libeufin/bank/Main.kt | 7++++++-
Mbank/src/test/kotlin/LibeuFinApiTest.kt | 42+++++++++++++++++++++++++++++++++++++++++-
Mutil/src/main/kotlin/time.kt | 5++++-
4 files changed, 53 insertions(+), 3 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt b/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt @@ -58,9 +58,11 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: BankApplicationContext) { val creationTime = Instant.now() val expirationTimestamp = if (tokenDuration == ChronoUnit.FOREVER.duration) { + logger.debug("Creating 'forever' token.") Instant.MAX } else { try { + logger.debug("Creating token with days duration: ${tokenDuration.toDays()}") creationTime.plus(tokenDuration) } catch (e: Exception) { logger.error("Could not add token duration to current time: ${e.message}") diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt @@ -153,6 +153,9 @@ object TalerProtocolTimestampSerializer : KSerializer<TalerProtocolTimestamp> { } val ts: Long = maybeTs.longOrNull ?: throw badRequest("Could not convert t_s '${maybeTs.content}' to a number") + // Not allowing negative values, despite java.time allowance. + if (ts < 0) + throw badRequest("Negative timestamp not allowed.") val instant = try { Instant.ofEpochSecond(ts) } catch (e: Exception) { @@ -217,8 +220,10 @@ object RelativeTimeSerializer : KSerializer<RelativeTime> { } val dUs: Long = maybeDUs.longOrNull ?: throw badRequest("Could not convert d_us: '${maybeDUs.content}' to a number") + if (dUs < 0) + throw badRequest("Negative duration specified.") val duration = try { - Duration.ofNanos(dUs * 1000L) + Duration.of(dUs, ChronoUnit.MICROS) } catch (e: Exception) { logger.error("Could not get Duration out of d_us content: ${dUs}. ${e.message}") throw badRequest("Could not get Duration out of d_us content: ${dUs}") diff --git a/bank/src/test/kotlin/LibeuFinApiTest.kt b/bank/src/test/kotlin/LibeuFinApiTest.kt @@ -157,6 +157,45 @@ class LibeuFinApiTest { } } + // Testing that too big or invalid durations fail the request. + @Test + fun tokenInvalidDurationTest() { + val db = initDb() + val ctx = getTestContext() + assert(db.customerCreate(customerFoo) != null) + testApplication { + application { + corebankWebApp(db, ctx) + } + var r = client.post("/accounts/foo/token") { + expectSuccess = false + contentType(ContentType.Application.Json) + basicAuth("foo", "pw") + setBody("""{ + "duration": {"d_us": "invalid"}, + "scope": "readonly"}""".trimIndent()) + } + assert(r.status == HttpStatusCode.BadRequest) + r = client.post("/accounts/foo/token") { + expectSuccess = false + contentType(ContentType.Application.Json) + basicAuth("foo", "pw") + setBody("""{ + "duration": {"d_us": ${Long.MAX_VALUE}}, + "scope": "readonly"}""".trimIndent()) + } + assert(r.status == HttpStatusCode.BadRequest) + r = client.post("/accounts/foo/token") { + expectSuccess = false + contentType(ContentType.Application.Json) + basicAuth("foo", "pw") + setBody("""{ + "duration": {"d_us": -1}, + "scope": "readonly"}""".trimIndent()) + } + assert(r.status == HttpStatusCode.BadRequest) + } + } // Checking the POST /token handling. @Test fun tokenTest() { @@ -182,7 +221,8 @@ class LibeuFinApiTest { val newTokDb = db.bearerTokenGet(Base32Crockford.decode(newTokObj.access_token)) val lifeTime = Duration.between(newTokDb!!.creationTime, newTokDb.expirationTime) assert(lifeTime == Duration.ofDays(1)) - // foo tries on bar endpoint + + // foo tries to create a token on behalf of bar, expect 403. val r = client.post("/accounts/bar/token") { expectSuccess = false basicAuth("foo", "pw") diff --git a/util/src/main/kotlin/time.kt b/util/src/main/kotlin/time.kt @@ -58,7 +58,10 @@ private fun Instant.toNanos(): Long? { fun Instant.toDbMicros(): Long? { if (this == Instant.MAX) return Long.MAX_VALUE - val nanos = this.toNanos() ?: return null + val nanos = this.toNanos() ?: run { + logger.error("Could not obtain micros to store to database, convenience conversion to nanos overflew.") + return null + } return nanos / 1000L }