[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-taldir] 02/02: go fmt
From: |
gnunet |
Subject: |
[taler-taldir] 02/02: go fmt |
Date: |
Mon, 18 Jul 2022 12:55:24 +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 194002465a2502bdf2ec815289d00b97757f9973
Author: Martin Schanzenbach <schanzen@gnunet.org>
AuthorDate: Mon Jul 18 12:55:18 2022 +0200
go fmt
---
cmd/taldir-cli/main.go | 75 ++-
cmd/taldir-server/main.go | 32 +-
cmd/taldir-server/main_test.go | 428 +++++++-------
pkg/rest/taldir.go | 1224 ++++++++++++++++++++--------------------
pkg/taler/merchant.go | 233 ++++----
pkg/util/helper.go | 94 ++-
6 files changed, 1033 insertions(+), 1053 deletions(-)
diff --git a/cmd/taldir-cli/main.go b/cmd/taldir-cli/main.go
index 3aa9c32..83ba368 100644
--- a/cmd/taldir-cli/main.go
+++ b/cmd/taldir-cli/main.go
@@ -16,52 +16,51 @@
//
// SPDX-License-Identifier: AGPL3.0-or-later
-
package main
import (
- "os"
- "fmt"
- "flag"
- "taler.net/taldir/pkg/rest"
- "taler.net/taldir/pkg/util"
- gnunetutil "git.gnunet.org/gnunet-go.git/pkg/util"
- "crypto/sha512"
+ "crypto/sha512"
+ "flag"
+ "fmt"
+ gnunetutil "git.gnunet.org/gnunet-go.git/pkg/util"
+ "os"
+ "taler.net/taldir/pkg/rest"
+ "taler.net/taldir/pkg/util"
)
// Generates a link from a challenge and address
func generateLink(host string, addr string, challenge string) string {
- h := sha512.New()
- h.Write([]byte(addr))
- h_addr := gnunetutil.EncodeBinaryToString(h.Sum(nil))
- return host + "/register/" + h_addr + "/" + challenge
+ h := sha512.New()
+ h.Write([]byte(addr))
+ h_addr := gnunetutil.EncodeBinaryToString(h.Sum(nil))
+ return host + "/register/" + h_addr + "/" + challenge
}
func main() {
- var solveFlag = flag.Bool("s", false, "Provide a solution for the
challenge/pubkey")
- var linkFlag = flag.Bool("l", false, "Provide a link for activation")
- var challengeFlag = flag.String("c", "", "Activation challenge")
- var pubkeyFlag = flag.String("p", "", "Public key")
- var addressFlag = flag.String("a", "", "Address")
- flag.Parse()
- cfgfile := "taldir.conf"
- t := taldir.Taldir{}
- t.Initialize(cfgfile)
- host := t.Cfg.Section("taldir").Key("host").MustString("http://localhost")
- if *solveFlag {
- if len(*challengeFlag) == 0 || len(*pubkeyFlag) == 0 {
- fmt.Println("You need to provide an activation challenge and a public
key to generate a solution")
- os.Exit(1)
- }
- fmt.Println(util.GenerateSolution(*pubkeyFlag, *challengeFlag))
- os.Exit(0)
- }
- if *linkFlag {
- if len(*challengeFlag) == 0 || len(*addressFlag) == 0 {
- fmt.Println("You need to provide an activation challenge and an address
to generate a link")
- os.Exit(1)
- }
- fmt.Println(generateLink(host, *addressFlag, *challengeFlag))
- os.Exit(0)
- }
+ var solveFlag = flag.Bool("s", false, "Provide a solution for the
challenge/pubkey")
+ var linkFlag = flag.Bool("l", false, "Provide a link for activation")
+ var challengeFlag = flag.String("c", "", "Activation challenge")
+ var pubkeyFlag = flag.String("p", "", "Public key")
+ var addressFlag = flag.String("a", "", "Address")
+ flag.Parse()
+ cfgfile := "taldir.conf"
+ t := taldir.Taldir{}
+ t.Initialize(cfgfile)
+ host :=
t.Cfg.Section("taldir").Key("host").MustString("http://localhost")
+ if *solveFlag {
+ if len(*challengeFlag) == 0 || len(*pubkeyFlag) == 0 {
+ fmt.Println("You need to provide an activation
challenge and a public key to generate a solution")
+ os.Exit(1)
+ }
+ fmt.Println(util.GenerateSolution(*pubkeyFlag, *challengeFlag))
+ os.Exit(0)
+ }
+ if *linkFlag {
+ if len(*challengeFlag) == 0 || len(*addressFlag) == 0 {
+ fmt.Println("You need to provide an activation
challenge and an address to generate a link")
+ os.Exit(1)
+ }
+ fmt.Println(generateLink(host, *addressFlag, *challengeFlag))
+ os.Exit(0)
+ }
}
diff --git a/cmd/taldir-server/main.go b/cmd/taldir-server/main.go
index 6f0b400..9d1a1f6 100644
--- a/cmd/taldir-server/main.go
+++ b/cmd/taldir-server/main.go
@@ -16,7 +16,6 @@
//
// SPDX-License-Identifier: AGPL3.0-or-later
-
package main
/* TODO
@@ -28,27 +27,28 @@ package main
*/
import (
- "flag"
- "net/http"
- "log"
- "taler.net/taldir/pkg/rest"
+ "flag"
+ "log"
+ "net/http"
+
+ taldir "taler.net/taldir/pkg/rest"
)
var t taldir.Taldir
func handleRequests() {
-
log.Fatal(http.ListenAndServe(t.Cfg.Section("taldir").Key("bind_to").MustString("localhost:11000"),
t.Router))
+
log.Fatal(http.ListenAndServe(t.Cfg.Section("taldir").Key("bind_to").MustString("localhost:11000"),
t.Router))
}
func main() {
- var cfgFlag = flag.String("c", "", "Configuration file to use")
-
- flag.Parse()
- cfgfile := "taldir.conf"
- if len(*cfgFlag) != 0 {
- cfgfile = *cfgFlag
- }
- t := taldir.Taldir{}
- t.Initialize(cfgfile)
- handleRequests()
+ var cfgFlag = flag.String("c", "", "Configuration file to use")
+
+ flag.Parse()
+ cfgfile := "taldir.conf"
+ if len(*cfgFlag) != 0 {
+ cfgfile = *cfgFlag
+ }
+ t := taldir.Taldir{}
+ t.Initialize(cfgfile)
+ handleRequests()
}
diff --git a/cmd/taldir-server/main_test.go b/cmd/taldir-server/main_test.go
index a61e425..ec475c8 100644
--- a/cmd/taldir-server/main_test.go
+++ b/cmd/taldir-server/main_test.go
@@ -16,23 +16,22 @@
//
// SPDX-License-Identifier: AGPL3.0-or-later
-
package main_test
import (
- "os"
- "testing"
- "net/http"
- "net/http/httptest"
- "crypto/sha512"
- "bytes"
- "strings"
- "io/ioutil"
- "taler.net/taldir/pkg/rest"
- _ "taler.net/taldir/cmd/taldir-server"
- gnunetutil "git.gnunet.org/gnunet-go.git/pkg/util"
- "github.com/jarcoal/httpmock"
- "taler.net/taldir/pkg/util"
+ "bytes"
+ "crypto/sha512"
+ gnunetutil "git.gnunet.org/gnunet-go.git/pkg/util"
+ "github.com/jarcoal/httpmock"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "strings"
+ _ "taler.net/taldir/cmd/taldir-server"
+ "taler.net/taldir/pkg/rest"
+ "taler.net/taldir/pkg/util"
+ "testing"
)
var t taldir.Taldir
@@ -70,253 +69,248 @@ var newOrderStatusUnpaidMockResponse = `
}
`
-
-
-
func TestMain(m *testing.M) {
- t.Initialize("testdata/taldir-test.conf")
- code := m.Run()
- t.ClearDatabase()
- os.Exit(code)
+ t.Initialize("testdata/taldir-test.conf")
+ code := m.Run()
+ t.ClearDatabase()
+ os.Exit(code)
}
func getHAddress(addr string) string {
- h := sha512.New()
- h.Write([]byte(addr))
- return gnunetutil.EncodeBinaryToString(h.Sum(nil))
+ h := sha512.New()
+ h.Write([]byte(addr))
+ return gnunetutil.EncodeBinaryToString(h.Sum(nil))
}
func TestNoEntry(s *testing.T) {
- t.ClearDatabase()
+ t.ClearDatabase()
- h_addr := getHAddress("jdoe@example.com")
- req, _ := http.NewRequest("GET", "/" + h_addr, nil)
- response := executeRequest(req)
+ 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)
- }
+ 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
+ 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)
- }
- file, err := os.Open("validation_code")
- if err != nil {
- s.Errorf("No validation code file found!\n")
- }
- code, err := ioutil.ReadAll(file)
- if err != nil {
- s.Errorf("Error reading validation code file contents!\n")
- }
- h_addr := getHAddress("abc@test")
- trimCode := strings.Trim(string(code), " \r\n")
- solution :=
util.GenerateSolution("000G006XE97PTWV3B7AJNCRQZA6BF26HPV3XZ07293FMY7KD4181946A90",
trimCode)
- solutionJSON := "{\"solution\": \"" + solution + "\"}"
- req, _ = http.NewRequest("POST", "/" + h_addr,
bytes.NewBuffer([]byte(solutionJSON)))
- response = executeRequest(req)
- if http.StatusNoContent != response.Code {
- s.Errorf("Expected response code %d. Got %d\n", http.StatusNoContent,
response.Code)
- }
+ 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)
+ }
+ file, err := os.Open("validation_code")
+ if err != nil {
+ s.Errorf("No validation code file found!\n")
+ }
+ code, err := ioutil.ReadAll(file)
+ if err != nil {
+ s.Errorf("Error reading validation code file contents!\n")
+ }
+ h_addr := getHAddress("abc@test")
+ trimCode := strings.Trim(string(code), " \r\n")
+ solution :=
util.GenerateSolution("000G006XE97PTWV3B7AJNCRQZA6BF26HPV3XZ07293FMY7KD4181946A90",
trimCode)
+ solutionJSON := "{\"solution\": \"" + solution + "\"}"
+ req, _ = http.NewRequest("POST", "/"+h_addr,
bytes.NewBuffer([]byte(solutionJSON)))
+ response = executeRequest(req)
+ if http.StatusNoContent != response.Code {
+ s.Errorf("Expected response code %d. Got %d\n",
http.StatusNoContent, response.Code)
+ }
}
func TestRegisterQRPageRequest(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)
- }
- req, _ = http.NewRequest("GET", "/register/NonSenseAddr/NonSenseCode", nil)
- response = executeRequest(req)
- if http.StatusNotFound != response.Code {
- s.Errorf("Expected response code %d. Got %d\n", http.StatusNotFound,
response.Code)
- }
-
- file, err := os.Open("validation_code")
- if err != nil {
- s.Errorf("No validation code file found!\n")
- }
- code, err := ioutil.ReadAll(file)
- if err != nil {
- s.Errorf("Error reading validation code file contents!\n")
- }
- h_addr := getHAddress("abc@test")
- trimCode := strings.Trim(string(code), " \r\n")
- req, _ = http.NewRequest("GET", "/register/" + h_addr + "/" + trimCode, nil)
- response = executeRequest(req)
- if http.StatusOK != response.Code {
- s.Errorf("Expected response code %d. Got %d\n", http.StatusOK,
response.Code)
- }
+ 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)
+ }
+ req, _ = http.NewRequest("GET", "/register/NonSenseAddr/NonSenseCode",
nil)
+ response = executeRequest(req)
+ if http.StatusNotFound != response.Code {
+ s.Errorf("Expected response code %d. Got %d\n",
http.StatusNotFound, response.Code)
+ }
+
+ file, err := os.Open("validation_code")
+ if err != nil {
+ s.Errorf("No validation code file found!\n")
+ }
+ code, err := ioutil.ReadAll(file)
+ if err != nil {
+ s.Errorf("Error reading validation code file contents!\n")
+ }
+ h_addr := getHAddress("abc@test")
+ trimCode := strings.Trim(string(code), " \r\n")
+ req, _ = http.NewRequest("GET", "/register/"+h_addr+"/"+trimCode, nil)
+ response = executeRequest(req)
+ if http.StatusOK != response.Code {
+ s.Errorf("Expected response code %d. Got %d\n", http.StatusOK,
response.Code)
+ }
}
-
func TestReRegisterRequest(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)
- }
- file, err := os.Open("validation_code")
- if err != nil {
- s.Errorf("No validation code file found!\n")
- }
- code, err := ioutil.ReadAll(file)
- if err != nil {
- s.Errorf("Error reading validation code file contents!\n")
- }
- h_addr := getHAddress("abc@test")
- trimCode := strings.Trim(string(code), " \r\n")
- solution :=
util.GenerateSolution("000G006XE97PTWV3B7AJNCRQZA6BF26HPV3XZ07293FMY7KD4181946A90",
trimCode)
- solutionJSON := "{\"solution\": \"" + solution + "\"}"
- req, _ = http.NewRequest("POST", "/" + h_addr,
bytes.NewBuffer([]byte(solutionJSON)))
- response = executeRequest(req)
- if http.StatusNoContent != response.Code {
- s.Errorf("Expected response code %d. Got %d\n", http.StatusNoContent,
response.Code)
- }
- req, _ = http.NewRequest("POST", "/register/test",
bytes.NewBuffer(validRegisterRequestUnmodified))
- response = executeRequest(req)
-
- if http.StatusOK != response.Code {
- s.Errorf("Expected response code %d. Got %d\n", http.StatusOK,
response.Code)
- }
+ 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)
+ }
+ file, err := os.Open("validation_code")
+ if err != nil {
+ s.Errorf("No validation code file found!\n")
+ }
+ code, err := ioutil.ReadAll(file)
+ if err != nil {
+ s.Errorf("Error reading validation code file contents!\n")
+ }
+ h_addr := getHAddress("abc@test")
+ trimCode := strings.Trim(string(code), " \r\n")
+ solution :=
util.GenerateSolution("000G006XE97PTWV3B7AJNCRQZA6BF26HPV3XZ07293FMY7KD4181946A90",
trimCode)
+ solutionJSON := "{\"solution\": \"" + solution + "\"}"
+ req, _ = http.NewRequest("POST", "/"+h_addr,
bytes.NewBuffer([]byte(solutionJSON)))
+ response = executeRequest(req)
+ if http.StatusNoContent != response.Code {
+ s.Errorf("Expected response code %d. Got %d\n",
http.StatusNoContent, response.Code)
+ }
+ req, _ = http.NewRequest("POST", "/register/test",
bytes.NewBuffer(validRegisterRequestUnmodified))
+ response = executeRequest(req)
+
+ if http.StatusOK != response.Code {
+ s.Errorf("Expected response code %d. Got %d\n", http.StatusOK,
response.Code)
+ }
}
func TestReRegisterRequestTooMany(s *testing.T) {
- t.ClearDatabase()
+ t.ClearDatabase()
- req, _ := http.NewRequest("POST", "/register/test",
bytes.NewBuffer(validRegisterRequest))
- response := executeRequest(req)
+ 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)
- }
- 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)
+ }
+ 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)
- }
- 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)
+ }
+ 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)
- }
- 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)
+ }
+ req, _ = http.NewRequest("POST", "/register/test",
bytes.NewBuffer(validRegisterRequest))
+ response = executeRequest(req)
- if http.StatusTooManyRequests != response.Code {
- s.Errorf("Expected response code %d. Got %d\n",
http.StatusTooManyRequests, response.Code)
- }
+ if http.StatusTooManyRequests != response.Code {
+ s.Errorf("Expected response code %d. Got %d\n",
http.StatusTooManyRequests, response.Code)
+ }
}
func TestSolutionRequestTooMany(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)
- }
- h_addr := getHAddress("abc@test")
- solution :=
util.GenerateSolution("000G006XE97PTWV3B7AJNCRQZA6BF26HPV3XZ07293FMY7KD4181946A90",
"wrongSolution")
- solutionJSON := "{\"solution\": \"" + solution + "\"}"
- req, _ = http.NewRequest("POST", "/" + h_addr,
bytes.NewBuffer([]byte(solutionJSON)))
- response = executeRequest(req)
- if http.StatusForbidden != response.Code {
- s.Errorf("Expected response code %d. Got %d\n", http.StatusForbidden,
response.Code)
- }
- req, _ = http.NewRequest("POST", "/" + h_addr,
bytes.NewBuffer([]byte(solutionJSON)))
- response = executeRequest(req)
- if http.StatusForbidden != response.Code {
- s.Errorf("Expected response code %d. Got %d\n", http.StatusForbidden,
response.Code)
- }
- req, _ = http.NewRequest("POST", "/" + h_addr,
bytes.NewBuffer([]byte(solutionJSON)))
- response = executeRequest(req)
- if http.StatusForbidden != response.Code {
- s.Errorf("Expected response code %d. Got %d\n", http.StatusForbidden,
response.Code)
- }
- req, _ = http.NewRequest("POST", "/" + h_addr,
bytes.NewBuffer([]byte(solutionJSON)))
- response = executeRequest(req)
- if http.StatusTooManyRequests != response.Code {
- s.Errorf("Expected response code %d. Got %d\n",
http.StatusTooManyRequests, response.Code)
- }
+ 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)
+ }
+ h_addr := getHAddress("abc@test")
+ solution :=
util.GenerateSolution("000G006XE97PTWV3B7AJNCRQZA6BF26HPV3XZ07293FMY7KD4181946A90",
"wrongSolution")
+ solutionJSON := "{\"solution\": \"" + solution + "\"}"
+ req, _ = http.NewRequest("POST", "/"+h_addr,
bytes.NewBuffer([]byte(solutionJSON)))
+ response = executeRequest(req)
+ if http.StatusForbidden != response.Code {
+ s.Errorf("Expected response code %d. Got %d\n",
http.StatusForbidden, response.Code)
+ }
+ req, _ = http.NewRequest("POST", "/"+h_addr,
bytes.NewBuffer([]byte(solutionJSON)))
+ response = executeRequest(req)
+ if http.StatusForbidden != response.Code {
+ s.Errorf("Expected response code %d. Got %d\n",
http.StatusForbidden, response.Code)
+ }
+ req, _ = http.NewRequest("POST", "/"+h_addr,
bytes.NewBuffer([]byte(solutionJSON)))
+ response = executeRequest(req)
+ if http.StatusForbidden != response.Code {
+ s.Errorf("Expected response code %d. Got %d\n",
http.StatusForbidden, response.Code)
+ }
+ req, _ = http.NewRequest("POST", "/"+h_addr,
bytes.NewBuffer([]byte(solutionJSON)))
+ response = executeRequest(req)
+ if http.StatusTooManyRequests != response.Code {
+ s.Errorf("Expected response code %d. Got %d\n",
http.StatusTooManyRequests, response.Code)
+ }
}
func TestRegisterRequestWrongPubkey(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)
- }
- file, err := os.Open("validation_code")
- if err != nil {
- s.Errorf("No validation code file found!\n")
- }
- code, err := ioutil.ReadAll(file)
- if err != nil {
- s.Errorf("Error reading validation code file contents!\n")
- }
- h_addr := getHAddress("abc@test")
- trimCode := strings.Trim(string(code), " \r\n")
- solution :=
util.GenerateSolution("000G006XE97PTWV3B7AJNCRQZA6BF26HPV3XZ07293FMY7KD4181946AA0",
trimCode)
- solutionJSON := "{\"solution\": \"" + solution + "\"}"
- req, _ = http.NewRequest("POST", "/" + h_addr,
bytes.NewBuffer([]byte(solutionJSON)))
- response = executeRequest(req)
- if http.StatusForbidden != response.Code {
- s.Errorf("Expected response code %d. Got %d\n", http.StatusForbidden,
response.Code)
- }
+ 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)
+ }
+ file, err := os.Open("validation_code")
+ if err != nil {
+ s.Errorf("No validation code file found!\n")
+ }
+ code, err := ioutil.ReadAll(file)
+ if err != nil {
+ s.Errorf("Error reading validation code file contents!\n")
+ }
+ h_addr := getHAddress("abc@test")
+ trimCode := strings.Trim(string(code), " \r\n")
+ solution :=
util.GenerateSolution("000G006XE97PTWV3B7AJNCRQZA6BF26HPV3XZ07293FMY7KD4181946AA0",
trimCode)
+ solutionJSON := "{\"solution\": \"" + solution + "\"}"
+ req, _ = http.NewRequest("POST", "/"+h_addr,
bytes.NewBuffer([]byte(solutionJSON)))
+ response = executeRequest(req)
+ if http.StatusForbidden != response.Code {
+ s.Errorf("Expected response code %d. Got %d\n",
http.StatusForbidden, response.Code)
+ }
}
-
func TestUnsupportedMethod(s *testing.T) {
- t.ClearDatabase()
+ t.ClearDatabase()
- req, _ := http.NewRequest("POST", "/register/email",
bytes.NewBuffer(validRegisterRequest))
- response := executeRequest(req)
+ req, _ := http.NewRequest("POST", "/register/email",
bytes.NewBuffer(validRegisterRequest))
+ response := executeRequest(req)
- if http.StatusNotFound != response.Code {
- s.Errorf("Expected response code %d. Got %d\n", http.StatusNotFound,
response.Code)
- }
+ if http.StatusNotFound != response.Code {
+ s.Errorf("Expected response code %d. Got %d\n",
http.StatusNotFound, response.Code)
+ }
}
func TestPaymentRequiredMethod(s *testing.T) {
- t.ClearDatabase()
- t.MonthlyFee = "KUDOS:5"
- httpmock.Activate()
- defer httpmock.DeactivateAndReset()
- req, _ := http.NewRequest("POST", "/register/test-cost",
bytes.NewBuffer(validRegisterRequest))
- httpmock.RegisterResponder("POST",
"http://merchant.taldir/instances/myInstance/private/orders",
httpmock.NewStringResponder(200, newOrderMockResponse))
- httpmock.RegisterResponder("GET",
"http://merchant.taldir/instances/myInstance/private/orders/testOrder1234",
httpmock.NewStringResponder(200, newOrderStatusUnpaidMockResponse))
-
- response := executeRequest(req)
- t.MonthlyFee = "KUDOS:0"
- if http.StatusPaymentRequired != response.Code {
- s.Errorf("Expected response code %d. Got %d\n",
http.StatusPaymentRequired, response.Code)
- }
+ t.ClearDatabase()
+ t.MonthlyFee = "KUDOS:5"
+ httpmock.Activate()
+ defer httpmock.DeactivateAndReset()
+ req, _ := http.NewRequest("POST", "/register/test-cost",
bytes.NewBuffer(validRegisterRequest))
+ httpmock.RegisterResponder("POST",
"http://merchant.taldir/instances/myInstance/private/orders",
httpmock.NewStringResponder(200, newOrderMockResponse))
+ httpmock.RegisterResponder("GET",
"http://merchant.taldir/instances/myInstance/private/orders/testOrder1234",
httpmock.NewStringResponder(200, newOrderStatusUnpaidMockResponse))
+
+ response := executeRequest(req)
+ t.MonthlyFee = "KUDOS:0"
+ if http.StatusPaymentRequired != response.Code {
+ s.Errorf("Expected response code %d. Got %d\n",
http.StatusPaymentRequired, response.Code)
+ }
}
diff --git a/pkg/rest/taldir.go b/pkg/rest/taldir.go
index 160d1d8..afe23d9 100644
--- a/pkg/rest/taldir.go
+++ b/pkg/rest/taldir.go
@@ -19,137 +19,134 @@
package taldir
/* TODO
- - ToS compression
- - ToS etag
+- ToS compression
+- ToS etag
*/
import (
- "os"
- "os/exec"
- "time"
- "fmt"
- "log"
- "io/ioutil"
- "mime"
- "net/http"
- "html/template"
- "encoding/json"
- "github.com/gorilla/mux"
- "gorm.io/gorm"
- "gorm.io/gorm/logger"
- "encoding/base64"
- "taler.net/taldir/pkg/util"
- "taler.net/taldir/pkg/gana"
- "taler.net/taldir/pkg/taler"
- "crypto/sha512"
- "gorm.io/driver/postgres"
- "gopkg.in/ini.v1"
- "strings"
- "github.com/skip2/go-qrcode"
- gnunetutil "git.gnunet.org/gnunet-go.git/pkg/util"
- "golang.org/x/text/language"
+ "crypto/sha512"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ gnunetutil "git.gnunet.org/gnunet-go.git/pkg/util"
+ "github.com/gorilla/mux"
+ "github.com/skip2/go-qrcode"
+ "golang.org/x/text/language"
+ "gopkg.in/ini.v1"
+ "gorm.io/driver/postgres"
+ "gorm.io/gorm"
+ "gorm.io/gorm/logger"
+ "html/template"
+ "io/ioutil"
+ "log"
+ "mime"
+ "net/http"
+ "os"
+ "os/exec"
+ "strings"
+ "taler.net/taldir/pkg/gana"
+ "taler.net/taldir/pkg/taler"
+ "taler.net/taldir/pkg/util"
+ "time"
)
// Taldir is the primary object of the Taldir service
type Taldir struct {
- // The main router
- Router *mux.Router
+ // The main router
+ Router *mux.Router
- // The main DB handle
- Db *gorm.DB
+ // The main DB handle
+ Db *gorm.DB
- // Our configuration from the config.json
- Cfg *ini.File
+ // Our configuration from the config.json
+ Cfg *ini.File
- // Map of supported validators as defined in the configuration
- Validators map[string]bool
+ // Map of supported validators as defined in the configuration
+ Validators map[string]bool
- // landing page
- ValidationTpl *template.Template
+ // landing page
+ ValidationTpl *template.Template
- // The address salt
- Salt string
+ // The address salt
+ Salt string
- // The timeframe for the validation requests
- ValidationTimeframe time.Duration
+ // The timeframe for the validation requests
+ ValidationTimeframe time.Duration
- // How often may a challenge be requested
- ValidationInitiationMax int64
+ // How often may a challenge be requested
+ ValidationInitiationMax int64
- // How often may a solution be attempted (in the given timeframe)
- SolutionAttemptsMax int
+ // How often may a solution be attempted (in the given timeframe)
+ SolutionAttemptsMax int
- // The timeframe for the above solution attempts
- SolutionTimeframe time.Duration
+ // The timeframe for the above solution attempts
+ SolutionTimeframe time.Duration
+ // Challenge length in bytes before encoding
+ ChallengeBytes int
- // Challenge length in bytes before encoding
- ChallengeBytes int
+ // Merchant object
+ Merchant taler.Merchant
- // Merchant object
- Merchant taler.Merchant
-
- // Monthly fee amount
- MonthlyFee string
+ // Monthly fee amount
+ MonthlyFee string
}
// VersionResponse is the JSON response of the /config enpoint
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"
+ // 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"`
- // Supported registration methods
- Methods []Method `json:"methods"`
+ // Name of the protocol.
+ Name string `json:"name"` // "taler-directory"
- // fee for one month of registration
- MonthlyFee string `json:"monthly_fee"`
+ // Supported registration methods
+ Methods []Method `json:"methods"`
+ // fee for one month of registration
+ MonthlyFee string `json:"monthly_fee"`
}
// Method is part of the VersionResponse and contains a supported validator
type Method struct {
- // Name of the method, e.g. "email" or "sms".
- Name string `json:"name"`
-
- // per challenge fee
- ChallengeFee string `json:"challenge_fee"`
+ // Name of the method, e.g. "email" or "sms".
+ Name string `json:"name"`
+ // per challenge fee
+ ChallengeFee string `json:"challenge_fee"`
}
// RateLimitedResponse is the JSON response when a rate limit is hit
type RateLimitedResponse struct {
- // Taler error code, TALER_EC_TALDIR_REGISTER_RATE_LIMITED.
- Code int `json:"code"`
+ // 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"`
+ // 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"`
+ // The human readable error message.
+ Hint string `json:"hint"`
}
// RegisterMessage is the JSON paylaod when a registration is requested
type RegisterMessage struct {
- // Address, in method-specific format
- Address string `json:"address"`
+ // Address, in method-specific format
+ Address string `json:"address"`
- // Public key of the user to register
- PublicKey string `json:"public_key"`
+ // 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"`
+ // (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"`
+ // For how long should the registration last
+ Duration int64 `json:"duration"`
}
// Entry is a mapping from the identity key hash to a wallet key
@@ -157,20 +154,20 @@ type RegisterMessage struct {
// one of the identity key types supported (e.g. an email address)
type entry struct {
- // ORM
- gorm.Model `json:"-"`
+ // ORM
+ gorm.Model `json:"-"`
- // The salted hash (SHA512) of the hashed address (h_address)
- HsAddress string `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"`
+ // (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"`
+ // Public key of the user to register in base32
+ PublicKey string `json:"public_key"`
- // How long the registration lasts in microseconds
- Duration time.Duration `json:"-"`
+ // How long the registration lasts in microseconds
+ Duration time.Duration `json:"-"`
}
// Validation is the object created when a registration for an entry is
initiated.
@@ -179,86 +176,85 @@ type entry struct {
// depending on the out-of-band chennel defined through the identity key type.
type validation struct {
- // ORM
- gorm.Model `json:"-"`
+ // ORM
+ gorm.Model `json:"-"`
- // The hash (SHA512) of the address
- HAddress string `json:"h_address"`
+ // The hash (SHA512) of the address
+ HAddress string `json:"h_address"`
- // For how long should the registration last
- Duration int64 `json:"duration"`
+ // 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"`
+ // (HTTPS) endpoint URL for the inbox service for this address
+ Inbox string `json:"inbox_url"`
- // The activation code sent to the client
- Challenge string `json:"-"`
+ // The activation code sent to the client
+ Challenge string `json:"-"`
- // The challenge has been sent already
- ChallengeSent bool `json:"-"`
+ // The challenge has been sent already
+ ChallengeSent bool `json:"-"`
- // Public key of the user to register
- PublicKey string `json:"public_key"`
+ // Public key of the user to register
+ PublicKey string `json:"public_key"`
- // How often was a solution for this validation tried
- SolutionAttemptCount int
+ // How often was a solution for this validation tried
+ SolutionAttemptCount int
- // The beginning of the last solution timeframe
- LastSolutionTimeframeStart time.Time
+ // The beginning of the last solution timeframe
+ LastSolutionTimeframeStart time.Time
- // The order ID associated with this validation
- OrderID string `json:"-"`
+ // The order ID associated with this validation
+ OrderID string `json:"-"`
}
-
// ErrorDetail is the detailed error payload returned from Taldir endpoints
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"`
+ // 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"`
+ // 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"`
+ // 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"`
+ // 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"`
+ // 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"`
+ // 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"`
+ // 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 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"`
+ // Name of the currency than was problematic (if applicable).
+ Currency string `json:"currency,omitempty"`
- // Expected type (if applicable).
- TypeExpected string `json:"type_expected,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 that was provided instead (if applicable).
+ TypeActual string `json:"type_actual,omitempty"`
}
// ValidationConfirmation is the payload sent by the client t complete a
// registration.
type ValidationConfirmation struct {
- // The solution is the SHA-512 hash of the challenge value
- // chosen by TalDir (encoded as string just as given in the URL, but
- // excluding the 0-termination) concatenated with the binary 32-byte
- // value representing the wallet's EdDSA public key.
- // The hash is provided as string in Crockford base32 encoding.
- Solution string `json:"solution"`
+ // The solution is the SHA-512 hash of the challenge value
+ // chosen by TalDir (encoded as string just as given in the URL, but
+ // excluding the 0-termination) concatenated with the binary 32-byte
+ // value representing the wallet's EdDSA public key.
+ // The hash is provided as string in Crockford base32 encoding.
+ Solution string `json:"solution"`
}
// NOTE: Go stores durations as nanoseconds. TalDir usually operates on
microseconds
@@ -267,520 +263,518 @@ const monthDurationUs = 2592000000000
// 1 Month as Go duration
const monthDuration = time.Duration(monthDurationUs * 1000)
-
// Primary lookup function.
// Allows the caller to query a wallet key using the hash(!) of the
// identity, e.g. SHA512(<email address>)
-func (t *Taldir) getSingleEntry(w http.ResponseWriter, r *http.Request){
- vars := mux.Vars(r)
- var entry entry
- hsAddress := saltHAddress(vars["h_address"], t.Salt)
- var err = t.Db.First(&entry, "hs_address = ?", hsAddress).Error
- if err == nil {
- w.Header().Set("Content-Type", "application/json")
- resp, _ := json.Marshal(entry)
- w.Write(resp)
- return
- }
- w.WriteHeader(http.StatusNotFound)
+func (t *Taldir) getSingleEntry(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ var entry entry
+ hsAddress := saltHAddress(vars["h_address"], t.Salt)
+ var err = t.Db.First(&entry, "hs_address = ?", hsAddress).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(hAddress string, salt string) string {
- h := sha512.New()
- h.Write([]byte(hAddress))
- h.Write([]byte(salt))
- return gnunetutil.EncodeBinaryToString(h.Sum(nil))
+ h := sha512.New()
+ h.Write([]byte(hAddress))
+ h.Write([]byte(salt))
+ return gnunetutil.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 (t *Taldir) 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 = t.Db.First(&validation, "h_address = ?", vars["h_address"]).Error
- if err != nil {
- w.WriteHeader(http.StatusNotFound)
- return
- }
- validation.SolutionAttemptCount++
- if
validation.LastSolutionTimeframeStart.Add(t.SolutionTimeframe).After(time.Now())
{
- if validation.SolutionAttemptCount > t.SolutionAttemptsMax {
- w.WriteHeader(429)
- return
- }
- } else {
- log.Println("New solution timeframe set.")
- validation.LastSolutionTimeframeStart = time.Now()
- validation.SolutionAttemptCount = 1
- }
- t.Db.Save(&validation)
- expectedSolution := util.GenerateSolution(validation.PublicKey,
validation.Challenge)
- if confirm.Solution != expectedSolution {
- w.WriteHeader(http.StatusForbidden)
- return
- }
- err = t.Db.Delete(&validation).Error
- if err != nil {
- log.Fatalf("Error deleting validation")
- w.WriteHeader(http.StatusInternalServerError)
- return
- }
- entry.HsAddress = saltHAddress(validation.HAddress, t.Salt)
- entry.Inbox = validation.Inbox
- tmpDuration := (entry.Duration.Microseconds() + validation.Duration) * 1000
- entry.Duration = time.Duration(tmpDuration)
- entry.PublicKey = validation.PublicKey
- err = t.Db.First(&entry, "hs_address = ?", entry.HsAddress).Error
- if err == nil {
- t.Db.Save(&entry)
- } else {
- err = t.Db.Create(&entry).Error
- if err != nil {
- w.WriteHeader(http.StatusInternalServerError)
- return
- }
- }
- w.WriteHeader(http.StatusNoContent)
+func (t *Taldir) 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 = t.Db.First(&validation, "h_address = ?", vars["h_address"]).Error
+ if err != nil {
+ w.WriteHeader(http.StatusNotFound)
+ return
+ }
+ validation.SolutionAttemptCount++
+ if
validation.LastSolutionTimeframeStart.Add(t.SolutionTimeframe).After(time.Now())
{
+ if validation.SolutionAttemptCount > t.SolutionAttemptsMax {
+ w.WriteHeader(429)
+ return
+ }
+ } else {
+ log.Println("New solution timeframe set.")
+ validation.LastSolutionTimeframeStart = time.Now()
+ validation.SolutionAttemptCount = 1
+ }
+ t.Db.Save(&validation)
+ expectedSolution := util.GenerateSolution(validation.PublicKey,
validation.Challenge)
+ if confirm.Solution != expectedSolution {
+ w.WriteHeader(http.StatusForbidden)
+ return
+ }
+ err = t.Db.Delete(&validation).Error
+ if err != nil {
+ log.Fatalf("Error deleting validation")
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ entry.HsAddress = saltHAddress(validation.HAddress, t.Salt)
+ entry.Inbox = validation.Inbox
+ tmpDuration := (entry.Duration.Microseconds() + validation.Duration) *
1000
+ entry.Duration = time.Duration(tmpDuration)
+ entry.PublicKey = validation.PublicKey
+ err = t.Db.First(&entry, "hs_address = ?", entry.HsAddress).Error
+ if err == nil {
+ t.Db.Save(&entry)
+ } else {
+ err = t.Db.Create(&entry).Error
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ }
+ w.WriteHeader(http.StatusNoContent)
}
func (t *Taldir) isRateLimited(hAddress string) (bool, error) {
- var validations []validation
- res := t.Db.Where("h_address = ?", hAddress).Find(&validations)
- // NOTE: Check rate limit
- if res.Error == nil {
- // Limit re-initiation attempts to ValidationInitiationMax times
- // within the expiration timeframe of a validation.
- return res.RowsAffected >= t.ValidationInitiationMax, nil
- }
- return false, nil
+ var validations []validation
+ res := t.Db.Where("h_address = ?", hAddress).Find(&validations)
+ // NOTE: Check rate limit
+ if res.Error == nil {
+ // Limit re-initiation attempts to ValidationInitiationMax times
+ // within the expiration timeframe of a validation.
+ return res.RowsAffected >= t.ValidationInitiationMax, nil
+ }
+ return false, nil
}
-func (t *Taldir) registerRequest(w http.ResponseWriter, r *http.Request){
- vars := mux.Vars(r)
- var req RegisterMessage
- var errDetail ErrorDetail
- var validation validation
- var entry entry
- // Check if this validation method is supported or not.
- if !t.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(http.StatusNotFound)
- w.Write(resp)
- return
- }
- 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
- }
-
- // Setup validation object. Retrieve object from DB if it already
- // exists.
- h := sha512.New()
- h.Write([]byte(req.Address))
- hAddress := gnunetutil.EncodeBinaryToString(h.Sum(nil))
- validation.HAddress = hAddress
- hsAddress := saltHAddress(validation.HAddress, t.Salt)
- err = t.Db.First(&entry, "hs_address = ?", hsAddress).Error
- // Round to the nearest multiple of a month
- reqDuration := time.Duration(req.Duration * 1000)
- reqDuration = reqDuration.Round(monthDuration)
- if err == nil {
- // Check if this entry is to be modified or extended
- entryModified := (req.Inbox != entry.Inbox) ||
- (req.PublicKey != entry.PublicKey)
- entryValidity := entry.CreatedAt.Add(entry.Duration)
- // NOTE: The extension must be at least one month
- if (reqDuration.Microseconds() == 0) && !entryModified {
- // Nothing changed. Return validity
- w.WriteHeader(http.StatusOK)
- w.Header().Set("Content-Type", "application/json")
- w.Write([]byte(fmt.Sprintf("{\"valid_for\": %d}",
time.Until(entryValidity).Microseconds())))
- return
- }
- }
- rateLimited, err := t.isRateLimited(hAddress)
- if nil != err {
- log.Printf("Error checking rate limit! %w", err)
- w.WriteHeader(http.StatusInternalServerError)
- return
- } else if rateLimited {
- w.WriteHeader(http.StatusTooManyRequests)
- rlResponse := RateLimitedResponse{
- Code: gana.TALDIR_REGISTER_RATE_LIMITED,
- RequestFrequency: t.ValidationTimeframe.Microseconds() /
t.ValidationInitiationMax,
- Hint: "Registration rate limit reached",
- }
- jsonResp, _ := json.Marshal(rlResponse)
- w.Write(jsonResp)
- t.Db.Delete(&validation)
- return
- }
- err = t.Db.First(&validation, "h_address = ? AND public_key = ? AND inbox =
? AND duration = ?",
- hAddress, req.PublicKey, req.Inbox,
reqDuration).Error
- validationExists := (nil == err)
- // FIXME: Always set new challenge?
- validation.Challenge = util.GenerateChallenge(t.ChallengeBytes)
- if !validationExists {
- validation.Inbox = req.Inbox
- validation.PublicKey = req.PublicKey
- validation.SolutionAttemptCount = 0
- validation.LastSolutionTimeframeStart = time.Now()
- validation.Duration = reqDuration.Microseconds()
- }
-
- fixedCost := t.Cfg.Section("taldir-" +
vars["method"]).Key("challenge_fee").MustString("KUDOS:0")
- sliceDuration := time.Duration(validation.Duration * 1000)
- cost, err := util.CalculateCost(t.MonthlyFee,
- fixedCost,
- sliceDuration,
- monthDuration)
- if err != nil {
- fmt.Println(err)
- w.WriteHeader(http.StatusInternalServerError)
- return
- }
- if !cost.IsZero() {
- if len(validation.OrderID) == 0 {
- // Add new order for new validations
- orderID, newOrderErr := t.Merchant.AddNewOrder(*cost)
- if newOrderErr != nil {
- fmt.Println(newOrderErr)
- w.WriteHeader(http.StatusInternalServerError)
- return
- }
- validation.OrderID = orderID
- }
-
- // Check if order paid.
- // FIXME: Remember that it was activated and paid
- payto, paytoErr := t.Merchant.IsOrderPaid(validation.OrderID)
- if paytoErr != nil {
- fmt.Println(paytoErr)
- w.WriteHeader(http.StatusInternalServerError)
- log.Println(paytoErr)
- return
- }
- if len(payto) != 0 {
- t.Db.Save(&validation)
- w.WriteHeader(http.StatusPaymentRequired)
- w.Header().Set("Taler", payto) // FIXME no idea what to do with this.
- return
- }
- // In this case, this order was paid
- }
- if validationExists {
- err = t.Db.Save(&validation).Error
- } else {
- err = t.Db.Create(&validation).Error
- }
- if err != nil {
- log.Println(err)
- w.WriteHeader(500)
- return
- }
- // Some validation methods are costly
- // Require explicit whitelisting for a resend.
- if validation.ChallengeSent &&
- !t.Cfg.Section("taldir-" +
vars["method"]).Key("allow_resend").MustBool(false) {
- w.WriteHeader(202)
- return
- }
- if !t.Cfg.Section("taldir-" + vars["method"]).HasKey("command") {
- log.Fatal(err)
- t.Db.Delete(&validation)
- w.WriteHeader(500)
- return
- }
- command := t.Cfg.Section("taldir-" + vars["method"]).Key("command").String()
- path, err := exec.LookPath(command)
- if err != nil {
- log.Println(err)
- t.Db.Delete(&validation)
- w.WriteHeader(500)
- return
- }
- out, err := exec.Command(path, req.Address, validation.Challenge).Output()
- if err != nil {
- log.Printf("%s, %w", out, err)
- t.Db.Delete(&validation)
- w.WriteHeader(500)
- return
- }
- validation.ChallengeSent = true
- w.WriteHeader(202)
+func (t *Taldir) registerRequest(w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ var req RegisterMessage
+ var errDetail ErrorDetail
+ var validation validation
+ var entry entry
+ // Check if this validation method is supported or not.
+ if !t.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(http.StatusNotFound)
+ w.Write(resp)
+ return
+ }
+ 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
+ }
+
+ // Setup validation object. Retrieve object from DB if it already
+ // exists.
+ h := sha512.New()
+ h.Write([]byte(req.Address))
+ hAddress := gnunetutil.EncodeBinaryToString(h.Sum(nil))
+ validation.HAddress = hAddress
+ hsAddress := saltHAddress(validation.HAddress, t.Salt)
+ err = t.Db.First(&entry, "hs_address = ?", hsAddress).Error
+ // Round to the nearest multiple of a month
+ reqDuration := time.Duration(req.Duration * 1000)
+ reqDuration = reqDuration.Round(monthDuration)
+ if err == nil {
+ // Check if this entry is to be modified or extended
+ entryModified := (req.Inbox != entry.Inbox) ||
+ (req.PublicKey != entry.PublicKey)
+ entryValidity := entry.CreatedAt.Add(entry.Duration)
+ // NOTE: The extension must be at least one month
+ if (reqDuration.Microseconds() == 0) && !entryModified {
+ // Nothing changed. Return validity
+ w.WriteHeader(http.StatusOK)
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte(fmt.Sprintf("{\"valid_for\": %d}",
time.Until(entryValidity).Microseconds())))
+ return
+ }
+ }
+ rateLimited, err := t.isRateLimited(hAddress)
+ if nil != err {
+ log.Printf("Error checking rate limit! %w", err)
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ } else if rateLimited {
+ w.WriteHeader(http.StatusTooManyRequests)
+ rlResponse := RateLimitedResponse{
+ Code: gana.TALDIR_REGISTER_RATE_LIMITED,
+ RequestFrequency: t.ValidationTimeframe.Microseconds()
/ t.ValidationInitiationMax,
+ Hint: "Registration rate limit reached",
+ }
+ jsonResp, _ := json.Marshal(rlResponse)
+ w.Write(jsonResp)
+ t.Db.Delete(&validation)
+ return
+ }
+ err = t.Db.First(&validation, "h_address = ? AND public_key = ? AND
inbox = ? AND duration = ?",
+ hAddress, req.PublicKey, req.Inbox, reqDuration).Error
+ validationExists := (nil == err)
+ // FIXME: Always set new challenge?
+ validation.Challenge = util.GenerateChallenge(t.ChallengeBytes)
+ if !validationExists {
+ validation.Inbox = req.Inbox
+ validation.PublicKey = req.PublicKey
+ validation.SolutionAttemptCount = 0
+ validation.LastSolutionTimeframeStart = time.Now()
+ validation.Duration = reqDuration.Microseconds()
+ }
+
+ fixedCost := t.Cfg.Section("taldir-" +
vars["method"]).Key("challenge_fee").MustString("KUDOS:0")
+ sliceDuration := time.Duration(validation.Duration * 1000)
+ cost, err := util.CalculateCost(t.MonthlyFee,
+ fixedCost,
+ sliceDuration,
+ monthDuration)
+ if err != nil {
+ fmt.Println(err)
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ if !cost.IsZero() {
+ if len(validation.OrderID) == 0 {
+ // Add new order for new validations
+ orderID, newOrderErr := t.Merchant.AddNewOrder(*cost)
+ if newOrderErr != nil {
+ fmt.Println(newOrderErr)
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+ validation.OrderID = orderID
+ }
+
+ // Check if order paid.
+ // FIXME: Remember that it was activated and paid
+ payto, paytoErr := t.Merchant.IsOrderPaid(validation.OrderID)
+ if paytoErr != nil {
+ fmt.Println(paytoErr)
+ w.WriteHeader(http.StatusInternalServerError)
+ log.Println(paytoErr)
+ return
+ }
+ if len(payto) != 0 {
+ t.Db.Save(&validation)
+ w.WriteHeader(http.StatusPaymentRequired)
+ w.Header().Set("Taler", payto) // FIXME no idea what to
do with this.
+ return
+ }
+ // In this case, this order was paid
+ }
+ if validationExists {
+ err = t.Db.Save(&validation).Error
+ } else {
+ err = t.Db.Create(&validation).Error
+ }
+ if err != nil {
+ log.Println(err)
+ w.WriteHeader(500)
+ return
+ }
+ // Some validation methods are costly
+ // Require explicit whitelisting for a resend.
+ if validation.ChallengeSent &&
+
!t.Cfg.Section("taldir-"+vars["method"]).Key("allow_resend").MustBool(false) {
+ w.WriteHeader(202)
+ return
+ }
+ if !t.Cfg.Section("taldir-" + vars["method"]).HasKey("command") {
+ log.Fatal(err)
+ t.Db.Delete(&validation)
+ w.WriteHeader(500)
+ return
+ }
+ command := t.Cfg.Section("taldir-" +
vars["method"]).Key("command").String()
+ path, err := exec.LookPath(command)
+ if err != nil {
+ log.Println(err)
+ t.Db.Delete(&validation)
+ w.WriteHeader(500)
+ return
+ }
+ out, err := exec.Command(path, req.Address,
validation.Challenge).Output()
+ if err != nil {
+ log.Printf("%s, %w", out, err)
+ t.Db.Delete(&validation)
+ w.WriteHeader(500)
+ return
+ }
+ validation.ChallengeSent = true
+ w.WriteHeader(202)
}
func (t *Taldir) configResponse(w http.ResponseWriter, r *http.Request) {
- meths := []Method{}
- i := 0
- for key := range t.Validators {
- var meth Method
- meth.Name = key
- 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:
t.Cfg.Section("taldir").Key("monthly_fee").MustString("KUDOS:1"),
- Methods: meths,
- }
- w.Header().Set("Content-Type", "application/json")
- response, _ := json.Marshal(cfg)
- w.Write(response)
+ meths := []Method{}
+ i := 0
+ for key := range t.Validators {
+ var meth Method
+ meth.Name = key
+ 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:
t.Cfg.Section("taldir").Key("monthly_fee").MustString("KUDOS:1"),
+ Methods: meths,
+ }
+ w.Header().Set("Content-Type", "application/json")
+ response, _ := json.Marshal(cfg)
+ w.Write(response)
}
func (t *Taldir) validationPage(w http.ResponseWriter, r *http.Request) {
- vars := mux.Vars(r)
- var walletLink string
- var png []byte
- var validation validation
-
- err := t.Db.First(&validation, "h_address = ?", vars["h_address"]).Error
- w.Header().Set("Content-Type", "text/html; charset=utf-8")
- if err != nil {
- // This validation does not exist.
- w.WriteHeader(404)
- return
- }
-
- walletLink = "taler://taldir/" + vars["h_address"] + "/" + vars["challenge"]
+ "-wallet"
- 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),
- }
- t.ValidationTpl.Execute(w, fullData)
- return
+ vars := mux.Vars(r)
+ var walletLink string
+ var png []byte
+ var validation validation
+
+ err := t.Db.First(&validation, "h_address = ?", vars["h_address"]).Error
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+ if err != nil {
+ // This validation does not exist.
+ w.WriteHeader(404)
+ return
+ }
+
+ walletLink = "taler://taldir/" + vars["h_address"] + "/" +
vars["challenge"] + "-wallet"
+ 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),
+ }
+ t.ValidationTpl.Execute(w, fullData)
+ return
}
// ClearDatabase nukes the database (for tests)
func (t *Taldir) ClearDatabase() {
- t.Db.Where("1 = 1").Delete(&entry{})
- t.Db.Where("1 = 1").Delete(&validation{})
+ t.Db.Where("1 = 1").Delete(&entry{})
+ t.Db.Where("1 = 1").Delete(&validation{})
}
func (t *Taldir) termsResponse(w http.ResponseWriter, r *http.Request) {
- fileType :=
t.Cfg.Section("taldir").Key("default_doc_filetype").MustString("text/html")
- termsLocation :=
t.Cfg.Section("taldir").Key("default_tos_path").MustString("terms/")
- for _, typ := range r.Header["Accept"] {
- for _, a := range
strings.Split(t.Cfg.Section("taldir").Key("supported_doc_filetypes").String(),
" ") {
- if typ == a {
- fileType = a
- }
- }
- }
-
- if len(r.Header.Get("Accept-Language")) != 0 {
- acceptLangs, _, _ :=
language.ParseAcceptLanguage(r.Header.Get("Accept-Language"))
- for _, lang := range acceptLangs {
- extensions, _ := mime.ExtensionsByType(fileType)
- for _, ext := range extensions {
- docFile := fmt.Sprintf("%s/%s/0%s", termsLocation, lang.String(), ext)
- log.Printf("Trying %s\n", docFile)
- fileBytes, err := ioutil.ReadFile(docFile)
- if nil == err {
- w.Header().Set("Content-Type", fileType)
- w.Write(fileBytes)
- return
- }
- }
- }
- }
- // Default document in expected/default format
- defaultLanguage :=
t.Cfg.Section("taldir").Key("default_doc_lang").MustString("en")
- extensions, _ := mime.ExtensionsByType(fileType)
- for _, ext := range extensions {
- docFile := fmt.Sprintf("%s/%s/0%s", termsLocation, defaultLanguage, ext)
- log.Println("Trying " + docFile)
- fileBytes, err := ioutil.ReadFile(docFile)
- if nil == err {
- w.Header().Set("Content-Type", fileType)
- w.Write(fileBytes)
- return
- }
- }
- w.WriteHeader(http.StatusNotFound)
+ fileType :=
t.Cfg.Section("taldir").Key("default_doc_filetype").MustString("text/html")
+ termsLocation :=
t.Cfg.Section("taldir").Key("default_tos_path").MustString("terms/")
+ for _, typ := range r.Header["Accept"] {
+ for _, a := range
strings.Split(t.Cfg.Section("taldir").Key("supported_doc_filetypes").String(),
" ") {
+ if typ == a {
+ fileType = a
+ }
+ }
+ }
+
+ if len(r.Header.Get("Accept-Language")) != 0 {
+ acceptLangs, _, _ :=
language.ParseAcceptLanguage(r.Header.Get("Accept-Language"))
+ for _, lang := range acceptLangs {
+ extensions, _ := mime.ExtensionsByType(fileType)
+ for _, ext := range extensions {
+ docFile := fmt.Sprintf("%s/%s/0%s",
termsLocation, lang.String(), ext)
+ log.Printf("Trying %s\n", docFile)
+ fileBytes, err := ioutil.ReadFile(docFile)
+ if nil == err {
+ w.Header().Set("Content-Type", fileType)
+ w.Write(fileBytes)
+ return
+ }
+ }
+ }
+ }
+ // Default document in expected/default format
+ defaultLanguage :=
t.Cfg.Section("taldir").Key("default_doc_lang").MustString("en")
+ extensions, _ := mime.ExtensionsByType(fileType)
+ for _, ext := range extensions {
+ docFile := fmt.Sprintf("%s/%s/0%s", termsLocation,
defaultLanguage, ext)
+ log.Println("Trying " + docFile)
+ fileBytes, err := ioutil.ReadFile(docFile)
+ if nil == err {
+ w.Header().Set("Content-Type", fileType)
+ w.Write(fileBytes)
+ return
+ }
+ }
+ w.WriteHeader(http.StatusNotFound)
}
func (t *Taldir) privacyResponse(w http.ResponseWriter, r *http.Request) {
- fileType :=
t.Cfg.Section("taldir").Key("default_doc_filetype").MustString("text/html")
- termsLocation :=
t.Cfg.Section("taldir").Key("default_pp_path").MustString("privacy/")
- for _, typ := range r.Header["Accept"] {
- for _, a := range
strings.Split(t.Cfg.Section("taldir").Key("supported_doc_filetypes").String(),
" ") {
- if typ == a {
- fileType = a
- }
- }
- }
-
- if len(r.Header.Get("Accept-Language")) != 0 {
- acceptLangs, _, _ :=
language.ParseAcceptLanguage(r.Header.Get("Accept-Language"))
- for _, lang := range acceptLangs {
- extensions, _ := mime.ExtensionsByType(fileType)
- for _, ext := range extensions {
- docFile := fmt.Sprintf("%s/%s/0%s", termsLocation, lang.String(), ext)
- log.Printf("Trying %s\n", docFile)
- fileBytes, err := ioutil.ReadFile(docFile)
- if nil == err {
- w.Header().Set("Content-Type", fileType)
- w.Write(fileBytes)
- return
- }
- }
- }
- }
- // Default document in expected/default format
- defaultLanguage :=
t.Cfg.Section("taldir").Key("default_doc_lang").MustString("en")
- extensions, _ := mime.ExtensionsByType(fileType)
- for _, ext := range extensions {
- docFile := fmt.Sprintf("%s/%s/0%s", termsLocation, defaultLanguage, ext)
- fileBytes, err := ioutil.ReadFile(docFile)
- if nil == err {
- w.Header().Set("Content-Type", fileType)
- w.Write(fileBytes)
- return
- }
- }
- w.WriteHeader(http.StatusNotFound)
+ fileType :=
t.Cfg.Section("taldir").Key("default_doc_filetype").MustString("text/html")
+ termsLocation :=
t.Cfg.Section("taldir").Key("default_pp_path").MustString("privacy/")
+ for _, typ := range r.Header["Accept"] {
+ for _, a := range
strings.Split(t.Cfg.Section("taldir").Key("supported_doc_filetypes").String(),
" ") {
+ if typ == a {
+ fileType = a
+ }
+ }
+ }
+
+ if len(r.Header.Get("Accept-Language")) != 0 {
+ acceptLangs, _, _ :=
language.ParseAcceptLanguage(r.Header.Get("Accept-Language"))
+ for _, lang := range acceptLangs {
+ extensions, _ := mime.ExtensionsByType(fileType)
+ for _, ext := range extensions {
+ docFile := fmt.Sprintf("%s/%s/0%s",
termsLocation, lang.String(), ext)
+ log.Printf("Trying %s\n", docFile)
+ fileBytes, err := ioutil.ReadFile(docFile)
+ if nil == err {
+ w.Header().Set("Content-Type", fileType)
+ w.Write(fileBytes)
+ return
+ }
+ }
+ }
+ }
+ // Default document in expected/default format
+ defaultLanguage :=
t.Cfg.Section("taldir").Key("default_doc_lang").MustString("en")
+ extensions, _ := mime.ExtensionsByType(fileType)
+ for _, ext := range extensions {
+ docFile := fmt.Sprintf("%s/%s/0%s", termsLocation,
defaultLanguage, ext)
+ fileBytes, err := ioutil.ReadFile(docFile)
+ if nil == err {
+ w.Header().Set("Content-Type", fileType)
+ w.Write(fileBytes)
+ return
+ }
+ }
+ w.WriteHeader(http.StatusNotFound)
}
-
func (t *Taldir) setupHandlers() {
- t.Router = mux.NewRouter().StrictSlash(true)
+ t.Router = mux.NewRouter().StrictSlash(true)
- /* ToS API */
- t.Router.HandleFunc("/terms", t.termsResponse).Methods("GET")
- t.Router.HandleFunc("/privacy", t.privacyResponse).Methods("GET")
+ /* ToS API */
+ t.Router.HandleFunc("/terms", t.termsResponse).Methods("GET")
+ t.Router.HandleFunc("/privacy", t.privacyResponse).Methods("GET")
- /* Config API */
- t.Router.HandleFunc("/config", t.configResponse).Methods("GET")
+ /* Config API */
+ t.Router.HandleFunc("/config", t.configResponse).Methods("GET")
- /* Aissets HTML */
- t.Router.PathPrefix("/css").Handler(http.StripPrefix("/css",
http.FileServer(http.Dir("./static/css"))))
+ /* Aissets HTML */
+ t.Router.PathPrefix("/css").Handler(http.StripPrefix("/css",
http.FileServer(http.Dir("./static/css"))))
- /* Registration API */
- t.Router.HandleFunc("/{h_address}", t.getSingleEntry).Methods("GET")
- t.Router.HandleFunc("/register/{method}", t.registerRequest).Methods("POST")
- t.Router.HandleFunc("/register/{h_address}/{challenge}",
t.validationPage).Methods("GET")
- t.Router.HandleFunc("/{h_address}", t.validationRequest).Methods("POST")
+ /* Registration API */
+ t.Router.HandleFunc("/{h_address}", t.getSingleEntry).Methods("GET")
+ t.Router.HandleFunc("/register/{method}",
t.registerRequest).Methods("POST")
+ t.Router.HandleFunc("/register/{h_address}/{challenge}",
t.validationPage).Methods("GET")
+ t.Router.HandleFunc("/{h_address}", t.validationRequest).Methods("POST")
}
// Initialize the Taldir instance with cfgfile
func (t *Taldir) Initialize(cfgfile string) {
- _cfg, err := ini.Load(cfgfile)
- if err != nil {
- fmt.Printf("Failed to read config: %v", err)
- os.Exit(1)
- }
- t.Cfg = _cfg
- if t.Cfg.Section("taldir").Key("production").MustBool(false) {
- fmt.Println("Production mode enabled")
- }
-
- t.Validators = make(map[string]bool)
- for _, a := range
strings.Split(t.Cfg.Section("taldir").Key("validators").String(), " ") {
- t.Validators[a] = true
- }
- 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)
-
- validationTTLStr :=
t.Cfg.Section("taldir").Key("validation_timeframe").MustString("5m")
- t.ValidationTimeframe, err = time.ParseDuration(validationTTLStr)
- if err != nil {
- log.Fatal(err)
- }
-
- retryTimeframeStr :=
t.Cfg.Section("taldir").Key("solution_attempt_timeframe").MustString("1h")
- t.SolutionTimeframe, err = time.ParseDuration(retryTimeframeStr)
- if err != nil {
- log.Fatal(err)
- }
- 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{
- Logger: logger.Default.LogMode(logger.Silent),
- })
- if err != nil {
- panic(err)
- }
- t.Db = _db
- if err := t.Db.AutoMigrate(&entry{}); err != nil {
- panic(err)
- }
- if err := t.Db.AutoMigrate(&validation{}); err != nil {
- panic(err)
- }
-
- // Clean up validations
- validationExpStr :=
t.Cfg.Section("taldir").Key("validation_expiration").MustString("24h")
- validationExp, err := time.ParseDuration(validationExpStr)
- if err != nil {
- log.Fatal(err)
- }
- go func() {
- for true {
- 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)
- }
- }()
- 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)
- }
- t.Salt = os.Getenv("TALDIR_SALT")
- if "" == t.Salt {
- t.Salt = t.Cfg.Section("taldir").Key("salt").MustString("ChangeMe")
- }
- merchURL :=
t.Cfg.Section("taldir").Key("merchant_baseurl_private").MustString("http://merchant.taldir/instances/myInstance")
- merchToken :=
t.Cfg.Section("taldir").Key("merchant_token").MustString("secretAccessToken")
- t.Merchant = taler.NewMerchant(merchURL, merchToken)
- t.setupHandlers()
+ _cfg, err := ini.Load(cfgfile)
+ if err != nil {
+ fmt.Printf("Failed to read config: %v", err)
+ os.Exit(1)
+ }
+ t.Cfg = _cfg
+ if t.Cfg.Section("taldir").Key("production").MustBool(false) {
+ fmt.Println("Production mode enabled")
+ }
+
+ t.Validators = make(map[string]bool)
+ for _, a := range
strings.Split(t.Cfg.Section("taldir").Key("validators").String(), " ") {
+ t.Validators[a] = true
+ }
+ 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)
+
+ validationTTLStr :=
t.Cfg.Section("taldir").Key("validation_timeframe").MustString("5m")
+ t.ValidationTimeframe, err = time.ParseDuration(validationTTLStr)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ retryTimeframeStr :=
t.Cfg.Section("taldir").Key("solution_attempt_timeframe").MustString("1h")
+ t.SolutionTimeframe, err = time.ParseDuration(retryTimeframeStr)
+ if err != nil {
+ log.Fatal(err)
+ }
+ 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{
+ Logger: logger.Default.LogMode(logger.Silent),
+ })
+ if err != nil {
+ panic(err)
+ }
+ t.Db = _db
+ if err := t.Db.AutoMigrate(&entry{}); err != nil {
+ panic(err)
+ }
+ if err := t.Db.AutoMigrate(&validation{}); err != nil {
+ panic(err)
+ }
+
+ // Clean up validations
+ validationExpStr :=
t.Cfg.Section("taldir").Key("validation_expiration").MustString("24h")
+ validationExp, err := time.ParseDuration(validationExpStr)
+ if err != nil {
+ log.Fatal(err)
+ }
+ go func() {
+ for true {
+ 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)
+ }
+ }()
+ 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)
+ }
+ t.Salt = os.Getenv("TALDIR_SALT")
+ if "" == t.Salt {
+ t.Salt =
t.Cfg.Section("taldir").Key("salt").MustString("ChangeMe")
+ }
+ merchURL :=
t.Cfg.Section("taldir").Key("merchant_baseurl_private").MustString("http://merchant.taldir/instances/myInstance")
+ merchToken :=
t.Cfg.Section("taldir").Key("merchant_token").MustString("secretAccessToken")
+ t.Merchant = taler.NewMerchant(merchURL, merchToken)
+ t.setupHandlers()
}
diff --git a/pkg/taler/merchant.go b/pkg/taler/merchant.go
index 1606b3d..862f00c 100644
--- a/pkg/taler/merchant.go
+++ b/pkg/taler/merchant.go
@@ -1,161 +1,156 @@
package taler
import (
- "net/http"
- "encoding/json"
- "bytes"
- "fmt"
- "errors"
- "io/ioutil"
- talerutil "taler.net/taler-go.git/pkg/util"
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ talerutil "taler.net/taler-go.git/pkg/util"
)
type PostOrderRequest struct {
- // The order must at least contain the minimal
- // order detail, but can override all.
- order MinimalOrderDetail
-
- // If set, the backend will then set the refund deadline to the current
- // time plus the specified delay. If it's not set, refunds will not be
- // possible.
- RefundDelay int64 `json:"refund_delay,omitempty"`
-
- // Specifies the payment target preferred by the client. Can be used
- // to select among the various (active) wire methods supported by the
instance.
- PaymentTarget string `json:"payment_target,omitempty"`
-
- // Specifies that some products are to be included in the
- // order from the inventory. For these inventory management
- // is performed (so the products must be in stock) and
- // details are completed from the product data of the backend.
- // FIXME: Not sure we actually need this for now
- //InventoryProducts []MinimalInventoryProduct
`json:"inventory_products,omitempty"`
-
- // Specifies a lock identifier that was used to
- // lock a product in the inventory. Only useful if
- // inventory_products is set. Used in case a frontend
- // reserved quantities of the individual products while
- // the shopping cart was being built. Multiple UUIDs can
- // be used in case different UUIDs were used for different
- // products (i.e. in case the user started with multiple
- // shopping sessions that were combined during checkout).
- LockUuids []string `json:"lock_uuids"`
-
- // Should a token for claiming the order be generated?
- // False can make sense if the ORDER_ID is sufficiently
- // high entropy to prevent adversarial claims (like it is
- // if the backend auto-generates one). Default is 'true'.
- CreateToken bool `json:"create_token,omitempty"`
-
+ // The order must at least contain the minimal
+ // order detail, but can override all.
+ order MinimalOrderDetail
+
+ // If set, the backend will then set the refund deadline to the current
+ // time plus the specified delay. If it's not set, refunds will not be
+ // possible.
+ RefundDelay int64 `json:"refund_delay,omitempty"`
+
+ // Specifies the payment target preferred by the client. Can be used
+ // to select among the various (active) wire methods supported by the
instance.
+ PaymentTarget string `json:"payment_target,omitempty"`
+
+ // Specifies that some products are to be included in the
+ // order from the inventory. For these inventory management
+ // is performed (so the products must be in stock) and
+ // details are completed from the product data of the backend.
+ // FIXME: Not sure we actually need this for now
+ //InventoryProducts []MinimalInventoryProduct
`json:"inventory_products,omitempty"`
+
+ // Specifies a lock identifier that was used to
+ // lock a product in the inventory. Only useful if
+ // inventory_products is set. Used in case a frontend
+ // reserved quantities of the individual products while
+ // the shopping cart was being built. Multiple UUIDs can
+ // be used in case different UUIDs were used for different
+ // products (i.e. in case the user started with multiple
+ // shopping sessions that were combined during checkout).
+ LockUuids []string `json:"lock_uuids"`
+
+ // Should a token for claiming the order be generated?
+ // False can make sense if the ORDER_ID is sufficiently
+ // high entropy to prevent adversarial claims (like it is
+ // if the backend auto-generates one). Default is 'true'.
+ CreateToken bool `json:"create_token,omitempty"`
}
type MinimalOrderDetail struct {
- // Amount to be paid by the customer.
- Amount string
+ // Amount to be paid by the customer.
+ Amount string
- // Short summary of the order.
- Summary string;
+ // Short summary of the order.
+ Summary string
}
-
// NOTE: Part of the above but optional
type FulfillmentMetadata struct {
- // See documentation of fulfillment_url in ContractTerms.
- // Either fulfillment_url or fulfillment_message must be specified.
- FulfillmentUrl string `json:"fulfillment_url,omitempty"`
+ // See documentation of fulfillment_url in ContractTerms.
+ // Either fulfillment_url or fulfillment_message must be specified.
+ FulfillmentUrl string `json:"fulfillment_url,omitempty"`
- // See documentation of fulfillment_message in ContractTerms.
- // Either fulfillment_url or fulfillment_message must be specified.
- FulfillmentMessage string `json:"fulfillment_message,omitempty"`
+ // See documentation of fulfillment_message in ContractTerms.
+ // Either fulfillment_url or fulfillment_message must be specified.
+ FulfillmentMessage string `json:"fulfillment_message,omitempty"`
}
type PostOrderResponse struct {
- // Order ID of the response that was just created.
- OrderId string `json:"order_id"`
+ // Order ID of the response that was just created.
+ OrderId string `json:"order_id"`
}
type PostOrderResponseToken struct {
- // Token that authorizes the wallet to claim the order.
- // Provided only if "create_token" was set to 'true'
- // in the request.
- Token string
+ // Token that authorizes the wallet to claim the order.
+ // Provided only if "create_token" was set to 'true'
+ // in the request.
+ Token string
}
type CheckPaymentStatusResponse struct {
- // Status of the order
- OrderStatus string `json:"order_status"`
+ // Status of the order
+ OrderStatus string `json:"order_status"`
}
type CheckPaymentPaytoResponse struct {
- // Status of the order
- TalerPayUri string `json:"taler_pay_uri"`
+ // Status of the order
+ TalerPayUri string `json:"taler_pay_uri"`
}
-
-
type Merchant struct {
- // The host of this merchant
- BaseUrlPrivate string
-
- // The access token to use for the private API
- AccessToken string
+ // The host of this merchant
+ BaseUrlPrivate string
+ // The access token to use for the private API
+ AccessToken string
}
func NewMerchant(merchBaseUrlPrivate string, merchAccessToken string) Merchant
{
- return Merchant{
- BaseUrlPrivate: merchBaseUrlPrivate,
- AccessToken: merchAccessToken,
- }
+ return Merchant{
+ BaseUrlPrivate: merchBaseUrlPrivate,
+ AccessToken: merchAccessToken,
+ }
}
func (m *Merchant) IsOrderPaid(orderId string) (string, error) {
- var orderPaidResponse CheckPaymentStatusResponse
- var paytoResponse CheckPaymentPaytoResponse
- resp, err := http.Get(m.BaseUrlPrivate + "/private/orders/" + orderId)
- if nil != err {
- return "", err
- }
- defer resp.Body.Close()
- if http.StatusOK != resp.StatusCode {
- message := fmt.Sprintf("Expected response code %d. Got %d", http.StatusOK,
resp.StatusCode)
- return "", errors.New(message)
- }
- respData, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return "", err
- }
- err = json.NewDecoder(bytes.NewReader(respData)).Decode(&orderPaidResponse)
- if err != nil {
- return "", err
- }
- if orderPaidResponse.OrderStatus != "paid" {
- err = json.NewDecoder(bytes.NewReader(respData)).Decode(&paytoResponse)
- return paytoResponse.TalerPayUri, err
- }
- return "", nil
+ var orderPaidResponse CheckPaymentStatusResponse
+ var paytoResponse CheckPaymentPaytoResponse
+ resp, err := http.Get(m.BaseUrlPrivate + "/private/orders/" + orderId)
+ if nil != err {
+ return "", err
+ }
+ defer resp.Body.Close()
+ if http.StatusOK != resp.StatusCode {
+ message := fmt.Sprintf("Expected response code %d. Got %d",
http.StatusOK, resp.StatusCode)
+ return "", errors.New(message)
+ }
+ respData, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return "", err
+ }
+ err =
json.NewDecoder(bytes.NewReader(respData)).Decode(&orderPaidResponse)
+ if err != nil {
+ return "", err
+ }
+ if orderPaidResponse.OrderStatus != "paid" {
+ err =
json.NewDecoder(bytes.NewReader(respData)).Decode(&paytoResponse)
+ return paytoResponse.TalerPayUri, err
+ }
+ return "", nil
}
func (m *Merchant) AddNewOrder(cost talerutil.Amount) (string, error) {
- var newOrder PostOrderRequest
- var orderDetail MinimalOrderDetail
- var orderResponse PostOrderResponse
- orderDetail.Amount = cost.String()
- // FIXME get from cfg
- orderDetail.Summary = "This is an order to a TalDir registration"
- newOrder.order = orderDetail
- reqString, _ := json.Marshal(newOrder)
- resp, err := http.Post(m.BaseUrlPrivate + "/private/orders",
"application/json", bytes.NewBuffer(reqString))
-
- if nil != err {
- return "", err
- }
- defer resp.Body.Close()
- if http.StatusOK != resp.StatusCode {
- message := fmt.Sprintf("Expected response code %d. Got %d", http.StatusOK,
resp.StatusCode)
- return "", errors.New(message)
- }
- err = json.NewDecoder(resp.Body).Decode(&orderResponse)
- return orderResponse.OrderId, err
+ var newOrder PostOrderRequest
+ var orderDetail MinimalOrderDetail
+ var orderResponse PostOrderResponse
+ orderDetail.Amount = cost.String()
+ // FIXME get from cfg
+ orderDetail.Summary = "This is an order to a TalDir registration"
+ newOrder.order = orderDetail
+ reqString, _ := json.Marshal(newOrder)
+ resp, err := http.Post(m.BaseUrlPrivate+"/private/orders",
"application/json", bytes.NewBuffer(reqString))
+
+ if nil != err {
+ return "", err
+ }
+ defer resp.Body.Close()
+ if http.StatusOK != resp.StatusCode {
+ message := fmt.Sprintf("Expected response code %d. Got %d",
http.StatusOK, resp.StatusCode)
+ return "", errors.New(message)
+ }
+ err = json.NewDecoder(resp.Body).Decode(&orderResponse)
+ return orderResponse.OrderId, err
}
diff --git a/pkg/util/helper.go b/pkg/util/helper.go
index 9243c5e..b99d6af 100644
--- a/pkg/util/helper.go
+++ b/pkg/util/helper.go
@@ -16,67 +16,65 @@
//
// SPDX-License-Identifier: AGPL3.0-or-later
-
package util
import (
- "fmt"
- "crypto/sha512"
- "math/rand"
- "time"
- gnunetutil "git.gnunet.org/gnunet-go.git/pkg/util"
- talerutil "taler.net/taler-go.git/pkg/util"
+ "crypto/sha512"
+ "fmt"
+ gnunetutil "git.gnunet.org/gnunet-go.git/pkg/util"
+ "math/rand"
+ talerutil "taler.net/taler-go.git/pkg/util"
+ "time"
)
-
// Generates a solution from a challenge and pubkey
func GenerateSolution(pubkeyEncoded string, challenge string) string {
- pubkey, err := gnunetutil.DecodeStringToBinary(pubkeyEncoded, 36)
- if err != nil {
- fmt.Println("error decoding pubkey:", err)
- return ""
- }
- h := sha512.New()
- h.Write([]byte(challenge))
- h.Write(pubkey)
- return gnunetutil.EncodeBinaryToString(h.Sum(nil))
+ pubkey, err := gnunetutil.DecodeStringToBinary(pubkeyEncoded, 36)
+ if err != nil {
+ fmt.Println("error decoding pubkey:", err)
+ return ""
+ }
+ h := sha512.New()
+ h.Write([]byte(challenge))
+ h.Write(pubkey)
+ return gnunetutil.EncodeBinaryToString(h.Sum(nil))
}
// Generates random reference token used in the validation flow.
func GenerateChallenge(bytes int) string {
- randBytes := make([]byte, bytes)
- _, err := rand.Read(randBytes)
- if err != nil {
- panic(err)
- }
- return gnunetutil.EncodeBinaryToString(randBytes)
+ randBytes := make([]byte, bytes)
+ _, err := rand.Read(randBytes)
+ if err != nil {
+ panic(err)
+ }
+ return gnunetutil.EncodeBinaryToString(randBytes)
}
// Check if this is a non-zero, positive amount
func CalculateCost(sliceCostAmount string, fixedCostAmount string, howLong
time.Duration, sliceDuration time.Duration) (*talerutil.Amount, error) {
- sliceCount := int(float64(howLong.Microseconds()) /
float64(sliceDuration.Microseconds()))
- sliceCost, err := talerutil.ParseAmount(sliceCostAmount)
- if nil != err {
- return nil, err
- }
- fixedCost, err := talerutil.ParseAmount(fixedCostAmount)
- if nil != err {
- return nil, err
- }
- sum := &talerutil.Amount{
- Currency: sliceCost.Currency,
- Value: 0,
- Fraction: 0,
- }
- for i := 0; i < sliceCount; i++ {
- sum, err = sum.Add(*sliceCost)
- if nil != err {
- return nil, err
- }
- }
- sum, err = sum.Add(*fixedCost)
- if nil != err {
- return nil, err
- }
- return sum, nil
+ sliceCount := int(float64(howLong.Microseconds()) /
float64(sliceDuration.Microseconds()))
+ sliceCost, err := talerutil.ParseAmount(sliceCostAmount)
+ if nil != err {
+ return nil, err
+ }
+ fixedCost, err := talerutil.ParseAmount(fixedCostAmount)
+ if nil != err {
+ return nil, err
+ }
+ sum := &talerutil.Amount{
+ Currency: sliceCost.Currency,
+ Value: 0,
+ Fraction: 0,
+ }
+ for i := 0; i < sliceCount; i++ {
+ sum, err = sum.Add(*sliceCost)
+ if nil != err {
+ return nil, err
+ }
+ }
+ sum, err = sum.Add(*fixedCost)
+ if nil != err {
+ return nil, err
+ }
+ return sum, nil
}
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.