gnunet-gns-registrar

GNU Name System registrar
Log | Files | Refs | README

commit ce24fe047bbe7b763fee142f60439b28e8c6563a
parent 0ebeb2c197f8fc45101862b960ce629d7c9be6f3
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Mon, 11 Dec 2023 17:15:27 +0100

towards editing registration; potentially refund

Diffstat:
Rgns-registrar.conf -> gns-registrar.conf.example | 0
Mgo.mod | 1+
Mpkg/rest/gnsregistrar.go | 160++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
3 files changed, 95 insertions(+), 66 deletions(-)

diff --git a/gns-registrar.conf b/gns-registrar.conf.example diff --git a/go.mod b/go.mod @@ -3,6 +3,7 @@ module gnunet.org/gnunet-gns-registrar go 1.18 require ( + github.com/google/uuid v1.4.0 github.com/gorilla/mux v1.8.1 github.com/schanzen/taler-go v0.0.4 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e diff --git a/pkg/rest/gnsregistrar.go b/pkg/rest/gnsregistrar.go @@ -39,6 +39,7 @@ import ( "golang.org/x/text/message" "gopkg.in/ini.v1" "github.com/schanzen/taler-go/pkg/merchant" + "github.com/google/uuid" talerutil "github.com/schanzen/taler-go/pkg/util" ) @@ -47,6 +48,7 @@ type RegistrationMetadata struct { Expiration uint64 `json:"expiration"` Paid bool `json:"paid"` OrderID string `json:"order_id"` + RegistrationID string `json:"registration_id"` NeedsPaymentUntil time.Time `json:"needs_payment_until"` } @@ -99,6 +101,9 @@ type Registrar struct { // buy names page BuyTpl *template.Template + // edit registration page + EditTpl *template.Template + // Merchant object Merchant merchant.Merchant @@ -125,10 +130,10 @@ type Registrar struct { // Gnunet basic auth on/off GnunetBasicAuthEnabled bool - + // Gnunet basic auth GnunetUsername string - + // Gnunet basic auth GnunetPassword string @@ -159,6 +164,10 @@ func (t *Registrar) configResponse(w http.ResponseWriter, r *http.Request) { w.Write(response) } +func generateRegistrationId() string { + return uuid.New().String() +} + // FIXME: Implement https://docs.taler.net/design-documents/051-fractional-digits.html and move to taler-go func (t *Registrar) localizedAmountString() (string) { p := message.NewPrinter(language.English) @@ -187,63 +196,6 @@ func (t *Registrar) landingPage(w http.ResponseWriter, r *http.Request) { return } -func (t *Registrar) registerName(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - var namestoreRequest NamestoreRecord - var delegationRecord RecordData - var metadataRecord RecordData - var gnunetError GnunetError - var registrationMetadata RegistrationMetadata - w.Header().Set("Content-Type", "text/html; charset=utf-8") - delegationRecord.IsPrivate = false - delegationRecord.IsRelativeExpiration = true - delegationRecord.IsSupplemental = false - delegationRecord.IsMaintenance = false - delegationRecord.IsShadow = false - delegationRecord.RecordType = guessDelegationRecordType(r.URL.Query().Get("zkey")) - delegationRecord.RelativeExpiration = uint64(t.RelativeDelegationExpiration.Microseconds()) - delegationRecord.Value = r.URL.Query().Get("zkey") - metadataRecord.IsPrivate = true - metadataRecord.IsRelativeExpiration = true - metadataRecord.IsSupplemental = false - metadataRecord.IsMaintenance = true - metadataRecord.IsShadow = false - metadataRecord.RecordType = "TXT" // FIXME use new recory type - metadataRecord.RelativeExpiration = uint64(t.RelativeDelegationExpiration.Microseconds()) - registrationMetadata = RegistrationMetadata{ - Paid: false, - Expiration: uint64(time.Now().Add(t.RelativeRegistrationExpiration).UnixMicro()), - } - metadataRecordValue, err := json.Marshal(registrationMetadata) - if nil != err { - http.Redirect(w, r, "/name?label="+vars["label"] + "&error=Registration failed", http.StatusSeeOther) - return - } - metadataRecord.Value = string(metadataRecordValue) - namestoreRequest.RecordName = vars["label"] - namestoreRequest.Records = []RecordData{delegationRecord,metadataRecord} - reqString, _ := json.Marshal(namestoreRequest) - // FIXME handle errors here - fmt.Println(namestoreRequest) - client := &http.Client{} - req, _ := http.NewRequest(http.MethodPost, t.GnunetUrl+"/namestore/" + t.RootZoneName, bytes.NewBuffer(reqString)) - if t.GnunetBasicAuthEnabled { - req.SetBasicAuth(t.GnunetUsername, t.GnunetPassword) - } - resp, err := client.Do(req) - resp.Body.Close() - if http.StatusNoContent != resp.StatusCode { - fmt.Printf("Got error: %d\n", resp.StatusCode) - json.NewDecoder(resp.Body).Decode(&gnunetError) - fmt.Println(gnunetError.Code) - fmt.Println(err) - http.Redirect(w, r, "/name?label="+vars["label"] + "&error=" + gnunetError.Description, http.StatusSeeOther) - return - } - http.Redirect(w, r, "/name/"+ vars["label"] + "?registered=true", http.StatusSeeOther) - return -} - func (t *Registrar) searchPage(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") http.Redirect(w, r, "/name/"+r.URL.Query().Get("label"), http.StatusSeeOther) @@ -295,7 +247,7 @@ func (t *Registrar) createOrUpdateRegistration(nsRecord *NamestoreRecord) (error return nil } -func (t *Registrar) setupRegistrationMetadataBeforePayment(label string, zkey string, orderId string, paymentUntil time.Time) (error) { +func (t *Registrar) setupRegistrationMetadataBeforePayment(label string, zkey string, orderId string, paymentUntil time.Time, regId string) (error) { var namestoreRequest NamestoreRecord var delegationRecord RecordData var metadataRecord RecordData @@ -319,6 +271,7 @@ func (t *Registrar) setupRegistrationMetadataBeforePayment(label string, zkey st Paid: false, OrderID: orderId, NeedsPaymentUntil: paymentUntil, + RegistrationID: regId, Expiration: uint64(time.Now().Add(t.RelativeRegistrationExpiration).UnixMicro()), } metadataRecordValue, err := json.Marshal(registrationMetadata) @@ -331,6 +284,74 @@ func (t *Registrar) setupRegistrationMetadataBeforePayment(label string, zkey st return t.createOrUpdateRegistration(&namestoreRequest) } +func (t *Registrar) editRegistration(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + var namestoreResponse NamestoreRecord + var regMetadata *RegistrationMetadata + var value string + w.Header().Set("Content-Type", "text/html; charset=utf-8") + client := &http.Client{} + req, _ := http.NewRequest(http.MethodGet,t.GnunetUrl + "/namestore/" + t.RootZoneName + "/" + vars["label"] + "?include_maintenance=yes", nil) + if t.GnunetBasicAuthEnabled { + req.SetBasicAuth(t.GnunetUsername, t.GnunetPassword) + } + resp, err := client.Do(req) + if err != nil { + fmt.Printf("Failed to get zone contents") + return + } + defer resp.Body.Close() + if http.StatusOK == resp.StatusCode { + respData, err := io.ReadAll(resp.Body) + if err != nil { + http.Redirect(w, r, "/name/" + vars["label"] + "?error=Error determining zone status", http.StatusSeeOther) + fmt.Printf("Failed to get zone contents" + err.Error()) + return + } + err = json.NewDecoder(bytes.NewReader(respData)).Decode(&namestoreResponse) + if err != nil { + http.Redirect(w, r, "/name/" + vars["label"] + "?error=Error determining zone status", http.StatusSeeOther) + fmt.Printf("Failed to get zone contents" + err.Error()) + return + } + regMetadata, err = t.getCurrentRegistrationMetadata(vars["label"], &namestoreResponse) + if err != nil { + http.Redirect(w, r, "/name/" + vars["label"] + "?error=Failed to get registration metadata", http.StatusSeeOther) + fmt.Printf("Failed to get registration metadata" + err.Error()) + return + } + } else if http.StatusNotFound != resp.StatusCode { + http.Redirect(w, r, "/name/" + vars["label"] + "?error=Error determining zone status", http.StatusSeeOther) + return + } + if nil == regMetadata { + http.Redirect(w, r, "/name/"+vars["label"], http.StatusSeeOther) + return + } + if regMetadata.RegistrationID != r.URL.Query().Get("token") { + http.Redirect(w, r, "/name/"+vars["label"] + "?error=Unauthorized", http.StatusSeeOther) + return + } + for _, record := range namestoreResponse.Records { + if isDelegationRecordType(record.RecordType) { + value = record.Value + } + } + registeredUntil := time.UnixMicro(int64(regMetadata.Expiration)).Format(time.UnixDate) + extendedExpiration := time.UnixMicro(int64(regMetadata.Expiration)).Add(t.RelativeRegistrationExpiration).Format(time.UnixDate) + fullData := map[string]interface{}{ + "label": vars["label"], + "zkey": value, + "extendedExpiration": extendedExpiration, + "registeredUntil": registeredUntil, + "token": r.URL.Query().Get("token"), + "error": r.URL.Query().Get("error"), + "cost": t.localizedAmountString(), + "suffixHint": t.SuffixHint, + } + t.EditTpl.Execute(w, fullData) + return +} func (t *Registrar) buyPage(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) @@ -374,7 +395,8 @@ func (t *Registrar) buyPage(w http.ResponseWriter, r *http.Request) { return } summaryMsg := strings.Replace(t.SummaryTemplateString, "${NAME}", vars["label"], 1) - orderID, newOrderErr := t.Merchant.AddNewOrder(*t.RegistrationCost, summaryMsg, t.BaseUrl + "/name/" + vars["label"]) + regId := generateRegistrationId() + orderID, newOrderErr := t.Merchant.AddNewOrder(*t.RegistrationCost, summaryMsg, t.BaseUrl + "/name/" + vars["label"] + "/edit?token=" + regId) if newOrderErr != nil { fmt.Println(newOrderErr) http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Unable to create order", http.StatusSeeOther) @@ -391,7 +413,7 @@ func (t *Registrar) buyPage(w http.ResponseWriter, r *http.Request) { return } paymentUntil := time.Now().Add(t.PaymentExpiration) - err = t.setupRegistrationMetadataBeforePayment(vars["label"], r.URL.Query().Get("zkey"), orderID, paymentUntil) + err = t.setupRegistrationMetadataBeforePayment(vars["label"], r.URL.Query().Get("zkey"), orderID, paymentUntil, regId) if err != nil { fmt.Println(err) http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Internal error", http.StatusSeeOther) @@ -433,7 +455,7 @@ func (t *Registrar) getCurrentRegistrationMetadata(label string, nsRecord *Names if rc == http.StatusNotFound { fmt.Printf("Registration for %s not found, removing\n", label) t.expireRegistration(label) - return nil, nil + return nil, nil } return nil, errors.New("Error determining payment status: " + paytoErr.Error()) } @@ -538,7 +560,7 @@ func (t *Registrar) namePage(w http.ResponseWriter, r *http.Request) { } if regMetadata != nil { if regMetadata.Paid { - registeredUntil = time.UnixMicro(int64(regMetadata.Expiration)).String() + registeredUntil = time.UnixMicro(int64(regMetadata.Expiration)).Format(time.UnixDate) } } fullData := map[string]interface{}{ @@ -561,7 +583,7 @@ func (t *Registrar) setupHandlers() { t.Router.HandleFunc("/", t.landingPage).Methods("GET") t.Router.HandleFunc("/name/{label}", t.namePage).Methods("GET") t.Router.HandleFunc("/name/{label}/buy", t.buyPage).Methods("GET") - t.Router.HandleFunc("/name/{label}/register", t.registerName).Methods("GET") + t.Router.HandleFunc("/name/{label}/edit", t.editRegistration).Methods("GET") t.Router.HandleFunc("/search", t.searchPage).Methods("GET") /* ToS API */ @@ -602,6 +624,11 @@ func (t *Registrar) Initialize(cfgfile string) { if err != nil { fmt.Println(err) } + editTplFile := t.Cfg.Section("gns-registrar").Key("edit_template").MustString("web/templates/edit.html") + t.EditTpl, err = template.ParseFiles(editTplFile) + if err != nil { + fmt.Println(err) + } paymentExp := t.Cfg.Section("gns-registrar").Key("payment_required_expiration").MustString("1h") recordExp := t.Cfg.Section("gns-registrar").Key("relative_delegation_expiration").MustString("24h") registrationExp := t.Cfg.Section("gns-registrar").Key("registration_expiration").MustString("120h") @@ -627,7 +654,8 @@ func (t *Registrar) Initialize(cfgfile string) { } resp, err := client.Do(req) if err != nil { - fmt.Printf("Failed to get zone key") + fmt.Println("Failed to get zone key") + os.Exit(1) return } defer resp.Body.Close()