commit e7f2072ae60b28c8465fd8dd84f56fd54f5b24a6
parent 6a0c1a45f3a41c62720daccefe2eb761813454b2
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date: Fri, 8 Aug 2025 12:31:25 +0200
refactor valiadtors to support future OIDC et al
Diffstat:
3 files changed, 167 insertions(+), 69 deletions(-)
diff --git a/pkg/taldir/command_validator.go b/pkg/taldir/command_validator.go
@@ -0,0 +1,100 @@
+// 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 (
+ "html/template"
+ "regexp"
+ "fmt"
+ "os/exec"
+)
+
+type CommandValidator struct {
+
+ // Name
+ name string
+
+ // Config
+ config *TaldirConfig
+
+ // Validator alias regex
+ validAliasRegex string
+
+ // The command to call for validation
+ command string
+
+ // registration/lookup page
+ landingPageTpl *template.Template
+}
+
+func (t *CommandValidator) LandingPageTpl() *template.Template {
+ return t.landingPageTpl
+}
+
+func (t *CommandValidator) Type() ValidatorType {
+ return ValidatorTypeCommand
+}
+
+func (t *CommandValidator) Name() string {
+ return t.name
+}
+
+func (t *CommandValidator) IsEnabled() bool {
+ return t.config.Ini.Section("taldir-validator-" + t.name).Key("enabled").MustBool(false)
+}
+
+func (t *CommandValidator) ChallengeFee() string {
+ return t.config.Ini.Section("taldir-validator-" + t.name).Key("challenge_fee").MustString("KUDOS:0")
+}
+
+func (t *CommandValidator) IsAliasValid(alias string) (err error) {
+ if t.validAliasRegex != "" {
+ matched, _ := regexp.MatchString(t.validAliasRegex, alias)
+ if !matched {
+ return fmt.Errorf("alias `%s' invalid", alias) // TODO i18n
+ }
+ }
+ return
+}
+
+func (t *CommandValidator) RegistrationStart(topic string, link string, message string, address string, challenge string) error {
+ path, err := exec.LookPath(t.command)
+ if err != nil {
+ return err
+ }
+ out, err := exec.Command(path, address, challenge, topic, message).Output()
+ // FIXME logger
+ fmt.Printf("Executing `%s %s %s %s %s`\n", path, address, challenge, topic, message)
+ if err != nil {
+ fmt.Printf("%s, %v\n", out, err)
+ return err
+ }
+ return nil
+}
+
+func make_command_validator (cfg *TaldirConfig, name string, landingPageTpl *template.Template) CommandValidator {
+ sec := cfg.Ini.Section("taldir-validator-" + name)
+ return CommandValidator{
+ name: name,
+ config: cfg,
+ landingPageTpl: landingPageTpl,
+ validAliasRegex: sec.Key("valid_alias_regex").MustString(""),
+ command: sec.Key("command").MustString(""),
+ }
+}
diff --git a/pkg/taldir/taldir.go b/pkg/taldir/taldir.go
@@ -34,7 +34,6 @@ import (
"net/http"
"net/url"
"os"
- "os/exec"
"regexp"
"strings"
"time"
@@ -128,34 +127,6 @@ type Taldir struct {
Logger *log.Logger
}
-type ValidatorType string
-
-const (
- ValidatorTypeCommand ValidatorType = "command"
- ValidatorTypeOIDC ValidatorType = "oidc"
-)
-
-type Validator struct {
-
- // Validator name
- Name string
-
- // Validator alias regex
- ValidAliasRegex string
-
- // Validator type
- Type ValidatorType
-
- // Amount of payment required
- ChallengeFee string
-
- // The command to call for validation
- Command string
-
- // registration/lookup page
- LandingPageTpl *template.Template
-}
-
// VersionResponse is the JSON response of the /config endpoint
type VersionResponse struct {
// libtool-style representation of the Merchant protocol version, see
@@ -312,16 +283,6 @@ func (t *Taldir) isPMSValid(pms string) (err error) {
return
}
-func (v *Validator) isAliasValid(alias string) (err error) {
- if v.ValidAliasRegex != "" {
- matched, _ := regexp.MatchString(v.ValidAliasRegex, alias)
- if !matched {
- return fmt.Errorf("alias `%s' invalid", alias) // TODO i18n
- }
- }
- return
-}
-
// Primary lookup function.
// Allows the caller to query a wallet key using the hash(!) of the
// identity, e.g. SHA512(<email address>)
@@ -344,7 +305,7 @@ func (t *Taldir) disseminateStop(e Entry) error {
for _, d := range t.Disseminators {
err := d.DisseminateStop(&e)
if err != nil {
- t.Logf(LogWarning, "Dissemination stop failed for disseminator `%s' and entry `%s'", d.Name, e.HsAddress)
+ t.Logf(LogWarning, "Dissemination stop failed for disseminator `%s' and entry `%s'", d.Name(), e.HsAddress)
}
}
return nil
@@ -355,7 +316,7 @@ func (t *Taldir) disseminateStart(e Entry) {
for _, d := range t.Disseminators {
err := d.DisseminateStart(&e)
if err != nil {
- t.Logf(LogWarning, "Dissemination start failed for disseminator `%s' and entry `%s': %v", d.Name, e.HsAddress, err)
+ t.Logf(LogWarning, "Dissemination start failed for disseminator `%s' and entry `%s': %v", d.Name(), e.HsAddress, err)
}
}
}
@@ -572,7 +533,7 @@ func (t *Taldir) registerRequest(w http.ResponseWriter, r *http.Request) {
sliceDuration := time.Duration(validation.Duration * 1000)
cost, err := util.CalculateCost(t.MonthlyFee,
- validator.ChallengeFee,
+ validator.ChallengeFee(),
sliceDuration,
monthDuration)
if err != nil {
@@ -618,21 +579,12 @@ func (t *Taldir) registerRequest(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500)
return
}
- path, err := exec.LookPath(validator.Command)
- if err != nil {
- t.Logf(LogError, err.Error()+"\n")
- t.Db.Delete(&validation)
- w.WriteHeader(500)
- return
- }
- t.Logf(LogDebug, "Found `%s' in path as `%s'\n", validator.Command, path)
topic := t.I18n.GetLocale(r).GetMessage("taldirRegTopic")
link := t.Host + "/register/" + url.QueryEscape(validation.HAddress) + "/" + url.QueryEscape(validation.Challenge) + "?address=" + url.QueryEscape(req.Address)
message := t.I18n.GetLocale(r).GetMessage("taldirRegMessage", link)
- out, err := exec.Command(path, req.Address, validation.Challenge, topic, message).Output()
- t.Logf(LogDebug, "Executing `%s %s %s %s %s`\n", path, req.Address, validation.Challenge, topic, message)
+ err = validator.RegistrationStart(topic, link, message, req.Address, validation.Challenge)
if err != nil {
- fmt.Printf("%s, %v\n", out, err)
+ t.Logf(LogError, err.Error()+"\n")
t.Db.Delete(&validation)
w.WriteHeader(500)
return
@@ -648,7 +600,7 @@ func (t *Taldir) configResponse(w http.ResponseWriter, r *http.Request) {
for key := range t.Validators {
var meth Method
meth.Name = key
- meth.ChallengeFee = t.Validators[key].ChallengeFee
+ meth.ChallengeFee = t.Validators[key].ChallengeFee()
i++
meths = append(meths, meth)
}
@@ -833,13 +785,13 @@ func (t *Taldir) methodLookupResultPage(w http.ResponseWriter, r *http.Request)
// Check if alias is valid
alias := r.URL.Query().Get("address")
- err := val.isAliasValid(alias)
+ err := val.IsAliasValid(alias)
emsg := ""
found := false
if nil != err {
t.Logf(LogWarning, "Not a valid alias\n")
emsg = t.I18n.GetLocale(r).GetMessage("aliasInvalid", alias)
- http.Redirect(w, r, fmt.Sprintf("/landing/"+val.Name+"?error=%s", emsg), http.StatusSeeOther)
+ http.Redirect(w, r, fmt.Sprintf("/landing/"+val.Name()+"?error=%s", emsg), http.StatusSeeOther)
return
} else {
hAddressBin := sha512.Sum512([]byte(r.URL.Query().Get("address")))
@@ -855,7 +807,7 @@ func (t *Taldir) methodLookupResultPage(w http.ResponseWriter, r *http.Request)
fullData := map[string]any{
"version": t.Cfg.Version,
"available": !found,
- "method": val.Name,
+ "method": val.Name(),
"address": r.URL.Query().Get("address"),
"result": entry.TargetUri,
"error": emsg,
@@ -884,7 +836,7 @@ func (t *Taldir) methodLandingPage(w http.ResponseWriter, r *http.Request) {
"productDisclaimerShort": template.HTML(t.I18n.GetLocale(r).GetMessage("productDisclaimerShort")),
"tr": t.I18n.GetLocale(r).GetMessage,
}
- err := val.LandingPageTpl.Execute(w, fullData)
+ err := val.LandingPageTpl().Execute(w, fullData)
if err != nil {
fmt.Println(err)
}
@@ -985,23 +937,16 @@ func (t *Taldir) Initialize(cfg TaldirConfig) {
continue
}
vname := strings.TrimPrefix(sec.Name(), "taldir-validator-")
- if !sec.Key("enabled").MustBool(false) {
- t.Logger.Printf("`%s` validator disabled.\n", vname)
- continue
- }
vlandingPageTplFile := sec.Key("registration_page").MustString(t.getFileName("web/templates/landing_" + vname + ".html"))
vlandingPageTpl, err := template.ParseFiles(vlandingPageTplFile, navTplFile, footerTplFile)
if err != nil {
t.Logger.Printf("`%s` template not found, disabling validator `%s`.\n", vlandingPageTplFile, vname)
continue
}
- t.Validators[vname] = Validator{
- Name: vname,
- LandingPageTpl: vlandingPageTpl,
- ChallengeFee: sec.Key("challenge_fee").MustString("KUDOS:0"),
- Command: sec.Key("command").MustString(""),
- Type: ValidatorType(sec.Key("type").MustString("")),
- ValidAliasRegex: sec.Key("valid_alias_regex").MustString(""),
+ v := make_command_validator(&cfg, vname, vlandingPageTpl)
+ if v.IsEnabled() {
+ t.Logger.Printf("`%s` validator disabled.\n", vname)
+ t.Validators[vname] = &v
}
}
t.Disseminators = make(map[string]Disseminator)
diff --git a/pkg/taldir/validator.go b/pkg/taldir/validator.go
@@ -0,0 +1,53 @@
+// 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 (
+ "html/template"
+)
+
+type ValidatorType string
+
+const (
+ ValidatorTypeCommand ValidatorType = "command"
+ ValidatorTypeOIDC ValidatorType = "oidc"
+)
+
+type Validator interface {
+
+ // Validator name
+ Name() string
+
+ // Validator type
+ Type() ValidatorType
+
+ // Amount of payment required
+ ChallengeFee() string
+
+ // registration/lookup page
+ LandingPageTpl() *template.Template
+
+ // Is alias valid
+ IsAliasValid(alias string) error
+
+ // Start registration
+ RegistrationStart(topic string, link string, message string, address string, challenge string) error
+}
+
+