[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libeufin] branch master updated: Implementing PATCH /auth
From: |
gnunet |
Subject: |
[libeufin] branch master updated: Implementing PATCH /auth |
Date: |
Tue, 03 Oct 2023 12:45:19 +0200 |
This is an automated email from the git hooks/post-receive script.
ms pushed a commit to branch master
in repository libeufin.
The following commit(s) were added to refs/heads/master by this push:
new 4b4fba43 Implementing PATCH /auth
4b4fba43 is described below
commit 4b4fba43ed644a39ceb5a19b3eb539e5f1d1bb55
Author: MS <ms@taler.net>
AuthorDate: Tue Oct 3 12:45:11 2023 +0200
Implementing PATCH /auth
---
.../main/kotlin/tech/libeufin/bank/BankMessages.kt | 11 ++-
.../tech/libeufin/bank/CorebankApiHandlers.kt | 109 ++++++++++++---------
bank/src/main/kotlin/tech/libeufin/bank/Main.kt | 1 +
bank/src/test/kotlin/DatabaseTest.kt | 11 +++
bank/src/test/kotlin/LibeuFinApiTest.kt | 34 +++++++
5 files changed, 116 insertions(+), 50 deletions(-)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt
b/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt
index 01f4da66..15188d41 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt
@@ -657,9 +657,18 @@ data class TransferResponse(
data class PublicAccountsResponse(
val public_accounts: MutableList<PublicAccount> = mutableListOf()
)
+
+/**
+ * Single element of GET /public-accounts list.
+ */
@Serializable
data class PublicAccount(
val payto_uri: String,
val balance: Balance,
val account_name: String
-)
\ No newline at end of file
+)
+
+@Serializable
+data class AccountPasswordChange(
+ val new_password: String
+)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
b/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
index 130609bf..a6a4f66f 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
@@ -24,6 +24,7 @@ private val logger: Logger =
LoggerFactory.getLogger("tech.libeufin.bank.account
*/
fun Routing.accountsMgmtHandlers(db: Database, ctx: BankApplicationContext) {
+ // TOKEN ENDPOINTS
delete("/accounts/{USERNAME}/token") {
val c = call.authenticateBankRequest(db, TokenScope.readonly) ?: throw
unauthorized()
/**
@@ -52,7 +53,6 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx:
BankApplicationContext) {
*/
call.respond(HttpStatusCode.NoContent)
}
-
post("/accounts/{USERNAME}/token") {
val customer =
call.authenticateBankRequest(db, TokenScope.refreshable) ?: throw
unauthorized("Authentication failed")
@@ -116,7 +116,46 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx:
BankApplicationContext) {
)
return@post
}
-
+ // ACCOUNT ENDPOINTS
+ get("/public-accounts") {
+ // no authentication here.
+ val publicAccounts = db.accountsGetPublic(ctx.currency)
+ if (publicAccounts.isEmpty()) {
+ call.respond(HttpStatusCode.NoContent)
+ return@get
+ }
+ call.respond(
+ PublicAccountsResponse().apply {
+ publicAccounts.forEach {
+ this.public_accounts.add(it)
+ }
+ }
+ )
+ return@get
+ }
+ get("/accounts") {
+ val c = call.authenticateBankRequest(db, TokenScope.readonly) ?: throw
unauthorized()
+ if (c.login != "admin") throw forbidden("Only admin allowed.")
+ // Get optional param.
+ val maybeFilter: String? = call.request.queryParameters["filter_name"]
+ logger.debug("Filtering on '${maybeFilter}'")
+ val queryParam = if (maybeFilter != null) {
+ "%${maybeFilter}%"
+ } else "%"
+ val dbRes = db.accountsGetForAdmin(queryParam)
+ if (dbRes.isEmpty()) {
+ call.respond(HttpStatusCode.NoContent)
+ return@get
+ }
+ call.respond(
+ ListBankAccountsResponse().apply {
+ dbRes.forEach { element ->
+ this.accounts.add(element)
+ }
+ }
+ )
+ return@get
+ }
post("/accounts") { // check if only admin is allowed to create new
accounts
if (ctx.restrictRegistration) {
val customer: Customer? = call.authenticateBankRequest(db,
TokenScope.readwrite)
@@ -220,46 +259,6 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx:
BankApplicationContext) {
call.respond(HttpStatusCode.Created)
return@post
}
- get("/public-accounts") {
- // no authentication here.
- val publicAccounts = db.accountsGetPublic(ctx.currency)
- if (publicAccounts.isEmpty()) {
- call.respond(HttpStatusCode.NoContent)
- return@get
- }
- call.respond(
- PublicAccountsResponse().apply {
- publicAccounts.forEach {
- this.public_accounts.add(it)
- }
- }
- )
- return@get
- }
- get("/accounts") {
- val c = call.authenticateBankRequest(db, TokenScope.readonly) ?: throw
unauthorized()
- if (c.login != "admin") throw forbidden("Only admin allowed.")
- // Get optional param.
- val maybeFilter: String? = call.request.queryParameters["filter_name"]
- logger.debug("Filtering on '${maybeFilter}'")
- val queryParam = if (maybeFilter != null) {
- "%${maybeFilter}%"
- } else "%"
- val dbRes = db.accountsGetForAdmin(queryParam)
- if (dbRes.isEmpty()) {
- call.respond(HttpStatusCode.NoContent)
- return@get
- }
- call.respond(
- ListBankAccountsResponse().apply {
- dbRes.forEach { element ->
- this.accounts.add(element)
- }
- }
- )
- return@get
- }
-
get("/accounts/{USERNAME}") {
val c = call.authenticateBankRequest(db, TokenScope.readonly) ?: throw
unauthorized("Login failed")
val resourceName = call.maybeUriComponent("USERNAME") ?: throw
badRequest(
@@ -324,7 +323,24 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx:
BankApplicationContext) {
}
return@delete
}
-
+ patch("/accounts/{USERNAME}/auth") {
+ val c = call.authenticateBankRequest(db, TokenScope.readwrite) ?:
throw unauthorized()
+ val accountName = call.getResourceName("USERNAME")
+ if (!accountName.canI(c, withAdmin = true)) throw forbidden()
+ val req = call.receive<AccountPasswordChange>()
+ val hashedPassword = CryptoUtil.hashpw(req.new_password)
+ if (!db.customerChangePassword(
+ accountName,
+ hashedPassword
+ ))
+ throw notFound(
+ "Account '$accountName' not found",
+ talerEc = TalerErrorCode.TALER_EC_END // FIXME: need at least
GENERIC_NOT_FOUND.
+ )
+ call.respond(HttpStatusCode.NoContent)
+ return@patch
+ }
+ // WITHDRAWAL ENDPOINTS
post("/accounts/{USERNAME}/withdrawals") {
val c = call.authenticateBankRequest(db, TokenScope.readwrite)
?: throw unauthorized() // Admin not allowed to withdraw in the
name of customers:
@@ -356,7 +372,6 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx:
BankApplicationContext) {
)
return@post
}
-
get("/withdrawals/{withdrawal_id}") {
val op = getWithdrawal(db, call.expectUriComponent("withdrawal_id"))
call.respond(
@@ -371,7 +386,6 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx:
BankApplicationContext) {
)
return@get
}
-
post("/withdrawals/{withdrawal_id}/abort") {
val op = getWithdrawal(db, call.expectUriComponent("withdrawal_id"))
// Idempotency:
if (op.aborted) {
@@ -384,7 +398,6 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx:
BankApplicationContext) {
call.respondText("{}", ContentType.Application.Json)
return@post
}
-
post("/withdrawals/{withdrawal_id}/confirm") {
val op = getWithdrawal(db, call.expectUriComponent("withdrawal_id"))
// Checking idempotency:
if (op.confirmationDone) {
@@ -434,7 +447,7 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx:
BankApplicationContext) {
}
return@post
}
-
+ // TRANSACTION ENDPOINT
get("/accounts/{USERNAME}/transactions") {
val c = call.authenticateBankRequest(db, TokenScope.readonly) ?: throw
unauthorized()
val resourceName = call.expectUriComponent("USERNAME")
@@ -465,7 +478,6 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx:
BankApplicationContext) {
call.respond(res)
return@get
}
-
// Creates a bank transaction.
post("/accounts/{USERNAME}/transactions") {
val c: Customer = call.authenticateBankRequest(db,
TokenScope.readwrite) ?: throw unauthorized()
@@ -512,7 +524,6 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx:
BankApplicationContext) {
}
return@post
}
-
get("/accounts/{USERNAME}/transactions/{T_ID}") {
val c = call.authenticateBankRequest(db, TokenScope.readonly) ?: throw
unauthorized()
val accountOwner = call.expectUriComponent("USERNAME") // auth ok,
check rights.
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
index 2db84c3c..7d329399 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
@@ -578,6 +578,7 @@ class ChangePw : CliktCommand("Change account password",
name = "passwd") {
if (!db.customerChangePassword(account, CryptoUtil.hashpw(password))) {
println("password change failed")
+ exitProcess(1)
} else {
println("password change succeeded")
}
diff --git a/bank/src/test/kotlin/DatabaseTest.kt
b/bank/src/test/kotlin/DatabaseTest.kt
index 17f1882b..5a1acb3e 100644
--- a/bank/src/test/kotlin/DatabaseTest.kt
+++ b/bank/src/test/kotlin/DatabaseTest.kt
@@ -425,6 +425,17 @@ class DatabaseTest {
assert(db.accountsGetForAdmin("%ar").size == 1) // gets Bar only
}
+ @Test
+ fun passwordChangeTest() {
+ val db = initDb()
+ // foo not found, this fails.
+ assert(!db.customerChangePassword("foo", "won't make it"))
+ // creating foo.
+ assert(db.customerCreate(customerFoo) != null)
+ // foo exists, this succeeds.
+ assert(db.customerChangePassword("foo", CryptoUtil.hashpw("new-pw")))
+ }
+
@Test
fun getPublicAccountsTest() {
val db = initDb()
diff --git a/bank/src/test/kotlin/LibeuFinApiTest.kt
b/bank/src/test/kotlin/LibeuFinApiTest.kt
index 05deba30..9984a72c 100644
--- a/bank/src/test/kotlin/LibeuFinApiTest.kt
+++ b/bank/src/test/kotlin/LibeuFinApiTest.kt
@@ -181,6 +181,40 @@ class LibeuFinApiTest {
}
}
+ @Test
+ fun passwordChangeTest() {
+ val db = initDb()
+ val ctx = getTestContext()
+ assert(db.customerCreate(customerFoo) != null)
+ testApplication {
+ application {
+ corebankWebApp(db, ctx)
+ }
+ // Changing the password.
+ client.patch("/accounts/foo/auth") {
+ expectSuccess = true
+ contentType(ContentType.Application.Json)
+ basicAuth("foo", "pw")
+ setBody("""{"new_password": "bar"}""")
+ }
+ // Previous password should fail.
+ client.patch("/accounts/foo/auth") {
+ expectSuccess = false
+ contentType(ContentType.Application.Json)
+ basicAuth("foo", "pw")
+ setBody("""{"new_password": "not-even-parsed"}""")
+ }.apply {
+ assert(this.status == HttpStatusCode.Unauthorized)
+ }
+ // New password should succeed.
+ client.patch("/accounts/foo/auth") {
+ expectSuccess = true
+ contentType(ContentType.Application.Json)
+ basicAuth("foo", "bar")
+ setBody("""{"new_password": "not-used"}""")
+ }
+ }
+ }
@Test
fun tokenDeletionTest() {
val db = initDb()
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [libeufin] branch master updated: Implementing PATCH /auth,
gnunet <=