gnunet-svn
[Top][All Lists]
Advanced

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

[taler-taldir] branch master updated (eac62d8 -> f16b14f)


From: gnunet
Subject: [taler-taldir] branch master updated (eac62d8 -> f16b14f)
Date: Wed, 06 Jul 2022 23:35:45 +0200

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

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

    from eac62d8  handle missing scripts better
     new 5a70d6d  Refactor for better testing
     new f16b14f  add tests

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 cmd/taldir-server/main.go                          | 487 +--------------------
 cmd/taldir-server/main_test.go                     |  86 ++++
 cmd/taldir-server/{main.go => taldir.go}           | 209 ++++-----
 .../taldir-server/testdata/taldir-test.conf        |   3 +-
 .../testdata/templates}/validation_landing.html    |   0
 scripts/taldir-validate-twitter                    |  13 +-
 taldir.conf                                        |   2 +-
 7 files changed, 211 insertions(+), 589 deletions(-)
 create mode 100644 cmd/taldir-server/main_test.go
 copy cmd/taldir-server/{main.go => taldir.go} (72%)
 copy taldir.conf => cmd/taldir-server/testdata/taldir-test.conf (86%)
 copy {templates => 
cmd/taldir-server/testdata/templates}/validation_landing.html (100%)

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/cmd/taldir-server/main_test.go b/cmd/taldir-server/main_test.go
new file mode 100644
index 0000000..ec13af1
--- /dev/null
+++ b/cmd/taldir-server/main_test.go
@@ -0,0 +1,86 @@
+// This file is part of gnunet-go, a GNUnet-implementation in Golang.
+// Copyright (C) 2019-2022 Bernd Fix  >Y<
+//
+// gnunet-go 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.
+//
+// gnunet-go 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 main_test
+
+import (
+  "os"
+  "testing"
+  "net/http"
+  "net/http/httptest"
+  "crypto/sha512"
+  "bytes"
+  "taler.net/taldir/cmd/taldir-server"
+  "taler.net/taldir/util"
+)
+
+var t main.Taldir
+
+var validRegisterRequest = []byte(`
+  {
+    "address": "abc@test",
+    "public_key": "000G006XE97PTWV3B7AJNCRQZA6BF26HPV3XZ07293FMY7KD4181946A90",
+    "inbox_url": "myinbox@xyz",
+    "duration": 23
+  }
+`)
+
+func TestMain(m *testing.M) {
+  t.Initialize("testdata/taldir-test.conf", true)
+  code := m.Run()
+  t.ClearDatabase()
+  os.Exit(code)
+}
+
+func getHAddress(addr string) string {
+  h := sha512.New()
+  h.Write([]byte(addr))
+  return util.EncodeBinaryToString(h.Sum(nil))
+}
+
+func TestNoEntry(s *testing.T) {
+    t.ClearDatabase()
+
+    h_addr := getHAddress("jdoe@example.com")
+    req, _ := http.NewRequest("GET", "/" + h_addr, nil)
+    response := executeRequest(req)
+
+    if http.StatusNotFound != response.Code {
+      s.Errorf("Expected response code %d. Got %d\n", http.StatusNotFound, 
response.Code)
+    }
+}
+
+func executeRequest(req *http.Request) *httptest.ResponseRecorder {
+  rr := httptest.NewRecorder()
+  t.Router.ServeHTTP(rr, req)
+  return rr
+}
+
+func TestRegisterRequest(s *testing.T) {
+    t.ClearDatabase()
+
+    req, _ := http.NewRequest("POST", "/register/test", 
bytes.NewBuffer(validRegisterRequest))
+    response := executeRequest(req)
+
+    if http.StatusAccepted != response.Code {
+      s.Errorf("Expected response code %d. Got %d\n", http.StatusAccepted, 
response.Code)
+    }
+}
+
+
diff --git a/cmd/taldir-server/main.go b/cmd/taldir-server/taldir.go
similarity index 72%
copy from cmd/taldir-server/main.go
copy to cmd/taldir-server/taldir.go
index b2ef3f7..3d22853 100644
--- a/cmd/taldir-server/main.go
+++ b/cmd/taldir-server/taldir.go
@@ -30,11 +30,9 @@ package main
 import (
   "os"
   "os/exec"
-  "bufio"
   "time"
   "fmt"
   "log"
-  "flag"
   "net/http"
   "html/template"
   "encoding/json"
@@ -50,6 +48,30 @@ import (
   "github.com/skip2/go-qrcode"
 )
 
+type Taldir struct {
+
+  // The main router
+  Router *mux.Router
+
+  // The main DB handle
+  Db *gorm.DB
+
+  // Our configuration from the config.json
+  Cfg *ini.File
+
+  // Map of supported validators as defined in the configuration
+  Validators map[string]bool
+
+  // landing page
+  ValidationTpl *template.Template
+
+  // The address salt
+  Salt string
+
+  // Request frequency
+  RequestFrequency int64
+}
+
 type VersionResponse struct {
   // libtool-style representation of the Merchant protocol version, see
   // 
https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
@@ -200,26 +222,14 @@ 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){
+func (t *Taldir) 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
+  hs_address := saltHAddress(vars["h_address"], t.Salt)
+  var err = t.Db.First(&entry, "hs_address = ?", hs_address).Error
   if err == nil {
     w.Header().Set("Content-Type", "application/json")
     resp, _ := json.Marshal(entry)
@@ -231,11 +241,7 @@ func getSingleEntry(w http.ResponseWriter, r 
*http.Request){
 
 // 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")
-  }
+func saltHAddress(h_address string, salt string) string {
   h := sha512.New()
   h.Write([]byte(h_address))
   h.Write([]byte(salt))
@@ -244,7 +250,7 @@ func saltHAddress(h_address string) string {
 
 // 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){
+func (t *Taldir) validationRequest(w http.ResponseWriter, r *http.Request){
   vars := mux.Vars(r)
   var entry Entry
   var validation Validation
@@ -263,7 +269,7 @@ func validationRequest(w http.ResponseWriter, r 
*http.Request){
     w.Write(resp)
     return
   }
-  err = db.First(&validation, "h_address = ?", vars["h_address"]).Error
+  err = t.Db.First(&validation, "h_address = ?", vars["h_address"]).Error
   if err != nil {
     w.WriteHeader(http.StatusNotFound)
     return
@@ -275,21 +281,21 @@ func validationRequest(w http.ResponseWriter, r 
*http.Request){
     return
   }
   // FIXME: Expire validations somewhere?
-  err = db.Delete(&validation).Error
+  err = t.Db.Delete(&validation).Error
   if err != nil {
     w.WriteHeader(http.StatusInternalServerError)
     return
   }
-  entry.HsAddress = saltHAddress(validation.HAddress)
+  entry.HsAddress = saltHAddress(validation.HAddress, t.Salt)
   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
+  err = t.Db.First(&entry, "hs_address = ?", entry.HsAddress).Error
   if err == nil {
-    db.Save(&entry)
+    t.Db.Save(&entry)
   } else {
-    err = db.Create(&entry).Error
+    err = t.Db.Create(&entry).Error
     if err != nil {
       w.WriteHeader(http.StatusInternalServerError)
       return
@@ -299,7 +305,7 @@ func validationRequest(w http.ResponseWriter, r 
*http.Request){
 }
 
 
-func registerRequest(w http.ResponseWriter, r *http.Request){
+func (t *Taldir) registerRequest(w http.ResponseWriter, r *http.Request){
   vars := mux.Vars(r)
   var req RegisterMessage
   var errDetail ErrorDetail
@@ -318,7 +324,7 @@ func registerRequest(w http.ResponseWriter, r 
*http.Request){
     w.Write(resp)
     return
   }
-  if !validators[vars["method"]] {
+  if !t.Validators[vars["method"]] {
     errDetail.Code = gana.TALDIR_METHOD_NOT_SUPPORTED
     errDetail.Hint = "Unsupported method"
     errDetail.Detail = "Given method: " + vars["method"]
@@ -332,19 +338,18 @@ func registerRequest(w http.ResponseWriter, r 
*http.Request){
   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
+  hs_address := saltHAddress(validation.HAddress, t.Salt)
+  err = t.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
+    earliestReRegistration := entry.RegisteredAt + t.RequestFrequency
     // Rate limit re-registrations.
     if time.Now().UnixMicro() < earliestReRegistration {
       w.WriteHeader(429)
       rlResponse := RateLimitedResponse{
         Code: gana.TALDIR_REGISTER_RATE_LIMITED,
-        RequestFrequency: reqFrequency,
+        RequestFrequency: t.RequestFrequency,
         Hint: "Registration rate limit reached",
       }
       jsonResp, _ := json.Marshal(rlResponse)
@@ -358,7 +363,7 @@ func registerRequest(w http.ResponseWriter, r 
*http.Request){
       return
     }
   }
-  err = db.First(&validation, "h_address = ?", validation.HAddress).Error
+  err = t.Db.First(&validation, "h_address = ?", validation.HAddress).Error
   validation.Code = util.GenerateCode()
   validation.Inbox = req.Inbox
   validation.Duration = req.Duration
@@ -367,34 +372,33 @@ func registerRequest(w http.ResponseWriter, r 
*http.Request){
     // 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
+    err = t.Db.Save(&validation).Error
   } else  {
-    err = db.Create(&validation).Error
+    err = t.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") {
+  if !t.Cfg.Section("taldir-" + vars["method"]).HasKey("command") {
     log.Fatal(err)
-    db.Delete(&validation)
+    t.Db.Delete(&validation)
     w.WriteHeader(500)
     return
   }
-  command := cfg.Section("taldir-" + vars["method"]).Key("command").String()
+  command := t.Cfg.Section("taldir-" + vars["method"]).Key("command").String()
   path, err := exec.LookPath(command)
   if err != nil {
     log.Println(err)
-    db.Delete(&validation)
+    t.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)
+    t.Db.Delete(&validation)
     w.WriteHeader(500)
     return
   }
@@ -406,20 +410,20 @@ func notImplemented(w http.ResponseWriter, r 
*http.Request) {
   return
 }
 
-func configResponse(w http.ResponseWriter, r *http.Request) {
+func (t *Taldir) configResponse(w http.ResponseWriter, r *http.Request) {
   meths := []Method{}
   i := 0
-  for key, _ := range validators {
+  for key, _ := range t.Validators {
     var meth Method
     meth.Name = key
-    meth.ChallengeFee = cfg.Section("taldir-" + 
key).Key("challenge_fee").MustString("1 Kudos")
+    meth.ChallengeFee = t.Cfg.Section("taldir-" + 
key).Key("challenge_fee").MustString("KUDOS:1")
     i++
     meths = append(meths, meth)
   }
   cfg := VersionResponse{
     Version: "0:0:0",
     Name: "taler-directory",
-    MonthlyFee: cfg.Section("taldir").Key("monthly_fee").MustString("1 Kudos"),
+    MonthlyFee: 
t.Cfg.Section("taldir").Key("monthly_fee").MustString("KUDOS:1"),
     Methods: meths,
   }
   w.Header().Set("Content-Type", "application/json")
@@ -427,7 +431,7 @@ func configResponse(w http.ResponseWriter, r *http.Request) 
{
   w.Write(response)
 }
 
-func validationPage(w http.ResponseWriter, r *http.Request) {
+func (t *Taldir) validationPage(w http.ResponseWriter, r *http.Request) {
   vars := mux.Vars(r)
   w.Header().Set("Content-Type", "text/html; charset=utf-8")
   var walletLink string
@@ -444,95 +448,92 @@ func validationPage(w http.ResponseWriter, r 
*http.Request) {
     "QRCode": template.URL("data:image/png;base64," + encodedPng),
     "WalletLink": template.URL(walletLink),
   }
-  validationTpl.Execute(w, fullData)
+  t.ValidationTpl.Execute(w, fullData)
   return
 }
 
-func handleRequests() {
-  myRouter := mux.NewRouter().StrictSlash(true)
+func (t *Taldir) ClearDatabase() {
+  t.Db.Where("1 = 1").Delete(&Entry{})
+  t.Db.Where("1 = 1").Delete(&Validation{})
+}
+
+func (t *Taldir) setupHandlers() {
+  t.Router = mux.NewRouter().StrictSlash(true)
 
   /* ToS API */
-  myRouter.HandleFunc("/terms", notImplemented).Methods("GET")
-  myRouter.HandleFunc("/privacy", notImplemented).Methods("GET")
+  t.Router.HandleFunc("/terms", notImplemented).Methods("GET")
+  t.Router.HandleFunc("/privacy", notImplemented).Methods("GET")
 
   /* Config API */
-  myRouter.HandleFunc("/config", configResponse).Methods("GET")
+  t.Router.HandleFunc("/config", t.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")
+  t.Router.HandleFunc("/{h_address}", t.getSingleEntry).Methods("GET")
+  t.Router.HandleFunc("/register/{method}", t.registerRequest).Methods("POST")
+  t.Router.HandleFunc("/register/{h_address}/{validation_code}", 
t.validationPage).Methods("GET")
+  t.Router.HandleFunc("/{h_address}", t.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")
-  flag.Parse()
-  cfgfile := "taldir.conf"
-  if len(*cfgFlag) != 0 {
-    cfgfile = *cfgFlag
-  }
+func (t *Taldir) handleRequests() {
+  
log.Fatal(http.ListenAndServe(t.Cfg.Section("taldir").Key("bind_to").MustString("localhost:11000"),
 t.Router))
+}
+
+func (t *Taldir) Initialize(cfgfile string, clearDb bool) {
   _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") 
+  t.Cfg = _cfg
+  if t.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
+  t.Validators = make(map[string]bool)
+  for _, a := range 
strings.Split(t.Cfg.Section("taldir").Key("validators").String(), " ") {
+    t.Validators[a] = true
   }
-  validators = make(map[string]bool)
-  for _, a := range 
strings.Split(cfg.Section("taldir").Key("validators").String(), " ") {
-    validators[a] = true
+  t.Validators = make(map[string]bool)
+  for _, a := range 
strings.Split(t.Cfg.Section("taldir").Key("validators").String(), " ") {
+    t.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"))
+  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{})
   if err != nil {
     panic(err)
   }
-  db = _db
-  if err := db.AutoMigrate(&Entry{}); err != nil {
+  t.Db = _db
+  if err := t.Db.AutoMigrate(&Entry{}); err != nil {
     panic(err)
   }
-  if err := db.AutoMigrate(&Validation{}); err != nil {
+  if err := t.Db.AutoMigrate(&Validation{}); err != nil {
     panic(err)
   }
-  if *dropFlag {
-    fmt.Println("Really delete all data in database? [y/N]:")
-    reader := bufio.NewReader(os.Stdin)
-    char, _, err := reader.ReadRune()
-
-    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{})
-      }
-      os.Exit(0)
-    }
-    os.Exit(1)
+  if clearDb {
+    t.ClearDatabase()
   }
 
-  validationTpl, err = template.ParseFiles("templates/validation_landing.html")
+  validationLandingTplFile := 
t.Cfg.Section("taldir").Key("validation_landing").MustString("templates/validation_landing.html")
+  t.ValidationTpl, err = template.ParseFiles(validationLandingTplFile)
   if err != nil {
     fmt.Println(err)
   }
-  handleRequests()
+  t.Salt = os.Getenv("TALDIR_SALT")
+  if "" == t.Salt {
+    t.Salt = t.Cfg.Section("taldir").Key("salt").MustString("ChangeMe")
+  }
+  t.RequestFrequency = 
t.Cfg.Section("taldir").Key("request_frequency").MustInt64(1000)
+  t.setupHandlers()
+}
+
+func (t *Taldir) Run() {
+  t.handleRequests()
 }
diff --git a/taldir.conf b/cmd/taldir-server/testdata/taldir-test.conf
similarity index 86%
copy from taldir.conf
copy to cmd/taldir-server/testdata/taldir-test.conf
index 9560e77..536a741 100644
--- a/taldir.conf
+++ b/cmd/taldir-server/testdata/taldir-test.conf
@@ -1,11 +1,12 @@
 [taldir]
 production = false
-validators = "email phone test"
+validators = "twitter test"
 host = "https://taldir.net";
 bind_to = "localhost:11000"
 salt = "ChangeMe"
 monthly_fee = KUDOS:1
 request_frequency = 3
+validation_landing = testdata/templates/validation_landing.html
 
 [taldir-email]
 sender = "taldir@taler.net"
diff --git a/templates/validation_landing.html 
b/cmd/taldir-server/testdata/templates/validation_landing.html
similarity index 100%
copy from templates/validation_landing.html
copy to cmd/taldir-server/testdata/templates/validation_landing.html
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]