taldir

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

commit 46ef7e5e7c236c81a2dd63baf0b58efea385cefc
parent 16454d75b6eccf44fd85ca2356de570e16a31964
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Thu,  7 Aug 2025 22:23:29 +0200

implementation draft GNS disseminator

Diffstat:
Mcmd/taldir-config/main.go | 5++---
Apkg/rest/config.go | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apkg/rest/db.go | 42++++++++++++++++++++++++++++++++++++++++++
Apkg/rest/disseminator.go | 37+++++++++++++++++++++++++++++++++++++
Apkg/rest/disseminator_gns.go | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpkg/rest/taldir.go | 118++++++++++++++-----------------------------------------------------------------
6 files changed, 225 insertions(+), 100 deletions(-)

diff --git a/cmd/taldir-config/main.go b/cmd/taldir-config/main.go @@ -57,7 +57,7 @@ func printCfgOptions(sec *ini.Section, option *string, onlyValue *bool) { } return } - if ! sec.HasKey(*option) { + if !sec.HasKey(*option) { fmt.Printf("Section `%s' does not have option `%s'!\n", sec.Name(), *option) os.Exit(1) } @@ -71,7 +71,6 @@ func printCfgSections(f *ini.File) { } } - func main() { var cfg *ini.File var err error @@ -95,7 +94,7 @@ func main() { } cfgfile := "taldir.conf" if len(*cfgFlag) != 0 { - cfg, err = ini.LooseLoad(*cfgFlag) + cfg, err = ini.Load(*cfgFlag) if err != nil { fmt.Printf("Failed to read config: %v\n", err) os.Exit(1) diff --git a/pkg/rest/config.go b/pkg/rest/config.go @@ -0,0 +1,61 @@ +// This file is part of tdir, the Taler Directory implementation. +// Copyright (C) 2022 Martin Schanzenbach +// +// Taldir 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 of the License, +// or (at your option) any later version. +// +// Taldir 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 this program. If not, see <http://www.gnu.org/licenses/>. +// +// SPDX-License-Identifier: AGPL3.0-or-later + +package taldir + +import ( + "github.com/schanzen/taler-go/pkg/merchant" + "gopkg.in/ini.v1" + "gorm.io/gorm" +) + +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 + + // The loglevel to use + Loglevel LogLevel +} + +type LogLevel int + +const ( + LogError LogLevel = iota + LogWarning + LogInfo + LogDebug +) + +var LoglevelStringMap = map[LogLevel]string{ + LogDebug: "DEBUG", + LogError: "ERROR", + LogWarning: "WARN", + LogInfo: "INFO", +} diff --git a/pkg/rest/db.go b/pkg/rest/db.go @@ -0,0 +1,42 @@ +// This file is part of tdir, the Taler Directory implementation. +// Copyright (C) 2025 Martin Schanzenbach +// +// Taldir 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 of the License, +// or (at your option) any later version. +// +// Taldir 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 this program. If not, see <http://www.gnu.org/licenses/>. +// +// SPDX-License-Identifier: AGPL3.0-or-later + +package taldir + +import ( + "gorm.io/gorm" + "time" +) + +// Entry is a mapping from the identity key hash to a wallet key +// The identity key hash is sha512(sha512(address)|salt) where identity is +// one of the identity key types supported (e.g. an email address) +type Entry struct { + + // ORM + gorm.Model `json:"-"` + + // The salted hash (SHA512) of the hashed address (h_address) + HsAddress string `json:"-"` + + // Target URI to associate with this address + TargetUri string `json:"target_uri"` + + // How long the registration lasts in microseconds + Duration time.Duration `json:"-"` +} diff --git a/pkg/rest/disseminator.go b/pkg/rest/disseminator.go @@ -0,0 +1,37 @@ +// This file is part of tdir, the Taler Directory implementation. +// Copyright (C) 2025 Martin Schanzenbach +// +// Taldir 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 of the License, +// or (at your option) any later version. +// +// Taldir 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 this program. If not, see <http://www.gnu.org/licenses/>. +// +// SPDX-License-Identifier: AGPL3.0-or-later + +package taldir + +type Disseminator struct { + + // Disseminator name + Name string + + // Config disseminator uses + Config *TaldirConfig + + // Disseminator function + DisseminateStart func(d *Disseminator, e *Entry) error + + // Disseminator function + DisseminateStop func(d *Disseminator, e *Entry) error + + // Disseminator active status + Enabled bool +} diff --git a/pkg/rest/disseminator_gns.go b/pkg/rest/disseminator_gns.go @@ -0,0 +1,62 @@ +// This file is part of tdir, the Taler Directory implementation. +// Copyright (C) 2025 Martin Schanzenbach +// +// Taldir 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 of the License, +// or (at your option) any later version. +// +// Taldir 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 this program. If not, see <http://www.gnu.org/licenses/>. +// +// SPDX-License-Identifier: AGPL3.0-or-later + +package taldir + +import ( + "fmt" + "os/exec" +) + +func gns_disseminate_stop(d *Disseminator, e *Entry) error { + path, err := exec.LookPath("gnunet-namestore") + if err != nil { + return fmt.Errorf("path of command not found: %w", err) + } + zone := d.Config.Ini.Section("taldir-disseminator-gns").Key("zone").MustString("taldir") + out, err := exec.Command(path, "--delete", "--public", "--zone", zone, "--type", "TXT", "--name", e.HsAddress, "--value", e.TargetUri).Output() + if err != nil { + return fmt.Errorf("failed to execute disseminator command: `%s', %w", out, err) + } + return nil +} + +func gns_disseminate_start(d *Disseminator, e *Entry) error { + path, err := exec.LookPath("gnunet-namestore") + if err != nil { + return fmt.Errorf("path of command not found: %w", err) + } + expiration := d.Config.Ini.Section("taldir-disseminator-gns").Key("expiration").MustString("1d") + zone := d.Config.Ini.Section("taldir-disseminator-gns").Key("zone").MustString("taldir") + out, err := exec.Command(path, "--add", "--public", "--expiration", expiration, "--zone", zone, "--type", "TXT", "--name", e.HsAddress, "--value", e.TargetUri).Output() + if err != nil { + return fmt.Errorf("failed to execute disseminator command: `%s', %w", out, err) + } + return nil +} + +func make_gns_disseminator(cfg *TaldirConfig) Disseminator { + d := Disseminator{ + Name: "gns", + Config: cfg, + DisseminateStart: gns_disseminate_start, + DisseminateStop: gns_disseminate_stop, + Enabled: cfg.Ini.Section("taldir-disseminator-gns").Key("enabled").MustBool(false), + } + return d +} diff --git a/pkg/rest/taldir.go b/pkg/rest/taldir.go @@ -46,33 +46,12 @@ import ( tos "github.com/schanzen/taler-go/pkg/rest" talerutil "github.com/schanzen/taler-go/pkg/util" "github.com/skip2/go-qrcode" - "gopkg.in/ini.v1" "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 - - // The loglevel to use - Loglevel LogLevel -} - // Taldir is the primary object of the Taldir service type Taldir struct { @@ -177,16 +156,6 @@ type Validator struct { LandingPageTpl *template.Template } -type Disseminator struct { - - // Disseminator name - Name string - - // The command to call for dissemination - Command string - -} - // VersionResponse is the JSON response of the /config endpoint type VersionResponse struct { // libtool-style representation of the Merchant protocol version, see @@ -240,24 +209,6 @@ type RegisterMessage struct { Duration int64 `json:"duration"` } -// Entry is a mapping from the identity key hash to a wallet key -// The identity key hash is sha512(sha512(address)|salt) where identity is -// one of the identity key types supported (e.g. an email address) -type Entry struct { - - // ORM - gorm.Model `json:"-"` - - // The salted hash (SHA512) of the hashed address (h_address) - HsAddress string `json:"-"` - - // Target URI to associate with this address - TargetUri string `json:"target_uri"` - - // How long the registration lasts in microseconds - Duration time.Duration `json:"-"` -} - // Validation is the object created when a registration for an entry is initiated. // The Validation stores the identity key (sha256(identity)) the secret // Validation reference. The Validation reference is sent to the identity @@ -389,21 +340,24 @@ func (t *Taldir) getSingleEntry(w http.ResponseWriter, r *http.Request) { } // Disseminate entry -func (t *Taldir) disseminateEntry(e Entry) error { +func (t *Taldir) disseminateStop(e Entry) error { for _, d := range t.Disseminators { - path, err := exec.LookPath(d.Command) + err := d.DisseminateStop(&d, &e) if err != nil { - t.Logf(LogError, err.Error()+"\n") - return fmt.Errorf("path of command not found: %w", err) + t.Logf(LogWarning, "Dissemination stop failed for disseminator `%s' and entry `%s'", d.Name, e.HsAddress) } - t.Logf(LogDebug, "Found `%s' in path as `%s'\n", d.Command, path) - out, err := exec.Command(path, e.HsAddress, e.TargetUri).Output() - t.Logf(LogDebug, "Executing `%s %s %s `\n", path, e.HsAddress, e.TargetUri) + } + return nil +} + +// Disseminate entry +func (t *Taldir) disseminateStart(e Entry) { + for _, d := range t.Disseminators { + err := d.DisseminateStart(&d, &e) if err != nil { - return fmt.Errorf("failed to execute disseminator command: `%s', %w", out, err) + t.Logf(LogWarning, "Dissemination start failed for disseminator `%s' and entry `%s': %v", d.Name, e.HsAddress, err) } } - return nil } // Disseminate all entries @@ -411,10 +365,7 @@ func (t *Taldir) disseminateEntries() error { var entries []Entry t.Db.Where("1 = 1").Find(&entries) for _, e := range entries { - err := t.disseminateEntry(e) - if err != nil { - return fmt.Errorf("failed to disseminate `%s': %w", e.HsAddress, err) - } + t.disseminateStart(e) } return nil } @@ -491,9 +442,10 @@ func (t *Taldir) validationRequest(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) return } + t.disseminateStop(entry) } else { t.Db.Save(&entry) - t.disseminateEntry(entry) + t.disseminateStart(entry) } } else { if validation.TargetUri == "" { @@ -992,24 +944,8 @@ func (t *Taldir) getFileName(relativeFileName string) string { return relativeFileName } -type LogLevel int - -const ( - LogError LogLevel = iota - LogWarning - LogInfo - LogDebug -) - -var LoglevelStringMap = map[LogLevel]string{ - LogDebug: "DEBUG", - LogError: "ERROR", - LogWarning: "WARN", - LogInfo: "INFO", -} - func (t *Taldir) Logf(loglevel LogLevel, fmt string, args ...any) { - if loglevel < t.Cfg.Loglevel { + if loglevel > t.Cfg.Loglevel { return } t.Logger.SetPrefix("taler-directory - " + LoglevelStringMap[loglevel] + " ") @@ -1070,23 +1006,10 @@ func (t *Taldir) Initialize(cfg TaldirConfig) { } } t.Disseminators = make(map[string]Disseminator) - for _, sec := range cfg.Ini.Sections() { - if !strings.HasPrefix(sec.Name(), "taldir-disseminator-") { - continue - } - dname := strings.TrimPrefix(sec.Name(), "taldir-disseminator-") - if !sec.HasKey("enabled") { - t.Logger.Printf("`enabled` key in section `[%s]` not found, disabling disseminator.\n", sec.Name()) - continue - } - if !sec.Key("enabled").MustBool(false) { - t.Logger.Printf("`%s` disseminator disabled.\n", dname) - continue - } - t.Disseminators[dname] = Disseminator{ - Name: dname, - Command: sec.Key("command").MustString(""), - } + gnsdisseminator := make_gns_disseminator(&cfg) + if gnsdisseminator.Enabled { + t.Disseminators[gnsdisseminator.Name] = gnsdisseminator + t.Logger.Printf("Disseminator `%s' enabled.\n", gnsdisseminator.Name) } t.ChallengeBytes = cfg.Ini.Section("taldir").Key("challenge_bytes").MustInt(16) t.ValidationInitiationMax = cfg.Ini.Section("taldir").Key("validation_initiation_max").MustInt64(3) @@ -1189,4 +1112,5 @@ func (t *Taldir) Initialize(cfg TaldirConfig) { } t.CurrencySpec = currencySpec t.setupHandlers() + t.disseminateEntries() }