gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taldir] 01/02: Refactor for better testing


From: gnunet
Subject: [taler-taldir] 01/02: Refactor for better testing
Date: Wed, 06 Jul 2022 23:35:46 +0200

This is an automated email from the git hooks/post-receive script.

martin-schanzenbach pushed a commit to branch master
in repository taldir.

commit 5a70d6d172f2f9330276830f415e62ae32ae032c
Author: Martin Schanzenbach <schanzen@gnunet.org>
AuthorDate: Wed Jul 6 22:23:48 2022 +0200

    Refactor for better testing
---
 cmd/taldir-server/main.go       | 487 +---------------------------------------
 scripts/taldir-validate-twitter |  13 +-
 taldir.conf                     |   2 +-
 3 files changed, 18 insertions(+), 484 deletions(-)

diff --git a/cmd/taldir-server/main.go b/cmd/taldir-server/main.go
index b2ef3f7..38261f1 100644
--- a/cmd/taldir-server/main.go
+++ b/cmd/taldir-server/main.go
@@ -29,445 +29,11 @@ package main
 
 import (
   "os"
-  "os/exec"
-  "bufio"
-  "time"
   "fmt"
-  "log"
+  "bufio"
   "flag"
-  "net/http"
-  "html/template"
-  "encoding/json"
-  "github.com/gorilla/mux"
-  "gorm.io/gorm"
-  "encoding/base64"
-  "taler.net/taldir/util"
-  "taler.net/taldir/gana"
-  "crypto/sha512"
-  "gorm.io/driver/postgres"
-  "gopkg.in/ini.v1"
-  "strings"
-  "github.com/skip2/go-qrcode"
 )
 
-type VersionResponse struct {
-  // libtool-style representation of the Merchant protocol version, see
-  // 
https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
-  // The format is "current:revision:age".
-  Version string `json:"version"`
-
-  // Name of the protocol.
-  Name string `json:"name"` // "taler-directory"
-
-  // Supported registration methods
-  Methods []Method `json:"methods"`
-
-  // fee for one month of registration
-  MonthlyFee string `json:"monthly_fee"`
-
-}
-
-type Method struct {
-
-  // Name of the method, e.g. "email" or "sms".
-  Name string `json:"name"`
-
-  // per challenge fee
-  ChallengeFee string `json:"challenge_fee"`
-
-}
-
-type RateLimitedResponse struct {
-
-  // Taler error code, TALER_EC_TALDIR_REGISTER_RATE_LIMITED.
-  Code int `json:"code"`
-
-  // At what frequency are new registrations allowed. FIXME: In what? 
Currently: In microseconds
-  RequestFrequency int64 `json:"request_frequency"`
-
-  // The human readable error message.
-  Hint string `json:"hint"`
-}
-
-type RegisterMessage struct {
-
-  // Address, in method-specific format
-  Address string `json:"address"`
-
-  // Public key of the user to register
-  PublicKey string `json:"public_key"`
-
-  // (HTTPS) endpoint URL for the inbox service for this address
-  Inbox string `json:"inbox_url"`
-
-  // For how long should the registration last
-  Duration int64 `json:"duration"`
-
-  // Order ID, if the client recently paid for this registration
-  // FIXME: As an optional field, maybe we want to parse this separately
-  // instead?
-  // Order_id string `json:"order_id"`
-}
-
-// A mappind entry from the identity key hash to a wallet key
-// The identity key hash is sha256(sha256(identity)|salt) where identity is
-// one of the identity key types supported (e.g. email)
-type Entry struct {
-
-  // ORM
-  gorm.Model  `json:"-"`
-
-  // The salted hash (SHA512) of the hashed address (h_address)
-  HsAddress string `json:"-"`
-
-  // (HTTPS) endpoint URL for the inbox service for this address
-  Inbox string `json:"inbox_url"`
-
-  // Public key of the user to register in base32
-  PublicKey string `json:"public_key"`
-
-  // Time of (re)registration. In Unix epoch microseconds)
-  RegisteredAt int64 `json:"-"`
-
-  // How long the registration lasts in microseconds
-  Duration int64 `json:"-"`
-}
-
-// A validation is 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
-// depending on the out-of-band chennel defined through the identity key type.
-type Validation struct {
-
-  // ORM
-  gorm.Model `json:"-"`
-
-  // The hash (SHA512) of the address
-  HAddress string `json:"h_address"`
-
-  // For how long should the registration last
-  Duration int64 `json:"duration"`
-
-  // (HTTPS) endpoint URL for the inbox service for this address
-  Inbox string `json:"inbox_url"`
-
-  // The activation code sent to the client
-  Code string `json:"activation_code"`
-
-  // Public key of the user to register
-  PublicKey string `json:"public_key"`
-}
-
-type ErrorDetail struct {
-
-  // Numeric error code unique to the condition.
-  // The other arguments are specific to the error value reported here.
-  Code int `json:"code"`
-
-  // Human-readable description of the error, i.e. "missing parameter", 
"commitment violation", ...
-  // Should give a human-readable hint about the error's nature. Optional, may 
change without notice!
-  Hint string `json:"hint,omitempty"`
-
-  // Optional detail about the specific input value that failed. May change 
without notice!
-  Detail string `json:"detail,omitempty"`
-
-  // Name of the parameter that was bogus (if applicable).
-  Parameter string `json:"parameter,omitempty"`
-
-  // Path to the argument that was bogus (if applicable).
-  Path string `json:"path,omitempty"`
-
-  // Offset of the argument that was bogus (if applicable).
-  Offset string `json:"offset,omitempty"`
-
-  // Index of the argument that was bogus (if applicable).
-  Index string `json:"index,omitempty"`
-
-  // Name of the object that was bogus (if applicable).
-  Object string `json:"object,omitempty"`
-
-  // Name of the currency than was problematic (if applicable).
-  Currency string `json:"currency,omitempty"`
-
-  // Expected type (if applicable).
-  TypeExpected string `json:"type_expected,omitempty"`
-
-  // Type that was provided instead (if applicable).
-  TypeActual string `json:"type_actual,omitempty"`
-}
-
-type ValidationConfirmation struct {
-  Solution string `json:"solution"`
-}
-
-// The main DB handle
-var db *gorm.DB
-
-// Our configuration from the config.json
-var cfg *ini.File
-
-// Map of supported validators as defined in the configuration
-var validators map[string]bool
-
-// landing page
-var validationTpl *template.Template
-
-// Primary lookup function.
-// Allows the caller to query a wallet key using the hash(!) of the
-// identity, e.g. SHA512(<email address>)
-func getSingleEntry(w http.ResponseWriter, r *http.Request){
-  vars := mux.Vars(r)
-  var entry Entry
-  hs_address := saltHAddress(vars["h_address"])
-  var err = db.First(&entry, "hs_address = ?", hs_address).Error
-  if err == nil {
-    w.Header().Set("Content-Type", "application/json")
-    resp, _ := json.Marshal(entry)
-    w.Write(resp)
-    return
-  }
-  w.WriteHeader(http.StatusNotFound)
-}
-
-// Hashes an identity key (e.g. sha256(<email address>)) with a salt for
-// Lookup and storage.
-func saltHAddress(h_address string) string {
-  salt := os.Getenv("TALDIR_SALT")
-  if "" == salt {
-    salt = cfg.Section("taldir").Key("salt").MustString("ChangeMe")
-  }
-  h := sha512.New()
-  h.Write([]byte(h_address))
-  h.Write([]byte(salt))
-  return util.EncodeBinaryToString(h.Sum(nil))
-}
-
-// Called by the registrant to validate the registration request. The 
reference ID was
-// provided "out of band" using a validation method such as email or SMS
-func validationRequest(w http.ResponseWriter, r *http.Request){
-  vars := mux.Vars(r)
-  var entry Entry
-  var validation Validation
-  var confirm ValidationConfirmation
-  var errDetail ErrorDetail
-  if r.Body == nil {
-    http.Error(w, "No request body", 400)
-    return
-  }
-  err := json.NewDecoder(r.Body).Decode(&confirm)
-  if err != nil {
-    errDetail.Code = 1006 //TALER_EC_JSON_INVALID
-    errDetail.Hint = "Unable to parse JSON"
-    resp, _ := json.Marshal(errDetail)
-    w.WriteHeader(400)
-    w.Write(resp)
-    return
-  }
-  err = db.First(&validation, "h_address = ?", vars["h_address"]).Error
-  if err != nil {
-    w.WriteHeader(http.StatusNotFound)
-    return
-  }
-  expectedSolution := util.GenerateSolution(validation.PublicKey, 
validation.Code)
-  if confirm.Solution != expectedSolution {
-    // FIXME how TF do we rate limit here??
-    w.WriteHeader(http.StatusForbidden)
-    return
-  }
-  // FIXME: Expire validations somewhere?
-  err = db.Delete(&validation).Error
-  if err != nil {
-    w.WriteHeader(http.StatusInternalServerError)
-    return
-  }
-  entry.HsAddress = saltHAddress(validation.HAddress)
-  entry.Inbox = validation.Inbox
-  entry.Duration = validation.Duration
-  entry.RegisteredAt = time.Now().UnixMicro()
-  entry.PublicKey = validation.PublicKey
-  err = db.First(&entry, "hs_address = ?", entry.HsAddress).Error
-  if err == nil {
-    db.Save(&entry)
-  } else {
-    err = db.Create(&entry).Error
-    if err != nil {
-      w.WriteHeader(http.StatusInternalServerError)
-      return
-    }
-  }
-  w.WriteHeader(http.StatusNoContent)
-}
-
-
-func registerRequest(w http.ResponseWriter, r *http.Request){
-  vars := mux.Vars(r)
-  var req RegisterMessage
-  var errDetail ErrorDetail
-  var validation Validation
-  var entry Entry
-  if r.Body == nil {
-    http.Error(w, "No request body", 400)
-    return
-  }
-  err := json.NewDecoder(r.Body).Decode(&req)
-  if err != nil {
-    errDetail.Code = gana.GENERIC_JSON_INVALID
-    errDetail.Hint = "Unable to parse JSON"
-    resp, _ := json.Marshal(errDetail)
-    w.WriteHeader(400)
-    w.Write(resp)
-    return
-  }
-  if !validators[vars["method"]] {
-    errDetail.Code = gana.TALDIR_METHOD_NOT_SUPPORTED
-    errDetail.Hint = "Unsupported method"
-    errDetail.Detail = "Given method: " + vars["method"]
-    resp, _ := json.Marshal(errDetail)
-    w.WriteHeader(404)
-    w.Write(resp)
-    return
-  }
-  h := sha512.New()
-  h.Write([]byte(req.Address))
-  validation.HAddress = util.EncodeBinaryToString(h.Sum(nil))
-  // We first try if there is already an entry for this address which
-  // is still valid and the duration is not extended.
-  hs_address := saltHAddress(validation.HAddress)
-  err = db.First(&entry, "hs_address = ?", hs_address).Error
-  if err != nil {
-    lastRegValidity := entry.RegisteredAt + entry.Duration
-    requestedValidity := time.Now().UnixMicro() + req.Duration
-    reqFrequency := 
cfg.Section("taldir").Key("request_frequency").MustInt64(1000)
-    earliestReRegistration := entry.RegisteredAt + reqFrequency
-    // Rate limit re-registrations.
-    if time.Now().UnixMicro() < earliestReRegistration {
-      w.WriteHeader(429)
-      rlResponse := RateLimitedResponse{
-        Code: gana.TALDIR_REGISTER_RATE_LIMITED,
-        RequestFrequency: reqFrequency,
-        Hint: "Registration rate limit reached",
-      }
-      jsonResp, _ := json.Marshal(rlResponse)
-      w.Write(jsonResp)
-      return
-    }
-    // Do not allow re-registrations with shorter duration.
-    if requestedValidity <= lastRegValidity {
-      w.WriteHeader(200)
-      // FIXME how to return how long it is already paid for??
-      return
-    }
-  }
-  err = db.First(&validation, "h_address = ?", validation.HAddress).Error
-  validation.Code = util.GenerateCode()
-  validation.Inbox = req.Inbox
-  validation.Duration = req.Duration
-  validation.PublicKey = req.PublicKey
-  if err == nil {
-    // FIXME: Validation already pending for this address
-    // How should we proceed here? Expire old validations?
-    log.Println("Validation for this address already exists")
-    err = db.Save(&validation).Error
-  } else  {
-    err = db.Create(&validation).Error
-  }
-  if err != nil {
-    // FIXME: API needs 400 error codes in such cases
-    w.WriteHeader(http.StatusInternalServerError)
-    return
-  }
-  fmt.Println("Address registration request created:", validation)
-  if !cfg.Section("taldir-" + vars["method"]).HasKey("command") {
-    log.Fatal(err)
-    db.Delete(&validation)
-    w.WriteHeader(500)
-    return
-  }
-  command := cfg.Section("taldir-" + vars["method"]).Key("command").String()
-  path, err := exec.LookPath(command)
-  if err != nil {
-    log.Println(err)
-    db.Delete(&validation)
-    w.WriteHeader(500)
-    return
-  }
-  out, err := exec.Command(path, req.Address, validation.Code).Output()
-  if err != nil {
-    log.Println(err)
-    db.Delete(&validation)
-    w.WriteHeader(500)
-    return
-  }
-  w.WriteHeader(202)
-  fmt.Printf("Output from method script %s is %s\n", path, out)
-}
-
-func notImplemented(w http.ResponseWriter, r *http.Request) {
-  return
-}
-
-func configResponse(w http.ResponseWriter, r *http.Request) {
-  meths := []Method{}
-  i := 0
-  for key, _ := range validators {
-    var meth Method
-    meth.Name = key
-    meth.ChallengeFee = cfg.Section("taldir-" + 
key).Key("challenge_fee").MustString("1 Kudos")
-    i++
-    meths = append(meths, meth)
-  }
-  cfg := VersionResponse{
-    Version: "0:0:0",
-    Name: "taler-directory",
-    MonthlyFee: cfg.Section("taldir").Key("monthly_fee").MustString("1 Kudos"),
-    Methods: meths,
-  }
-  w.Header().Set("Content-Type", "application/json")
-  response, _ := json.Marshal(cfg)
-  w.Write(response)
-}
-
-func validationPage(w http.ResponseWriter, r *http.Request) {
-  vars := mux.Vars(r)
-  w.Header().Set("Content-Type", "text/html; charset=utf-8")
-  var walletLink string
-  walletLink = "taler://taldir/" + vars["h_address"] + "/" + 
vars["validation_code"] + "-wallet"
-  var png []byte
-  png, err := qrcode.Encode(walletLink, qrcode.Medium, 256)
-  if err != nil {
-    w.WriteHeader(500)
-    return
-  }
-  encodedPng := base64.StdEncoding.EncodeToString(png)
-
-  fullData := map[string]interface{}{
-    "QRCode": template.URL("data:image/png;base64," + encodedPng),
-    "WalletLink": template.URL(walletLink),
-  }
-  validationTpl.Execute(w, fullData)
-  return
-}
-
-func handleRequests() {
-  myRouter := mux.NewRouter().StrictSlash(true)
-
-  /* ToS API */
-  myRouter.HandleFunc("/terms", notImplemented).Methods("GET")
-  myRouter.HandleFunc("/privacy", notImplemented).Methods("GET")
-
-  /* Config API */
-  myRouter.HandleFunc("/config", configResponse).Methods("GET")
-
-
-  /* Registration API */
-  myRouter.HandleFunc("/{h_address}", getSingleEntry).Methods("GET")
-  myRouter.HandleFunc("/register/{method}", registerRequest).Methods("POST")
-  myRouter.HandleFunc("/register/{h_address}/{validation_code}", 
validationPage).Methods("GET")
-  myRouter.HandleFunc("/{h_address}", validationRequest).Methods("POST")
-
-  
log.Fatal(http.ListenAndServe(cfg.Section("taldir").Key("bind_to").MustString("localhost:11000"),
 myRouter))
-}
-
 func main() {
   var dropFlag = flag.Bool("D", false, "Drop all data in table (DANGEROUS!)")
   var cfgFlag = flag.String("c", "", "Configuration file to use")
@@ -476,42 +42,8 @@ func main() {
   if len(*cfgFlag) != 0 {
     cfgfile = *cfgFlag
   }
-  _cfg, err := ini.Load(cfgfile)
-  if err != nil {
-    fmt.Printf("Failed to read config: %v", err)
-    os.Exit(1)
-  }
-  cfg = _cfg
-  if cfg.Section("taldir").Key("production").MustBool(false) {
-    fmt.Println("Production mode enabled") 
-  }
-
-  validators = make(map[string]bool)
-  for _, a := range 
strings.Split(cfg.Section("taldir").Key("validators").String(), " ") {
-    validators[a] = true
-  }
-  validators = make(map[string]bool)
-  for _, a := range 
strings.Split(cfg.Section("taldir").Key("validators").String(), " ") {
-    validators[a] = true
-  }
-
-  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, err := gorm.Open(postgres.Open(psqlconn), &gorm.Config{})
-  if err != nil {
-    panic(err)
-  }
-  db = _db
-  if err := db.AutoMigrate(&Entry{}); err != nil {
-    panic(err)
-  }
-  if err := db.AutoMigrate(&Validation{}); err != nil {
-    panic(err)
-  }
+  t := Taldir{}
+  clearDb := false
   if *dropFlag {
     fmt.Println("Really delete all data in database? [y/N]:")
     reader := bufio.NewReader(os.Stdin)
@@ -520,19 +52,12 @@ func main() {
     if err == nil {
       fmt.Println(char)
       if char == 'y' {
-        fmt.Println("Deleting entries...")
-        db.Where("1 = 1").Delete(&Entry{})
-        fmt.Println("Deleting validations...")
-        db.Where("1 = 1").Delete(&Validation{})
+        clearDb = true
       }
       os.Exit(0)
     }
     os.Exit(1)
   }
-
-  validationTpl, err = template.ParseFiles("templates/validation_landing.html")
-  if err != nil {
-    fmt.Println(err)
-  }
-  handleRequests()
+  t.Initialize(cfgfile, clearDb)
+  t.Run()
 }
diff --git a/scripts/taldir-validate-twitter b/scripts/taldir-validate-twitter
index cd1b6e4..e684ea2 100755
--- a/scripts/taldir-validate-twitter
+++ b/scripts/taldir-validate-twitter
@@ -1,3 +1,12 @@
 #!/bin/bash
-echo $1 $2
-echo $2 > validation_code
+#
+# IMPORTANT: Before this can be used, as the taldir service user
+# you need to authorize this CLI app for the taldir twitter account.
+# e.g.:
+# $ t authorize
+#
+TWITTER_USER=$1
+CODE=$2
+LINK=$(taldir-cli -l -a $1 -c $2)
+MESSAGE="Follow this link to complete your Taldir registration: $LINK"
+t dm $TWITTER_USER $MESSAGE
diff --git a/taldir.conf b/taldir.conf
index 9560e77..aac8fe1 100644
--- a/taldir.conf
+++ b/taldir.conf
@@ -1,6 +1,6 @@
 [taldir]
 production = false
-validators = "email phone test"
+validators = "twitter test"
 host = "https://taldir.net";
 bind_to = "localhost:11000"
 salt = "ChangeMe"

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]