commit c4aac943d0f81327d837584063d8f619d6326257
parent 9b9db0eb58f16fea18f35f221e81a58e90aeca54
Author: Antoine A <>
Date: Fri, 20 Sep 2024 11:05:45 +0200
Merge remote-tracking branch 'origin/master' into dev/antoine/nexus-dev
Diffstat:
7 files changed, 86 insertions(+), 43 deletions(-)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/Database.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/Database.kt
@@ -64,19 +64,19 @@ class Database(dbConfig: DatabaseConfig, internal val bankCurrency: String, inte
flow.emit(creditRow)
}
},
- "outgoing_tx" to {
+ "bank_outgoing_tx" to {
val (account, merchant, debitRow, creditRow) = it.split(' ', limit = 4).map { it.toLong() }
outgoingTxFlows[account]?.run {
flow.emit(debitRow)
}
},
- "incoming_tx" to {
+ "bank_incoming_tx" to {
val (account, row) = it.split(' ', limit = 2).map { it.toLong() }
incomingTxFlows[account]?.run {
flow.emit(row)
}
},
- "withdrawal_status" to {
+ "bank_withdrawal_status" to {
val raw = it.split(' ', limit = 2)
val uuid = UUID.fromString(raw[0])
val status = WithdrawalStatus.valueOf(raw[1])
diff --git a/common/src/main/kotlin/db/notifications.kt b/common/src/main/kotlin/db/notifications.kt
@@ -60,7 +60,11 @@ fun watchNotifications(
conn.getNotifications(0) // Block until we receive at least one notification
.forEach {
// Dispatch
- listeners[it.name]!!(it.parameter)
+ try {
+ listeners[it.name]!!(it.parameter)
+ } catch (e: Exception) {
+ throw Exception("channel ${it.name} with input '${it.parameter}'", e)
+ }
}
}
} catch (e: Exception) {
diff --git a/database-versioning/libeufin-bank-procedures.sql b/database-versioning/libeufin-bank-procedures.sql
@@ -567,7 +567,7 @@ INTO local_amount.val, local_amount.frac, local_bank_account_id
FROM bank_account_transactions WHERE bank_transaction_id=in_debit_row_id;
CALL stats_register_payment('taler_out', NULL, local_amount, null);
-- notify new transaction
-PERFORM pg_notify('outgoing_tx', in_debtor_account_id || ' ' || in_creditor_account_id || ' ' || in_debit_row_id || ' ' || in_credit_row_id);
+PERFORM pg_notify('bank_outgoing_tx', in_debtor_account_id || ' ' || in_creditor_account_id || ' ' || in_debit_row_id || ' ' || in_credit_row_id);
END $$;
COMMENT ON PROCEDURE register_outgoing
IS 'Register a bank transaction as a taler outgoing transaction and announce it';
@@ -605,7 +605,7 @@ IF in_type = 'reserve' THEN
CALL stats_register_payment('taler_in', NULL, local_amount, null);
END IF;
-- Notify new incoming transaction
-PERFORM pg_notify('incoming_tx', local_bank_account_id || ' ' || in_tx_row_id);
+PERFORM pg_notify('bank_incoming_tx', local_bank_account_id || ' ' || in_tx_row_id);
END $$;
COMMENT ON PROCEDURE register_incoming
IS 'Register a bank transaction as a taler incoming transaction and announce it';
@@ -1026,7 +1026,7 @@ IF not_selected THEN
WHERE withdrawal_uuid=in_withdrawal_uuid;
-- Notify status change
- PERFORM pg_notify('withdrawal_status', in_withdrawal_uuid::text || ' selected');
+ PERFORM pg_notify('bank_withdrawal_status', in_withdrawal_uuid::text || ' selected');
END IF;
END $$;
COMMENT ON FUNCTION select_taler_withdrawal IS 'Set details of a withdrawal operation';
@@ -1049,7 +1049,7 @@ IF NOT FOUND OR out_already_confirmed THEN
END IF;
-- Notify status change
-PERFORM pg_notify('withdrawal_status', in_withdrawal_uuid::text || ' aborted');
+PERFORM pg_notify('bank_withdrawal_status', in_withdrawal_uuid::text || ' aborted');
END $$;
COMMENT ON FUNCTION abort_taler_withdrawal IS 'Abort a withdrawal operation.';
@@ -1146,7 +1146,7 @@ UPDATE taler_withdrawal_operations
CALL register_incoming(tx_row_id, 'reserve'::taler_incoming_type, reserve_pub_local, NULL);
-- Notify status change
-PERFORM pg_notify('withdrawal_status', in_withdrawal_uuid::text || ' confirmed');
+PERFORM pg_notify('bank_withdrawal_status', in_withdrawal_uuid::text || ' confirmed');
END $$;
COMMENT ON FUNCTION confirm_taler_withdrawal
IS 'Set a withdrawal operation as confirmed and wire the funds to the exchange.';
diff --git a/database-versioning/libeufin-nexus-procedures.sql b/database-versioning/libeufin-nexus-procedures.sql
@@ -167,7 +167,7 @@ IF NOT out_found THEN
ELSE
INSERT INTO talerable_outgoing_transactions(outgoing_transaction_id, wtid, exchange_base_url)
VALUES (out_tx_id, in_wtid, in_exchange_url);
- PERFORM pg_notify('outgoing_tx', out_tx_id::text);
+ PERFORM pg_notify('nexus_outgoing_tx', out_tx_id::text);
END IF;
END IF;
@@ -295,7 +295,7 @@ ELSE
,in_debit_payto
,in_bank_id
) RETURNING incoming_transaction_id INTO out_tx_id;
- PERFORM pg_notify('revenue_tx', out_tx_id::text);
+ PERFORM pg_notify('nexus_revenue_tx', out_tx_id::text);
END IF;
-- Register as talerable
@@ -312,7 +312,7 @@ IF in_type IS NOT NULL AND NOT EXISTS(SELECT FROM talerable_incoming_transaction
,in_reserve_pub
,in_account_pub
);
- PERFORM pg_notify('incoming_tx', out_tx_id::text);
+ PERFORM pg_notify('nexus_incoming_tx', out_tx_id::text);
END IF;
END $$;
@@ -448,7 +448,7 @@ INSERT INTO transfer_operations(
,in_exchange_base_url
);
out_timestamp = in_timestamp;
-PERFORM pg_notify('outgoing_tx', out_tx_row_id::text);
+PERFORM pg_notify('nexus_outgoing_tx', out_tx_row_id::text);
END $$;
CREATE FUNCTION batch_outgoing_transactions(
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSetup.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSetup.kt
@@ -232,9 +232,15 @@ class EbicsSetup: CliktCommand() {
if (account.name != null && account.name != cfg.account.name)
logger.warn("Expected NAME '${cfg.account.name}' from config got '${account.name}' from bank")
+ logger.debug("User status: ${hkd.status.name} ${hkd.status.description}")
+ logger.debug("Supported orders:")
for (order in hkd.orders) {
logger.debug("${order.type}${order.params}: ${order.description}")
}
+ logger.debug("Authorized orders:")
+ for (order in hkd.permissions) {
+ logger.debug("${order.type}${order.params}")
+ }
}
}
} catch (e: Exception) {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/Database.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/Database.kt
@@ -79,15 +79,15 @@ class Database(dbConfig: DatabaseConfig, val bankCurrency: String): DbPool(dbCon
init {
watchNotifications(pgSource, "libeufin_nexus", LoggerFactory.getLogger("libeufin-nexus-db-watcher"), mapOf(
- "revenue_tx" to {
+ "nexus_revenue_tx" to {
val id = it.toLong()
revenueTxFlows.emit(id)
},
- "outgoing_tx" to {
+ "nexus_outgoing_tx" to {
val id = it.toLong()
outgoingTxFlows.emit(id)
},
- "incoming_tx" to {
+ "nexus_incoming_tx" to {
val id = it.toLong()
incomingTxFlows.emit(id)
}
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsAdministrative.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsAdministrative.kt
@@ -31,7 +31,9 @@ data class VersionNumber(val number: Float, val schema: String) {
data class HKD (
val account: AccountInfo,
- val orders: List<OrderInfo>
+ val orders: List<OrderInfo>,
+ val permissions: List<OrderInfo>,
+ val status: UserStatus
)
data class AccountInfo (
val currency: String?,
@@ -43,6 +45,17 @@ data class OrderInfo (
val params: String,
val description: String,
)
+// TODO use this in ebics setup to get current state and required actions
+enum class UserStatus(val description: String) {
+ Ready("Subscriber is permitted access"),
+ New("Subscriber is established, pending access permission"),
+ INI("Subscriber has sent INI file, but no HIA file yet"),
+ HIA("Subscriber has sent HIA order, but no INI file yet"),
+ Initialised("Subscriber has sent both HIA order and INI file"),
+ SuspendedFailedAttempts("Suspended after several failed attempts, new initialisation via INI and HIA possible"),
+ SuspendedSPR("Suspended after SPR order, new initialisation via INI and HIA possible"),
+ SuspendedBank("Suspended by bank, new initialisation via INI and HIA is not possible, suspension can only be revoked by the bank"),
+}
object EbicsAdministrative {
fun HEV(cfg: NexusEbicsConfig): ByteArray {
@@ -69,8 +82,33 @@ object EbicsAdministrative {
}
fun parseHKD(stream: InputStream): HKD {
+ fun XmlDestructor.orderInfo() = OrderInfo(
+ one("AdminOrderType").text(),
+ opt("Service") {
+ // TODO user a structured type to enable comparison
+ val params = StringBuilder()
+ opt("ServiceName")?.run {
+ params.append(" ${text()}")
+ }
+ opt("Scope")?.run {
+ params.append(" ${text()}")
+ }
+ opt("ServiceOption")?.run {
+ params.append(" ${text()}")
+ }
+ opt("MsgName")?.run {
+ params.append(" ${text()}")
+ }
+ opt("Container")?.run {
+ params.append(" ${attr("containerType")}")
+ }
+ params.toString()
+ } ?: "",
+ opt("Description")?.text() ?: ""
+ )
+ // TODO handle multiple partner, accounts and user using their respective ids
return XmlDestructor.fromStream(stream, "HKDResponseOrderData") {
- one("PartnerInfo") {
+ val (account, orders) = one("PartnerInfo") {
var currency: String? = null
var iban: String? = null
val name = opt("AddressInfo")?.one("Name")?.text()
@@ -82,33 +120,28 @@ object EbicsAdministrative {
}
}
}
- val orders = map("OrderInfo") {
- OrderInfo(
- one("AdminOrderType").text(),
- opt("Service") {
- val params = StringBuilder()
- opt("ServiceName")?.run {
- params.append(" ${text()}")
- }
- opt("Scope")?.run {
- params.append(" ${text()}")
- }
- opt("ServiceOption")?.run {
- params.append(" ${text()}")
- }
- opt("MsgName")?.run {
- params.append(" ${text()}")
- }
- opt("Container")?.run {
- params.append(" ${attr("containerType")}")
- }
- params.toString()
- } ?: "",
- one("Description").text()
- )
+ val orders = map("OrderInfo") { orderInfo() }
+ Pair(AccountInfo(currency, iban, name), orders)
+ }
+ val (permissions, status) = one("UserInfo") {
+ val userId = one("UserID").text()
+ val status = when (val status = one("UserID").attr("Status")) {
+ "1" -> UserStatus.Ready
+ "2" -> UserStatus.New
+ "3" -> UserStatus.INI
+ "4" -> UserStatus.HIA
+ "5" -> UserStatus.Initialised
+ "6" -> UserStatus.SuspendedFailedAttempts
+ // 7 is not applicable per spec
+ "8" -> UserStatus.SuspendedSPR
+ "9" -> UserStatus.SuspendedBank
+ else -> throw Exception("Unknown user statte $status")
}
- HKD(AccountInfo(currency, iban, name), orders)
+ val userName = opt("Name")?.text()
+ val permissions = map("Permission") { orderInfo() }
+ Pair(permissions, status)
}
+ HKD(account, orders, permissions, status)
}
}
}