libeufin

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

config.rs (7263B)


      1 /*
      2 * This file is part of LibEuFin.
      3 * Copyright (C) 2026 Taler Systems S.A.
      4 
      5 * LibEuFin is free software; you can redistribute it and/or modify
      6 * it under the terms of the GNU Affero General Public License as
      7 * published by the Free Software Foundation; either version 3, or
      8 * (at your option) any later version.
      9 
     10 * LibEuFin is distributed in the hope that it will be useful, but
     11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     12 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General
     13 * Public License for more details.
     14 
     15 * You should have received a copy of the GNU Affero General Public
     16 * License along with LibEuFin; see the file COPYING.  If not, see
     17 * <http://www.gnu.org/licenses/>
     18 */
     19 
     20 use std::{cell::OnceCell, time::Duration};
     21 
     22 use compact_str::CompactString;
     23 use jiff::civil::Time;
     24 use libeufin_ebics::{
     25     config::{EbicsHostCfg, EbicsKeysCfg, EbicsSetupCfg},
     26     keys::{BankKeys, ClientKeys},
     27 };
     28 use reqwest::Url;
     29 use taler_api::{
     30     Serve,
     31     config::{AuthCfg, DbCfg},
     32 };
     33 use taler_common::{
     34     config::{CfgErr, Config, Section},
     35     map_config,
     36 };
     37 
     38 pub fn parse_db_cfg(cfg: &Config) -> Result<DbCfg, CfgErr> {
     39     DbCfg::parse(cfg.section("ebisyncdb-postgres"))
     40 }
     41 
     42 pub struct EbisyncSetupCfg {
     43     pub enc: Option<Vec<u8>>,
     44     pub auth: Option<Vec<u8>>,
     45 }
     46 
     47 impl EbisyncSetupCfg {
     48     pub fn parse(cfg: &Config) -> Result<Self, CfgErr> {
     49         let s = cfg.section("ebisync-setup");
     50         Ok(Self {
     51             enc: s.hex("bank_encryption_pub_key_hash").opt()?,
     52             auth: s.hex("bank_authentication_pub_key_hash").opt()?,
     53         })
     54     }
     55 }
     56 
     57 pub enum Destination {
     58     None,
     59     AzureBlobStorage {
     60         api: Url,
     61         name: String,
     62         key: String,
     63         container: String,
     64     },
     65 }
     66 
     67 pub enum Source {
     68     None,
     69     SyncAPI(AuthCfg),
     70 }
     71 
     72 pub struct EbisyncFetchCfg {
     73     pub frequency: Duration,
     74     pub frequency_raw: String,
     75     pub checkpoint_time: Time,
     76     pub destination: Destination,
     77 }
     78 
     79 impl EbisyncFetchCfg {
     80     pub fn parse(cfg: &Config) -> Result<Self, CfgErr> {
     81         let s = cfg.section("ebisync-fetch");
     82         Ok(Self {
     83             frequency: s.duration("frequency").require()?,
     84             frequency_raw: s.str("frequency").require()?,
     85             checkpoint_time: s.time("checkpoint_time_of_day").require()?,
     86             destination: map_config!(s, "ebics file destination", "destination",
     87                 "none" => { Destination::None },
     88                 "azure-blob-storage" => {
     89                     Destination::AzureBlobStorage { api:  s.base_url("azure_api_url").require()?, name: s.str("azure_account_name").require()?, key: s.str("azure_account_key").require()?, container: s.str("azure_container").require()? }
     90                 }
     91             ).require()?
     92         })
     93     }
     94 }
     95 
     96 pub struct EbisyncSubmitCfg {
     97     pub source: Source,
     98 }
     99 
    100 impl EbisyncSubmitCfg {
    101     pub fn parse(cfg: &Config) -> Result<Self, CfgErr> {
    102         let s = cfg.section("ebisync-submit");
    103         Ok(Self {
    104             source: map_config!(s, "ebics file source", "source",
    105                 "none" => { Source::None },
    106                 "ebisync-api" => { Source::SyncAPI(AuthCfg::parse(&s)?) }
    107             )
    108             .require()?,
    109         })
    110     }
    111 }
    112 
    113 pub struct EbisyncServeCfg {
    114     pub serve: Serve,
    115     pub spa_path: String,
    116 }
    117 
    118 impl EbisyncServeCfg {
    119     pub fn parse(cfg: &Config) -> Result<Self, CfgErr> {
    120         let s = cfg.section("ebisync-httpd");
    121         Ok(Self {
    122             serve: Serve::parse(&s)?,
    123             spa_path: s.path("spa").require()?,
    124         })
    125     }
    126 }
    127 
    128 #[derive(Clone)]
    129 pub struct EbisyncHostCfg {
    130     pub base_url: Url,
    131     pub unix_path: Option<String>,
    132     pub host_id: CompactString,
    133     pub user_id: CompactString,
    134     pub partner_id: CompactString,
    135 }
    136 
    137 impl EbisyncHostCfg {
    138     pub fn parse(s: &Section) -> Result<Self, CfgErr> {
    139         Ok(Self {
    140             base_url: s.url("host_base_url").require()?,
    141             unix_path: s.path("UNIXPATH").opt()?,
    142             host_id: s.cstr("host_id").require()?,
    143             user_id: s.cstr("user_id").require()?,
    144             partner_id: s.cstr("partner_id").require()?,
    145         })
    146     }
    147 
    148     pub fn ebics<'a>(&'a self) -> EbicsHostCfg<'a> {
    149         EbicsHostCfg {
    150             base_url: self.base_url.as_str(),
    151             unix_path: self.unix_path.as_deref(),
    152             host_id: &self.host_id,
    153             user_id: &self.user_id,
    154             partner_id: &self.partner_id,
    155         }
    156     }
    157 }
    158 
    159 pub struct EbisyncCfg<'a> {
    160     pub bank: String,
    161     pub client: String,
    162     pub host: EbisyncHostCfg,
    163     pub fetch: OnceCell<EbisyncFetchCfg>,
    164     pub submit: OnceCell<EbisyncSubmitCfg>,
    165     pub setup: OnceCell<EbisyncSetupCfg>,
    166     pub serve: OnceCell<EbisyncServeCfg>,
    167     pub db_cfg: DbCfg,
    168     pub cfg: &'a Config,
    169 }
    170 
    171 impl<'a> EbisyncCfg<'a> {
    172     pub fn parse(cfg: &'a Config) -> Result<Self, CfgErr> {
    173         let s = cfg.section("ebisync");
    174         Ok(Self {
    175             host: EbisyncHostCfg::parse(&s)?,
    176             bank: s.path("bank_public_keys_file").require()?,
    177             client: s.path("client_private_keys_file").require()?,
    178             fetch: OnceCell::new(),
    179             submit: OnceCell::new(),
    180             setup: OnceCell::new(),
    181             serve: OnceCell::new(),
    182             db_cfg: parse_db_cfg(cfg)?,
    183             cfg,
    184         })
    185     }
    186 
    187     pub fn fetch(&self) -> Result<&EbisyncFetchCfg, CfgErr> {
    188         // TODO use get_or_try_init when stable
    189         if let Some(fetch) = self.fetch.get() {
    190             return Ok(fetch);
    191         }
    192         let fetch = EbisyncFetchCfg::parse(self.cfg)?;
    193         self.fetch.set(fetch).ok();
    194         Ok(self.fetch.get().unwrap())
    195     }
    196 
    197     pub fn submit(&self) -> Result<&EbisyncSubmitCfg, CfgErr> {
    198         // TODO use get_or_try_init when stable
    199         if let Some(submit) = self.submit.get() {
    200             return Ok(submit);
    201         }
    202         let submit = EbisyncSubmitCfg::parse(self.cfg)?;
    203         self.submit.set(submit).ok();
    204         Ok(self.submit.get().unwrap())
    205     }
    206 
    207     pub fn serve(&self) -> Result<&EbisyncServeCfg, CfgErr> {
    208         // TODO use get_or_try_init when stable
    209         if let Some(serve) = self.serve.get() {
    210             return Ok(serve);
    211         }
    212         let serve = EbisyncServeCfg::parse(self.cfg)?;
    213         self.serve.set(serve).ok();
    214         Ok(self.serve.get().unwrap())
    215     }
    216 
    217     pub fn ebics_setup(&'a self) -> Result<EbicsSetupCfg<'a>, CfgErr> {
    218         let setup = {
    219             if let Some(setup) = self.setup.get() {
    220                 setup
    221             } else {
    222                 let setup = EbisyncSetupCfg::parse(self.cfg)?;
    223                 self.setup.set(setup).ok();
    224                 self.setup.get().unwrap()
    225             }
    226         };
    227         Ok(EbicsSetupCfg {
    228             keys: EbicsKeysCfg {
    229                 bank: &self.bank,
    230                 client: &self.client,
    231             },
    232             host: self.host.ebics(),
    233             enc: setup.enc.as_deref(),
    234             auth: setup.auth.as_deref(),
    235         })
    236     }
    237 
    238     pub fn expect_full_keys(&self) -> Result<(ClientKeys, BankKeys), CfgErr> {
    239         libeufin_ebics::keys::expect_full_keys(
    240             &EbicsKeysCfg {
    241                 bank: &self.bank,
    242                 client: &self.client,
    243             },
    244             "libeufin-ebisync setup",
    245         )
    246     }
    247 }