taldir

Directory service to resolve wallet mailboxes by messenger addresses
Log | Files | Refs | Submodules | README | LICENSE

commit de58e42f472aa2cdf915c17dae43c0604ed54d29
parent 0478a7a1d81fcf63168dd53b46e52ebb87c42269
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Fri, 25 Apr 2025 22:00:19 +0200

refactoring for testing

Diffstat:
Mcmd/taldir-server/main.go | 26+++++++++++++++++++++++---
Mcmd/taldir-server/main_test.go | 27+++++++++++++++++++++------
Mgo.mod | 10++++------
Mpkg/rest/taldir.go | 114++++++++++++++++++++++++++++++++++++++++---------------------------------------
4 files changed, 106 insertions(+), 71 deletions(-)

diff --git a/cmd/taldir-server/main.go b/cmd/taldir-server/main.go @@ -28,10 +28,13 @@ package main import ( "flag" + "fmt" "log" "net/http" "os" + "gopkg.in/ini.v1" + "gorm.io/driver/postgres" taldir "taler.net/taldir/pkg/rest" ) @@ -42,7 +45,7 @@ var ( ) func handleRequests(t *taldir.Taldir) { - log.Fatal(http.ListenAndServe(t.Cfg.Section("taldir").Key("bind_to").MustString("localhost:11000"), t.Router)) + log.Fatal(http.ListenAndServe(t.Cfg.Ini.Section("taldir").Key("bind_to").MustString("localhost:11000"), t.Router)) } func main() { @@ -55,7 +58,24 @@ func main() { cfgfile = *cfgFlag } t := taldir.Taldir{} - t.Initialize(cfgfile, version, taldirdatahome) - t.Cfg.WriteTo(os.Stdout) + cfg, err := ini.LooseLoad(cfgfile) + if err != nil { + log.Fatalf("Failed to read config: %v", err) + os.Exit(1) + } + psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", + cfg.Section("taldir-pq").Key("host").MustString("localhost"), + cfg.Section("taldir-pq").Key("port").MustInt64(5432), + cfg.Section("taldir-pq").Key("user").MustString("taldir"), + cfg.Section("taldir-pq").Key("password").MustString("secret"), + cfg.Section("taldir-pq").Key("db_name").MustString("taldir")) + db := postgres.Open(psqlconn) + cfg.WriteTo(os.Stdout) + t.Initialize(taldir.TaldirConfig{ + Ini: cfg, + Version: version, + Datahome: taldirdatahome, + Db: db, + }) handleRequests(&t) } diff --git a/cmd/taldir-server/main_test.go b/cmd/taldir-server/main_test.go @@ -21,7 +21,8 @@ package main_test import ( "bytes" "crypto/sha512" - "io/ioutil" + "io" + "log" "net/http" "net/http/httptest" "os" @@ -31,6 +32,8 @@ import ( gnunetutil "gnunet/util" "github.com/jarcoal/httpmock" + "gopkg.in/ini.v1" + "gorm.io/driver/sqlite" _ "taler.net/taldir/cmd/taldir-server" "taler.net/taldir/internal/util" taldir "taler.net/taldir/pkg/rest" @@ -72,7 +75,19 @@ var newOrderStatusUnpaidMockResponse = ` ` func TestMain(m *testing.M) { - t.Initialize("testdata/taldir-test.conf") + cfg, err := ini.LooseLoad("testdata/taldir-test.conf") + if err != nil { + log.Fatalf("Failed to read config: %v", err) + os.Exit(1) + } + db := sqlite.Open("file::memory:?cache=shared") + t.Initialize(taldir.TaldirConfig{ + Ini: cfg, + Version: "testing", + Datahome: "../../", + Db: db, + }) + log.Println("hello") code := m.Run() t.ClearDatabase() os.Exit(code) @@ -115,7 +130,7 @@ func TestRegisterRequest(s *testing.T) { if err != nil { s.Errorf("No validation code file found!\n") } - code, err := ioutil.ReadAll(file) + code, err := io.ReadAll(file) if err != nil { s.Errorf("Error reading validation code file contents!\n") } @@ -149,7 +164,7 @@ func TestRegisterQRPageRequest(s *testing.T) { if err != nil { s.Errorf("No validation code file found!\n") } - code, err := ioutil.ReadAll(file) + code, err := io.ReadAll(file) if err != nil { s.Errorf("Error reading validation code file contents!\n") } @@ -175,7 +190,7 @@ func TestReRegisterRequest(s *testing.T) { if err != nil { s.Errorf("No validation code file found!\n") } - code, err := ioutil.ReadAll(file) + code, err := io.ReadAll(file) if err != nil { s.Errorf("Error reading validation code file contents!\n") } @@ -275,7 +290,7 @@ func TestRegisterRequestWrongPubkey(s *testing.T) { if err != nil { s.Errorf("No validation code file found!\n") } - code, err := ioutil.ReadAll(file) + code, err := io.ReadAll(file) if err != nil { s.Errorf("Error reading validation code file contents!\n") } diff --git a/go.mod b/go.mod @@ -20,23 +20,21 @@ require ( require ( github.com/BurntSushi/toml v1.5.0 // indirect github.com/bfix/gospel v1.2.31 // indirect - github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgconn v1.14.3 // indirect - github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgproto3/v2 v2.3.3 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/pgtype v1.14.4 // indirect - github.com/jackc/pgx/v4 v4.18.3 // indirect github.com/jackc/pgx/v5 v5.7.4 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/kr/text v0.1.0 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect golang.org/x/crypto v0.37.0 // indirect golang.org/x/net v0.39.0 // indirect golang.org/x/sync v0.13.0 // indirect golang.org/x/text v0.24.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/driver/sqlite v1.5.7 // indirect ) replace gnunet v0.1.27 => ./third_party/gnunet-go/src/gnunet diff --git a/pkg/rest/taldir.go b/pkg/rest/taldir.go @@ -49,13 +49,29 @@ import ( talerutil "github.com/schanzen/taler-go/pkg/util" "github.com/skip2/go-qrcode" "gopkg.in/ini.v1" - "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" "taler.net/taldir/internal/gana" "taler.net/taldir/internal/util" ) +type TaldirConfig struct { + // The config to use + Ini *ini.File + + // The Taldir Version + Version string + + // The Taldir data home location (usually $datadir/taldir) + Datahome string + + // The database connection to use + Db gorm.Dialector + + // The merchant connection to use + Merchant merchant.Merchant +} + // Taldir is the primary object of the Taldir service type Taldir struct { @@ -66,10 +82,7 @@ type Taldir struct { Db *gorm.DB // Our configuration from the config.json - Cfg *ini.File - - // TalDir version - Version string + Cfg TaldirConfig // Map of supported validators as defined in the configuration Validators map[string]Validator @@ -645,7 +658,7 @@ func (t *Taldir) configResponse(w http.ResponseWriter, r *http.Request) { cfg := VersionResponse{ Version: "0:0:0", Name: "taler-directory", - MonthlyFee: t.Cfg.Section("taldir").Key("monthly_fee").MustString("KUDOS:1"), + MonthlyFee: t.Cfg.Ini.Section("taldir").Key("monthly_fee").MustString("KUDOS:1"), Methods: meths, } w.Header().Set("Content-Type", "application/json") @@ -703,7 +716,7 @@ func (t *Taldir) validationPage(w http.ResponseWriter, r *http.Request) { encodedPng := base64.StdEncoding.EncodeToString(png) fullData := map[string]interface{}{ - "version": t.Version, + "version": t.Cfg.Version, "QRCode": template.URL("data:image/png;base64," + encodedPng), "WalletLink": template.URL(walletLink), "productDisclaimerShort": template.HTML(t.I18n.GetLocale(r).GetMessage("productDisclaimerShort")), @@ -718,7 +731,7 @@ func (t *Taldir) validationPage(w http.ResponseWriter, r *http.Request) { confirmDeletionOrRegistration = t.I18n.GetLocale(r).GetMessage("confirmReg", address, validation.TargetUri) } fullData := map[string]interface{}{ - "version": t.Version, + "version": t.Cfg.Version, "error": r.URL.Query().Get("error"), "target_uri": template.URL(validation.TargetUri), "address": template.URL(address), @@ -740,11 +753,11 @@ func (t *Taldir) ClearDatabase() { } func (t *Taldir) termsResponse(w http.ResponseWriter, r *http.Request) { - tos.ServiceTermsResponse(t.Cfg.Section("taldir"), w, r) + tos.ServiceTermsResponse(t.Cfg.Ini.Section("taldir"), w, r) } func (t *Taldir) privacyResponse(w http.ResponseWriter, r *http.Request) { - tos.PrivacyPolicyResponse(t.Cfg.Section("taldir"), w, r) + tos.PrivacyPolicyResponse(t.Cfg.Ini.Section("taldir"), w, r) } func (t *Taldir) landingPage(w http.ResponseWriter, r *http.Request) { @@ -752,7 +765,7 @@ func (t *Taldir) landingPage(w http.ResponseWriter, r *http.Request) { translateFunc := t.I18n.GetLocale(r).GetMessage fullData := map[string]interface{}{ "validators": t.Validators, - "version": t.Version, + "version": t.Cfg.Version, "lookupOrRegisterCardTitle": template.HTML(translateFunc("lookup")), "selectAliasToLookupCardText": template.HTML(translateFunc("selectAliasToLookup")), "registerCardText": template.HTML(translateFunc("howtoRegisterOrModify")), @@ -772,7 +785,7 @@ func (t *Taldir) imprintPage(w http.ResponseWriter, r *http.Request) { translateFunc := t.I18n.GetLocale(r).GetMessage fullData := map[string]interface{}{ "validators": t.Validators, - "version": t.Version, + "version": t.Cfg.Version, "productDisclaimerShort": template.HTML(translateFunc("productDisclaimerShort")), "error": translateFunc(r.URL.Query().Get("error")), "tr": translateFunc, @@ -789,7 +802,7 @@ func (t *Taldir) aboutPage(w http.ResponseWriter, r *http.Request) { translateFunc := t.I18n.GetLocale(r).GetMessage fullData := map[string]interface{}{ "validators": t.Validators, - "version": t.Version, + "version": t.Cfg.Version, "productDisclaimerShort": template.HTML(translateFunc("productDisclaimerShort")), "productDisclaimer": template.HTML(translateFunc("productDisclaimer")), "error": translateFunc(r.URL.Query().Get("error")), @@ -836,7 +849,7 @@ func (t *Taldir) methodLookupResultPage(w http.ResponseWriter, r *http.Request) } } fullData := map[string]interface{}{ - "version": t.Version, + "version": t.Cfg.Version, "available": !found, "method": val.Name, "address": r.URL.Query().Get("address"), @@ -863,7 +876,7 @@ func (t *Taldir) methodLandingPage(w http.ResponseWriter, r *http.Request) { return } fullData := map[string]interface{}{ - "version": t.Version, + "version": t.Cfg.Version, "error": r.URL.Query().Get("error"), "productDisclaimerShort": template.HTML(t.I18n.GetLocale(r).GetMessage("productDisclaimerShort")), "tr": t.I18n.GetLocale(r).GetMessage, @@ -928,31 +941,26 @@ func (t *Taldir) getFileName(relativeFileName string, datahome string) string { } // Initialize the Taldir instance with cfgfile -func (t *Taldir) Initialize(cfgfile string, version string, datahome string) { - _cfg, err := ini.LooseLoad(cfgfile) - if err != nil { - log.Fatalf("Failed to read config: %v", err) - os.Exit(1) - } - t.Cfg = _cfg +func (t *Taldir) Initialize(cfg TaldirConfig) { + t.Cfg = cfg // FIXME localedir - t.I18n, err = i18n.New(i18n.Glob("./locales/*/*", i18n.LoaderConfig{ + i18n, err := i18n.New(i18n.Glob("./locales/*/*", i18n.LoaderConfig{ // Set custom functions per locale! Funcs: getFuncs, }), "en-US", "de-DE") if err != nil { panic(err) } - if t.Cfg.Section("taldir").Key("production").MustBool(false) { + t.I18n = i18n + if t.Cfg.Ini.Section("taldir").Key("production").MustBool(false) { fmt.Println("Production mode enabled") } - navTplFile := t.Cfg.Section("taldir").Key("navigation").MustString(t.getFileName("web/templates/nav.html", datahome)) - footerTplFile := t.Cfg.Section("taldir").Key("footer").MustString(t.getFileName("web/templates/footer.html", datahome)) - t.Version = version - t.BaseUrl = t.Cfg.Section("taldir").Key("base_url").MustString("http://localhost:11000") + navTplFile := cfg.Ini.Section("taldir").Key("navigation").MustString(t.getFileName("web/templates/nav.html", cfg.Datahome)) + footerTplFile := cfg.Ini.Section("taldir").Key("footer").MustString(t.getFileName("web/templates/footer.html", cfg.Datahome)) + t.BaseUrl = cfg.Ini.Section("taldir").Key("base_url").MustString("http://localhost:11000") t.Validators = make(map[string]Validator) - for _, sec := range t.Cfg.Sections() { + for _, sec := range cfg.Ini.Sections() { if !strings.HasPrefix(sec.Name(), "taldir-validator-") { continue } @@ -961,7 +969,7 @@ func (t *Taldir) Initialize(cfgfile string, version string, datahome string) { continue } vname := strings.TrimPrefix(sec.Name(), "taldir-validator-") - vlandingPageTplFile := sec.Key("registration_page").MustString(t.getFileName("web/templates/landing_"+vname+".html", datahome)) + vlandingPageTplFile := sec.Key("registration_page").MustString(t.getFileName("web/templates/landing_"+vname+".html", cfg.Datahome)) vlandingPageTpl, err := template.ParseFiles(vlandingPageTplFile, navTplFile, footerTplFile) if err != nil { log.Printf("`%s` template not found, disabling validator `%s`.\n", vlandingPageTplFile, vname) @@ -977,33 +985,27 @@ func (t *Taldir) Initialize(cfgfile string, version string, datahome string) { ValidAliasRegex: sec.Key("valid_alias_regex").MustString(""), } } - t.ChallengeBytes = t.Cfg.Section("taldir").Key("challenge_bytes").MustInt(16) - t.ValidationInitiationMax = t.Cfg.Section("taldir").Key("validation_initiation_max").MustInt64(3) - t.SolutionAttemptsMax = t.Cfg.Section("taldir").Key("solution_attempt_max").MustInt(3) + t.ChallengeBytes = cfg.Ini.Section("taldir").Key("challenge_bytes").MustInt(16) + t.ValidationInitiationMax = cfg.Ini.Section("taldir").Key("validation_initiation_max").MustInt64(3) + t.SolutionAttemptsMax = cfg.Ini.Section("taldir").Key("solution_attempt_max").MustInt(3) - validationTTLStr := t.Cfg.Section("taldir").Key("validation_timeframe").MustString("5m") - t.ValidPMSRegex = t.Cfg.Section("taldir").Key("valid_payment_system_address_regex").MustString("[A-Z]+") + validationTTLStr := cfg.Ini.Section("taldir").Key("validation_timeframe").MustString("5m") + t.ValidPMSRegex = cfg.Ini.Section("taldir").Key("valid_payment_system_address_regex").MustString("[A-Z]+") t.ValidationTimeframe, err = time.ParseDuration(validationTTLStr) if err != nil { log.Fatal(err) os.Exit(1) } - retryTimeframeStr := t.Cfg.Section("taldir").Key("solution_attempt_timeframe").MustString("1h") + retryTimeframeStr := cfg.Ini.Section("taldir").Key("solution_attempt_timeframe").MustString("1h") t.SolutionTimeframe, err = time.ParseDuration(retryTimeframeStr) if err != nil { log.Fatal(err) os.Exit(1) } - t.MonthlyFee = t.Cfg.Section("taldir").Key("monthly_fee").MustString("KUDOS:0") - - psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", - t.Cfg.Section("taldir-pq").Key("host").MustString("localhost"), - t.Cfg.Section("taldir-pq").Key("port").MustInt64(5432), - t.Cfg.Section("taldir-pq").Key("user").MustString("taldir"), - t.Cfg.Section("taldir-pq").Key("password").MustString("secret"), - t.Cfg.Section("taldir-pq").Key("db_name").MustString("taldir")) - _db, err := gorm.Open(postgres.Open(psqlconn), &gorm.Config{ + t.MonthlyFee = cfg.Ini.Section("taldir").Key("monthly_fee").MustString("KUDOS:0") + + _db, err := gorm.Open(cfg.Db, &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { @@ -1016,60 +1018,60 @@ func (t *Taldir) Initialize(cfgfile string, version string, datahome string) { if err := t.Db.AutoMigrate(&Validation{}); err != nil { panic(err) } - if t.Cfg.Section("taldir").Key("purge_mappings_on_startup_dangerous").MustBool(false) { + if cfg.Ini.Section("taldir").Key("purge_mappings_on_startup_dangerous").MustBool(false) { log.Println("DANGER Purging mappings!") tx := t.Db.Where("1 = 1").Delete(&Entry{}) log.Printf("Deleted %d entries.\n", tx.RowsAffected) } // Clean up validations - validationExpStr := t.Cfg.Section("taldir").Key("validation_expiration").MustString("24h") + validationExpStr := cfg.Ini.Section("taldir").Key("validation_expiration").MustString("24h") validationExp, err := time.ParseDuration(validationExpStr) if err != nil { log.Fatal(err) os.Exit(1) } go func() { - for true { + for { tx := t.Db.Where("created_at < ?", time.Now().Add(-validationExp)).Delete(&Validation{}) log.Printf("Cleaned up %d stale validations.\n", tx.RowsAffected) time.Sleep(validationExp) } }() - imprintTplFile := t.Cfg.Section("taldir").Key("imprint_page").MustString(t.getFileName("web/templates/imprint.html", datahome)) + imprintTplFile := cfg.Ini.Section("taldir").Key("imprint_page").MustString(t.getFileName("web/templates/imprint.html", cfg.Datahome)) t.ImprintTpl, err = template.ParseFiles(imprintTplFile, navTplFile, footerTplFile) if err != nil { log.Fatal(err) os.Exit(1) } - validationLandingTplFile := t.Cfg.Section("taldir").Key("validation_landing").MustString(t.getFileName("web/templates/validation_landing.html", datahome)) + validationLandingTplFile := cfg.Ini.Section("taldir").Key("validation_landing").MustString(t.getFileName("web/templates/validation_landing.html", cfg.Datahome)) t.ValidationTpl, err = template.ParseFiles(validationLandingTplFile, navTplFile, footerTplFile) if err != nil { log.Fatal(err) os.Exit(1) } - landingTplFile := t.Cfg.Section("taldir").Key("landing_page").MustString(t.getFileName("web/templates/landing.html", datahome)) + landingTplFile := cfg.Ini.Section("taldir").Key("landing_page").MustString(t.getFileName("web/templates/landing.html", cfg.Datahome)) t.LandingPageTpl, err = template.ParseFiles(landingTplFile, navTplFile, footerTplFile) if err != nil { log.Fatal(err) os.Exit(1) } - lookupResultTplFile := t.Cfg.Section("taldir").Key("lookup_result_page").MustString(t.getFileName("web/templates/lookup_result.html", datahome)) + lookupResultTplFile := cfg.Ini.Section("taldir").Key("lookup_result_page").MustString(t.getFileName("web/templates/lookup_result.html", cfg.Datahome)) t.LookupResultPageTpl, err = template.ParseFiles(lookupResultTplFile, navTplFile, footerTplFile) if err != nil { log.Fatal(err) os.Exit(1) } - aboutTplFile := t.Cfg.Section("taldir").Key("about_page").MustString(t.getFileName("web/templates/about.html", datahome)) + aboutTplFile := cfg.Ini.Section("taldir").Key("about_page").MustString(t.getFileName("web/templates/about.html", cfg.Datahome)) t.AboutPageTpl, err = template.ParseFiles(aboutTplFile, navTplFile, footerTplFile) if err != nil { log.Fatal(err) os.Exit(1) } t.Salt = os.Getenv("TALDIR_SALT") - if "" == t.Salt { - t.Salt = t.Cfg.Section("taldir").Key("salt").MustString("ChangeMe") + if t.Salt == "" { + t.Salt = cfg.Ini.Section("taldir").Key("salt").MustString("ChangeMe") } - t.Host = t.Cfg.Section("taldir").Key("base_url").MustString("http://localhost") + t.Host = cfg.Ini.Section("taldir").Key("base_url").MustString("http://localhost") //merchURL := t.Cfg.Section("taldir").Key("merchant_base_url").MustString("http://merchant.taldir/") //merchToken := t.Cfg.Section("taldir").Key("merchant_token").MustString("secretAccessToken") //t.Merchant = merchant.NewMerchant(merchURL, merchToken)