libeufin

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

commit e3613d161e17748a97e99f27b6b2c17242602ecc
parent 2e2101a2dacbaf985fe0a57bbce7a14064c5704b
Author: Antoine A <>
Date:   Tue, 22 Oct 2024 16:48:12 +0200

bank: add flag to disable password quality check

Diffstat:
Abank/conf/test_no_password_check.conf | 12++++++++++++
Mbank/src/main/kotlin/tech/libeufin/bank/Config.kt | 4+++-
Mbank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt | 4++--
Mbank/src/main/kotlin/tech/libeufin/bank/cli/ChangePw.kt | 2+-
Mbank/src/test/kotlin/CoreBankApiTest.kt | 43++++++++++++++++++++++++++++++++++++++++++-
Mbank/src/test/kotlin/helpers.kt | 2+-
Mcommon/src/main/kotlin/crypto/PwCrypto.kt | 3++-
Mcontrib/bank.conf | 4++++
8 files changed, 67 insertions(+), 7 deletions(-)

diff --git a/bank/conf/test_no_password_check.conf b/bank/conf/test_no_password_check.conf @@ -0,0 +1,11 @@ +[libeufin-bank] +CURRENCY = KUDOS +WIRE_TYPE = iban +IBAN_PAYTO_BIC = SANDBOXX +ALLOW_REGISTRATION = yes +ALLOW_ACCOUNT_DELETION = yes +PWD_HASH_CONFIG = { "cost": 4 } +PWD_CHECK = no + +[libeufin-bankdb-postgres] +CONFIG = postgresql:///libeufincheck +\ No newline at end of file diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Config.kt b/bank/src/main/kotlin/tech/libeufin/bank/Config.kt @@ -53,7 +53,8 @@ data class BankConfig( val pwCrypto: PwCrypto, val gcAbortAfter: Duration, val gcCleanAfter: Duration, - val gcDeleteAfter: Duration + val gcDeleteAfter: Duration, + val pwdCheckQuality: Boolean ) { val dbCfg: DatabaseConfig by lazy { val sect = cfg.section("libeufin-bankdb-postgres") @@ -154,6 +155,7 @@ private fun TalerConfig.loadBankConfig(): BankConfig = section("libeufin-bank"). gcAbortAfter = duration("gc_abort_after").require(), gcCleanAfter = duration("gc_clean_after").require(), gcDeleteAfter = duration("gc_delete_after").require(), + pwdCheckQuality = boolean("pwd_check").default(true) ) } diff --git a/bank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/api/CoreBankApi.kt @@ -208,7 +208,7 @@ suspend fun createAccount( TalerErrorCode.END ) - val password = req.password.checkPw() + val password = req.password.checkPw(cfg.pwdCheckQuality) suspend fun doDb(internalPayto: Payto) = db.account.create( username = req.username, @@ -401,7 +401,7 @@ private fun Routing.coreBankAccountsApi(db: Database, ctx: BankConfig) { TalerErrorCode.BANK_NON_ADMIN_PATCH_MISSING_OLD_PASSWORD ) } - val newPassword = req.new_password.checkPw() + val newPassword = req.new_password.checkPw(ctx.pwdCheckQuality) when (db.account.reconfigPassword(call.username, newPassword, req.old_password, call.isAdmin || challenge != null, ctx.pwCrypto)) { AccountPatchAuthResult.Success -> call.respond(HttpStatusCode.NoContent) diff --git a/bank/src/main/kotlin/tech/libeufin/bank/cli/ChangePw.kt b/bank/src/main/kotlin/tech/libeufin/bank/cli/ChangePw.kt @@ -61,8 +61,8 @@ class ChangePw : CliktCommand("passwd") { ).ask()!! } override fun run() = cliCmd(logger, common.log) { - val password = password.checkPw() bankConfig(common.config).withDb { db, cfg -> + val password = password.checkPw(cfg.pwdCheckQuality) val res = db.account.reconfigPassword(username, password, null, true, cfg.pwCrypto) when (res) { AccountPatchAuthResult.UnknownAccount -> diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt @@ -267,7 +267,7 @@ class CoreBankTokenApiTest { } class CoreBankAccountsApiTest { - // Testing the account creation and its idempotency + // POST /accounts @Test fun create() = bankSetup { // Check generated payto @@ -560,6 +560,27 @@ class CoreBankAccountsApiTest { }.assertConflict(TalerErrorCode.BANK_TAN_CHANNEL_NOT_SUPPORTED) } + // POST /accounts + @Test + fun createNoCheck() = bankSetup("test_no_password_check.conf") { + // Testing short password + client.post("/accounts") { + json { + "username" to "short" + "name" to "John Smith" + "password" to "short" + } + }.assertOk() + // Testing long password + client.post("/accounts") { + json { + "username" to "long" + "name" to "Jane Smith" + "password" to "loooooooooooooooooooooooooooooooooooooooooooooooooooooooong-password" + } + }.assertOk() + } + // DELETE /accounts/USERNAME @Test fun delete() = bankSetup { db -> @@ -946,6 +967,26 @@ class CoreBankAccountsApiTest { }.assertNoContent() } + // PATCH /accounts/USERNAME/auth + @Test + fun passwordChangeNoCheck() = bankSetup("test_no_password_check.conf") { + // Testing short password + client.patchA("/accounts/merchant/auth") { + json { + "old_password" to "merchant-password" + "new_password" to "short" + } + }.assertNoContent() + // Testing long password + client.patch("/accounts/merchant/auth") { + basicAuth("merchant", "short") + json { + "old_password" to "short" + "new_password" to "looooooooooooooooooooooooooooooooooooooooooooooooooooooooong-password" + } + }.assertNoContent() + } + // GET /public-accounts and GET /accounts @Test fun list() = bankSetup(conf = "test_no_conversion.conf") { db -> diff --git a/bank/src/test/kotlin/helpers.kt b/bank/src/test/kotlin/helpers.kt @@ -344,7 +344,7 @@ suspend fun HttpResponse.assertChallenge( check: suspend (TanChannel, String) -> Unit = { _, _ -> } ): HttpResponse { val id = assertAcceptedJson<TanChallenge>().challenge_id - val username = call.request.url.pathSegments[2] + val username = call.request.url.segments[1] val res = call.client.postA("/accounts/$username/challenge/$id").assertOkJson<TanTransmission>() check(res.tan_channel, res.tan_info) val code = tanCode(res.tan_info) diff --git a/common/src/main/kotlin/crypto/PwCrypto.kt b/common/src/main/kotlin/crypto/PwCrypto.kt @@ -30,7 +30,8 @@ private const val PASSWORD_MIN_LEN = 8 private const val PASSWORD_MAX_LEN = 64 /** Check if a string is a valid password */ -fun String.checkPw(): Password { +fun String.checkPw(checkQuality: Boolean): Password { + if (!checkQuality) return Password(this) val len = this.length return when { len < PASSWORD_MIN_LEN -> throw conflict( diff --git a/contrib/bank.conf b/contrib/bank.conf @@ -91,6 +91,10 @@ PWD_HASH_ALGORITHM = bcrypt # When PWD_HASH_ALGORITHM = bcrypt you can configure cost PWD_HASH_CONFIG = { "cost": 8 } +# Whether to check password quality +# Unstable flag, will become a non configurable default in a future version +PWD_CHECK = yes + # Time after which pending operations are aborted during garbage collection GC_ABORT_AFTER = 15m