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 }