taler-rust

GNU Taler code in Rust. Largely core banking integrations.
Log | Files | Refs | Submodules | README | LICENSE

commit 29b5207e9f46e9d91cce0002cb206c7af79ece35
parent bac023df91fa79225f5e622ba074000be812f3ae
Author: Antoine A <>
Date:   Thu, 19 Jun 2025 12:02:27 +0200

common: more common config logic

Diffstat:
MCargo.lock | 1+
MCargo.toml | 1+
Mcommon/taler-api/Cargo.toml | 1+
Acommon/taler-api/src/config.rs | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcommon/taler-api/src/lib.rs | 1+
Mtaler-magnet-bank/Cargo.toml | 2+-
Mtaler-magnet-bank/src/config.rs | 71++++++++---------------------------------------------------------------
Mtaler-magnet-bank/src/main.rs | 22+++++++++++++---------
8 files changed, 116 insertions(+), 73 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -2221,6 +2221,7 @@ name = "taler-api" version = "0.0.0" dependencies = [ "axum", + "base64", "criterion", "dashmap", "ed25519-dalek", diff --git a/Cargo.toml b/Cargo.toml @@ -44,3 +44,4 @@ taler-test-utils = { path = "common/taler-test-utils" } anyhow = "1" http-body-util = "0.1.2" libdeflater = "1.22.0" +base64 = "0.22" diff --git a/common/taler-api/Cargo.toml b/common/taler-api/Cargo.toml @@ -10,6 +10,7 @@ license-file.workspace = true [dependencies] listenfd = "1.0.0" dashmap = "6.1" +base64.workspace = true http-body-util.workspace = true libdeflater.workspace = true ed25519-dalek = { version = "2.1.1", default-features = false } diff --git a/common/taler-api/src/config.rs b/common/taler-api/src/config.rs @@ -0,0 +1,90 @@ +/* + This file is part of TALER + Copyright (C) 2025 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ + +use std::net::{IpAddr, SocketAddr}; + +use base64::{Engine, prelude::BASE64_STANDARD}; +use sqlx::postgres::PgConnectOptions; +use taler_common::{ + config::{Section, ValueErr}, + map_config, +}; + +use crate::{Serve, auth::AuthMethod}; + +/// Basic database config +pub struct DbCfg { + pub cfg: PgConnectOptions, + pub sql_dir: String, +} + +impl DbCfg { + pub fn parse(sect: Section) -> Result<Self, ValueErr> { + Ok(Self { + cfg: sect.postgres("CONFIG").require()?, + sql_dir: sect.path("SQL_DIR").require()?, + }) + } +} + +/// Basic api config +pub struct ApiCfg { + pub auth: AuthMethod, +} + +impl ApiCfg { + pub fn parse(sect: Section) -> Result<Option<Self>, ValueErr> { + Ok(if sect.boolean("ENABLED").require()? { + let auth = map_config!(sect, "auth_method", "AUTH_METHOD", + "none" => { + Ok(AuthMethod::None) + }, + "basic" => { + let username = sect.str("USERNAME").require()?; + let password = sect.str("PASSWORD").require()?; + Ok(AuthMethod::Basic( + BASE64_STANDARD.encode(format!("{username}:{password}")), + )) + }, + "bearer" => { + Ok(AuthMethod::Bearer(sect.str("AUTH_TOKEN").require()?)) + } + ) + .require()?; + Some(Self { auth }) + } else { + None + }) + } +} + +impl Serve { + pub fn parse(sect: Section) -> Result<Self, ValueErr> { + map_config!(sect, "serve", "SERVE", + "tcp" => { + let port = sect.number("PORT").require()?; + let ip: IpAddr = sect.parse("IP addr", "BIND_TO").require()?; + Ok::<Serve, ValueErr>(Serve::Tcp(SocketAddr::new(ip, port))) + }, + "unix" => { + let path = sect.path("UNIXPATH").require()?; + let permission = sect.unix_mode("UNIXPATH_MODE").require()?; + Ok::<Serve, ValueErr>(Serve::Unix { path, permission }) + } + ) + .require() + } +} diff --git a/common/taler-api/src/lib.rs b/common/taler-api/src/lib.rs @@ -22,6 +22,7 @@ use tracing::info; pub mod api; pub mod auth; +pub mod config; pub mod constants; pub mod db; pub mod error; diff --git a/taler-magnet-bank/Cargo.toml b/taler-magnet-bank/Cargo.toml @@ -18,7 +18,6 @@ hmac = "0.12" sha1 = "0.10" p256 = { version = "0.13.2", features = ["alloc", "ecdsa"] } spki = "0.7.3" -base64 = "0.22" form_urlencoded = "1.2" percent-encoding = "2.3" passterm = "2.0" @@ -35,6 +34,7 @@ thiserror.workspace = true tracing.workspace = true tokio.workspace = true anyhow.workspace = true +base64.workspace = true [dev-dependencies] taler-test-utils.workspace = true diff --git a/taler-magnet-bank/src/config.rs b/taler-magnet-bank/src/config.rs @@ -14,34 +14,21 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -use std::net::{IpAddr, SocketAddr}; - -use base64::{Engine, prelude::BASE64_STANDARD}; use reqwest::Url; -use sqlx::postgres::PgConnectOptions; -use taler_api::{Serve, auth::AuthMethod}; +use taler_api::{ + Serve, + config::{ApiCfg, DbCfg}, +}; use taler_common::{ - config::{Config, Section, ValueErr, map_config}, + config::{Config, ValueErr}, types::payto::PaytoURI, }; use crate::{FullHuPayto, HuIban, magnet::Token}; -pub struct DbCfg { - pub cfg: PgConnectOptions, - pub sql_dir: String, -} - -impl DbCfg { - pub fn parse(cfg: &Config) -> Result<Self, ValueErr> { - let sect = cfg.section("magnet-bankdb-postgres"); - Ok(Self { - cfg: sect.postgres("CONFIG").require()?, - sql_dir: sect.path("SQL_DIR").require()?, - }) - } +pub fn parse_db_cfg(cfg: &Config) -> Result<DbCfg, ValueErr> { + DbCfg::parse(cfg.section("magnet-bankdb-postgres")) } - pub fn parse_account_payto(cfg: &Config) -> Result<FullHuPayto, ValueErr> { let sect = cfg.section("magnet-bank"); let iban: HuIban = sect.parse("iban", "IBAN").require()?; @@ -50,36 +37,6 @@ pub fn parse_account_payto(cfg: &Config) -> Result<FullHuPayto, ValueErr> { Ok(FullHuPayto::new(iban, name)) } -pub struct ApiCfg { - pub auth: AuthMethod, -} - -impl ApiCfg { - pub fn parse(sect: Section) -> Result<Option<Self>, ValueErr> { - Ok(if sect.boolean("ENABLED").require()? { - let auth = map_config!(sect, "auth_method", "AUTH_METHOD", - "none" => { - Ok(AuthMethod::None) - }, - "basic" => { - let username = sect.str("USERNAME").require()?; - let password = sect.str("PASSWORD").require()?; - Ok(AuthMethod::Basic( - BASE64_STANDARD.encode(format!("{username}:{password}")), - )) - }, - "bearer" => { - Ok(AuthMethod::Bearer(sect.str("AUTH_TOKEN").require()?)) - } - ) - .require()?; - Some(Self { auth }) - } else { - None - }) - } -} - pub struct ServeCfg { pub payto: PaytoURI, pub serve: Serve, @@ -93,19 +50,7 @@ impl ServeCfg { let sect = cfg.section("magnet-bank-httpd"); - let serve = map_config!(sect, "serve", "SERVE", - "tcp" => { - let port = sect.number("PORT").require()?; - let ip: IpAddr = sect.parse("IP addr", "BIND_TO").require()?; - Ok::<Serve, ValueErr>(Serve::Tcp(SocketAddr::new(ip, port))) - }, - "unix" => { - let path = sect.path("UNIXPATH").require()?; - let permission = sect.unix_mode("UNIXPATH_MODE").require()?; - Ok::<Serve, ValueErr>(Serve::Unix { path, permission }) - } - ) - .require()?; + let serve = Serve::parse(sect)?; let wire_gateway = ApiCfg::parse(cfg.section("magnet-bank-httpd-wire-gateway-api"))?; let revenue = ApiCfg::parse(cfg.section("magnet-bank-httpd-revenue-api"))?; diff --git a/taler-magnet-bank/src/main.rs b/taler-magnet-bank/src/main.rs @@ -19,11 +19,15 @@ use std::sync::Arc; use clap::Parser; use taler_api::api::{Router, TalerRouter as _}; use taler_common::{ - cli::{long_version, ConfigCmd}, config::{parser::ConfigSource, Config}, db::{dbinit, pool}, taler_main, CommonArgs + CommonArgs, + cli::{ConfigCmd, long_version}, + config::{Config, parser::ConfigSource}, + db::{dbinit, pool}, + taler_main, }; use taler_magnet_bank::{ adapter::MagnetApi, - config::{DbCfg, ServeCfg, WorkerCfg}, + config::{ServeCfg, WorkerCfg, parse_db_cfg}, dev::{self, DevCmd}, magnet::AuthClient, setup, @@ -76,16 +80,16 @@ enum Command { async fn app(args: Args, cfg: Config) -> anyhow::Result<()> { match args.cmd { - Command::Setup { reset } => { - let cfg = WorkerCfg::parse(&cfg)?; - setup::setup(cfg, reset).await? - } Command::Dbinit { reset } => { - let cfg = DbCfg::parse(&cfg)?; + let cfg = parse_db_cfg(&cfg)?; let pool = pool(cfg.cfg, "magnet_bank").await?; let mut conn = pool.acquire().await?; dbinit(&mut conn, cfg.sql_dir.as_ref(), "magnet-bank", reset).await?; } + Command::Setup { reset } => { + let cfg = WorkerCfg::parse(&cfg)?; + setup::setup(cfg, reset).await? + } Command::Serve { check } => { if check { let cfg = ServeCfg::parse(&cfg)?; @@ -93,7 +97,7 @@ async fn app(args: Args, cfg: Config) -> anyhow::Result<()> { std::process::exit(1); } } else { - let db = DbCfg::parse(&cfg)?; + let db = parse_db_cfg(&cfg)?; let pool = pool(db.cfg, "magnet_bank").await?; let cfg = ServeCfg::parse(&cfg)?; let api = Arc::new(MagnetApi::start(pool, cfg.payto).await); @@ -108,7 +112,7 @@ async fn app(args: Args, cfg: Config) -> anyhow::Result<()> { } } Command::Worker { transient: _ } => { - let db = DbCfg::parse(&cfg)?; + let db = parse_db_cfg(&cfg)?; let pool = pool(db.cfg, "magnet_bank").await?; let cfg = WorkerCfg::parse(&cfg)?; let keys = setup::load(&cfg)?;