[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-taldir] 02/03: add additional endpoints
From: |
gnunet |
Subject: |
[taler-taldir] 02/03: add additional endpoints |
Date: |
Tue, 05 Jul 2022 12:40:45 +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 93c8da8d359b3ce06fa51d259d3339ccde6e60ee
Author: Martin Schanzenbach <schanzen@gnunet.org>
AuthorDate: Tue Jul 5 11:50:01 2022 +0200
add additional endpoints
---
taldir.go | 102 +++++++++++++++++++++++++++++++++++++++-----------------------
1 file changed, 65 insertions(+), 37 deletions(-)
diff --git a/taldir.go b/taldir.go
index 387cf33..2b05657 100644
--- a/taldir.go
+++ b/taldir.go
@@ -3,6 +3,7 @@ package main
import (
"os"
"os/exec"
+ "time"
"fmt"
"log"
"net/http"
@@ -12,7 +13,7 @@ import (
"encoding/base32"
"math/rand"
"net/smtp"
- "crypto/sha256"
+ "crypto/sha512"
"gorm.io/driver/postgres"
"gopkg.in/ini.v1"
"strings"
@@ -50,7 +51,7 @@ type RateLimitedResponse struct {
Code int `json:"code"`
// At what frequency are new registrations allowed. FIXME: In what?
- Request_frequency uint `json:"request_frequency"`
+ RequestFrequency int64 `json:"request_frequency"`
// The human readable error message.
Hint string `json:"hint"`
@@ -68,7 +69,7 @@ type RegisterMessage struct {
Inbox string `json:"inbox_url"`
// For how long should the registration last
- Duration uint64 `json:"duration"`
+ 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
@@ -81,8 +82,10 @@ type RegisterMessage struct {
// one of the identity key types supported (e.g. email)
type Entry struct {
gorm.Model
- IdentityKeyHash string `json:"identity_key_hash"`
- TalerWalletKey string `json:"taler_wallet_key"`
+ HAddress string `json:"h_address"`
+ PublicKey string `json:"public_key"`
+ RegisteredAt int64 `json:"valid_until"`
+ Duration int64 `json:"duration"`
}
// A validation is created when a registration for an entry is initiated.
@@ -95,7 +98,6 @@ type Validation struct {
Method string `json:"method"`
ValidationReference string `json:"reference"`
PublicKey string `json:"public_key"`
- RetryCount uint
}
type ErrorDetail struct {
@@ -171,18 +173,18 @@ func sendEmail(recipient string, ref Validation) {
// Primary lookup function.
// Allows the caller to query a wallet key using the hash(!) of the
// identity, e.g. sha256(<email address>)
-/*func returnSingleEntry(w http.ResponseWriter, r *http.Request){
+func getSingleEntry(w http.ResponseWriter, r *http.Request){
vars := mux.Vars(r)
var entry Entry
- identityKeyHash := hashIdentityKey(vars["identity_key"])
- var err = db.First(&entry, "identity_key_hash = ?", identityKeyHash).Error
+ //identityKeyHash := hashIdentityKey(vars["identity_key"])
+ var err = db.First(&entry, "h_address = ?", vars["h_address"]).Error
if err == nil {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(entry)
return
}
w.WriteHeader(http.StatusNotFound)
-}*/
+}
// Hashes an identity key (e.g. sha256(<email address>)) with a salt for
// Lookup and storage.
@@ -192,7 +194,7 @@ func hashIdentityKey(idkey string) string {
salt = cfg.Section("taldir").Key("salt").MustString("ChangeMe")
}
fmt.Println("Using salt " + salt)
- h := sha256.New()
+ h := sha512.New()
h.Write([]byte(idkey))
h.Write([]byte(salt))
return base32.StdEncoding.EncodeToString(h.Sum(nil))
@@ -200,26 +202,33 @@ func hashIdentityKey(idkey 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 validateSingleEntry(w http.ResponseWriter, r *http.Request){
+func validationRequest(w http.ResponseWriter, r *http.Request){
vars := mux.Vars(r)
var entry Entry
var validation Validation
- var err = db.First(&validation, "validation_reference = ?",
vars["reference"]).Error
+ err := db.First(&validation, "h_address = ?", vars["h_address"]).Error
if err != nil {
w.WriteHeader(http.StatusNotFound)
+ return
}
- err = db.First(&validation, "identity_key = ?", validation.IdentityKey).Error
- if err != nil {
- w.WriteHeader(http.StatusNotFound)
+ if vars["validation_code"] != validation.ValidationReference {
+ // 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.IdentityKeyHash = hashIdentityKey(validation.IdentityKey)
- entry.TalerWalletKey = validation.TalerWalletKey
- err = db.First(&entry, "identity_key_hash = ?", entry.IdentityKeyHash).Error
+ // FIXME are we still doing this??
+ //entry.IdentityKeyHash = hashIdentityKey(validation.IdentityKey)
+ entry.HAddress = validation.HAddress
+ entry.PublicKey = validation.PublicKey
+ err = db.First(&entry, "h_address = ?", entry.HAddress).Error
if err == nil {
+ // FIXME is this not in theory possible that we find such an entry??
w.WriteHeader(http.StatusConflict)
return
}
@@ -228,8 +237,8 @@ func hashIdentityKey(idkey string) string {
w.WriteHeader(http.StatusInternalServerError)
return
}
- w.WriteHeader(http.StatusCreated)
-}*/
+ w.WriteHeader(http.StatusNoContent)
+}
// Generates random reference token used in the validation flow.
@@ -247,6 +256,7 @@ func registerRequest(w http.ResponseWriter, r
*http.Request){
var req RegisterMessage
var errDetail ErrorDetail
var validation Validation
+ var entry Entry
if r.Body == nil {
http.Error(w, "No request body", 400)
return
@@ -269,29 +279,42 @@ func registerRequest(w http.ResponseWriter, r
*http.Request){
w.Write(resp)
return
}
- // TODO make sure sha256(vars["identity"]) == validation.IdentityKey or
simply set it?
- h := sha256.New()
+ h := sha512.New()
h.Write([]byte(req.Address))
validation.HAddress = base32.StdEncoding.EncodeToString(h.Sum(nil))
- err = db.First(&validation, "h_address = ?", validation.HAddress).Error
- if err == nil {
- reqFrequency := cfg.Section("taldir").Key("request_frequency").MustUint(1)
- if validation.RetryCount >= reqFrequency - 1 {
+ // We first try if there is already an entry for this address which
+ // is still valid and the duration is not extended.
+ err = db.First(&entry, "h_address = ?", validation.HAddress).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: 23, //FIXME TALER_EC_TALDIR_REGISTER_RATE_LIMITED
- Request_frequency: reqFrequency,
- Hint: "Retry maximum reached. Deleting validation",
+ RequestFrequency: reqFrequency,
+ Hint: "Registration rate limit reached",
}
jsonResp, _ := json.Marshal(rlResponse)
w.Write(jsonResp)
- db.Delete(&validation)
return
}
- validation.RetryCount++
- validation.ValidationReference = generateToken()
- db.Save(&validation)
- fmt.Printf("Retrying validation send\n")
+ // 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
+ if err == nil {
+ // Validation already pending for this address
+ db.Delete(&validation) // FIXME for debugging only
+ w.WriteHeader(202)
+ return
} else {
validation.ValidationReference = generateToken()
err = db.Create(&validation).Error
@@ -302,21 +325,24 @@ func registerRequest(w http.ResponseWriter, r
*http.Request){
}
fmt.Println("Address registration request created:", validation)
}
- w.WriteHeader(202)
// FIXME: Here we should call the validator shell script with the
// parsed parameters to initiate the validation.
if !cfg.Section("taldir-" + vars["method"]).HasKey("command") {
log.Fatal(err)
- // FIXME cleanup validation?
+ db.Delete(&validation)
+ w.WriteHeader(500)
return
}
command := cfg.Section("taldir-" + vars["method"]).Key("command").String()
out, err := exec.Command(command, req.Address,
validation.ValidationReference).Output()
if err != nil {
log.Fatal(err)
+ db.Delete(&validation)
+ w.WriteHeader(500)
+ return
}
+ w.WriteHeader(202)
fmt.Printf("Output from method script is %s\n", out)
- // sendEmail(vars["identity"], validation)
}
func notImplemented(w http.ResponseWriter, r *http.Request) {
@@ -358,7 +384,9 @@ func handleRequests() {
/* Registration API */
//myRouter.HandleFunc("/directory/{identity_key}",
returnSingleEntry).Methods("GET")
//myRouter.HandleFunc("/validation/{reference}",
validateSingleEntry).Methods("GET")
+ myRouter.HandleFunc("/{h_address}", getSingleEntry).Methods("GET")
myRouter.HandleFunc("/register/{method}", registerRequest).Methods("POST")
+ myRouter.HandleFunc("/register/{h_address}/{validation_code}",
validationRequest).Methods("GET")
log.Fatal(http.ListenAndServe(cfg.Section("taldir").Key("bind_to").MustString("localhost:11000"),
myRouter))
}
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.