gnunet-gns-registrar

GNU Name System registrar
Log | Files | Refs | README

commit 175dd7e04feac6ce0ceba4a2f37b076df336de43
parent d7459187752f7adecd4202087d2edad38951d775
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Mon, 11 Dec 2023 20:15:41 +0100

edit value and extend registration

Diffstat:
Mpkg/rest/gnsregistrar.go | 167++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Mweb/templates/edit.html | 11++++-------
2 files changed, 153 insertions(+), 25 deletions(-)

diff --git a/pkg/rest/gnsregistrar.go b/pkg/rest/gnsregistrar.go @@ -272,7 +272,7 @@ func (t *Registrar) setupRegistrationMetadataBeforePayment(label string, zkey st OrderID: orderId, NeedsPaymentUntil: paymentUntil, RegistrationID: regId, - Expiration: uint64(time.Now().Add(t.RelativeRegistrationExpiration).UnixMicro()), + Expiration: 0, } metadataRecordValue, err := json.Marshal(registrationMetadata) if nil != err { @@ -284,6 +284,79 @@ func (t *Registrar) setupRegistrationMetadataBeforePayment(label string, zkey st return t.createOrUpdateRegistration(&namestoreRequest) } +func (t *Registrar) updateRegistration(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + var namestoreResponse NamestoreRecord + var zkeyRecord RecordData + var metaRecord RecordData + var regMetadata *RegistrationMetadata + 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 { + http.Redirect(w, r, "/" + "?error=Registration failed: Failed to get zone contents", http.StatusSeeOther) + 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, "/" + "?error=Registration failed: Failed to get zone contents", 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, "/" + "?error=Registration failed: Failed to get zone contents", 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, "/" + "?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 + } + r.ParseForm() + token := r.Form.Get("token") + zkey := r.Form.Get("zkey") + if regMetadata.RegistrationID != token { + http.Redirect(w, r, "/name/"+vars["label"] + "?error=Unauthorized", http.StatusSeeOther) + return + } + for _, record := range namestoreResponse.Records { + if isDelegationRecordType(record.RecordType) { + zkeyRecord = record + } else { + metaRecord = record + } + } + zkeyRecord.Value = zkey + zkeyRecord.RecordType = guessDelegationRecordType(zkey) + namestoreResponse.Records = []RecordData{metaRecord, zkeyRecord} + err = t.createOrUpdateRegistration(&namestoreResponse) + if nil != err { + fmt.Println(err) + http.Redirect(w, r, "/name/"+vars["label"] + "?error=Update: Internal error", http.StatusSeeOther) + return + } + http.Redirect(w, r, "/name/"+vars["label"] + "/edit?token=" + token, http.StatusSeeOther) + return +} + func (t *Registrar) editRegistration(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) var namestoreResponse NamestoreRecord @@ -297,6 +370,7 @@ func (t *Registrar) editRegistration(w http.ResponseWriter, r *http.Request) { } resp, err := client.Do(req) if err != nil { + http.Redirect(w, r, "/" + "?error=Registration failed: Failed to get zone contents", http.StatusSeeOther) fmt.Printf("Failed to get zone contents") return } @@ -304,19 +378,19 @@ func (t *Registrar) editRegistration(w http.ResponseWriter, r *http.Request) { 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) + http.Redirect(w, r, "/" + "?error=Registration failed: Failed to get zone contents", 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) + http.Redirect(w, r, "/" + "?error=Registration failed: Failed to get zone contents", 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) + http.Redirect(w, r, "/" + "?error=Failed to get registration metadata", http.StatusSeeOther) fmt.Printf("Failed to get registration metadata" + err.Error()) return } @@ -365,6 +439,7 @@ func (t *Registrar) buyPage(w http.ResponseWriter, r *http.Request) { } resp, err := client.Do(req) if err != nil { + http.Redirect(w, r, "/" + "?error=Registration failed: Failed to get zone contents", http.StatusSeeOther) fmt.Printf("Failed to get zone contents") return } @@ -372,16 +447,19 @@ func (t *Registrar) buyPage(w http.ResponseWriter, r *http.Request) { if http.StatusOK == resp.StatusCode { respData, err := io.ReadAll(resp.Body) if err != nil { + http.Redirect(w, r, "/" + "?error=Registration failed: Failed to get zone contents", 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, "/" + "?error=Registration failed: Failed to get zone contents", 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, "/" + "?error=Registration failed: Failed to get registration metadata", http.StatusSeeOther) fmt.Printf("Failed to get registration metadata" + err.Error()) return } @@ -390,12 +468,18 @@ func (t *Registrar) buyPage(w http.ResponseWriter, r *http.Request) { return } var errorMsg = "" + var regId = "" if nil != regMetadata { - http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Pending buy order", http.StatusSeeOther) - return + if regMetadata.Paid == false { + http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Pending buy order", http.StatusSeeOther) + return + } + regMetadata.Paid = false + regId = regMetadata.RegistrationID + } else { + regId = generateRegistrationId() } summaryMsg := strings.Replace(t.SummaryTemplateString, "${NAME}", vars["label"], 1) - regId := generateRegistrationId() orderID, newOrderErr := t.Merchant.AddNewOrder(*t.RegistrationCost, summaryMsg, t.BaseUrl + "/name/" + vars["label"] + "/edit?token=" + regId) if newOrderErr != nil { fmt.Println(newOrderErr) @@ -413,17 +497,46 @@ 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, regId) - if err != nil { - fmt.Println(err) - http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Internal error", http.StatusSeeOther) - return + if nil != regMetadata { + var newZkeyRecord RecordData + var metaRecord RecordData + for _, record := range namestoreResponse.Records { + if isDelegationRecordType(record.RecordType) { + record.IsPrivate = false + newZkeyRecord = record + } else { + metaRecord = record + } + } + regMetadata.NeedsPaymentUntil = paymentUntil + regMetadata.OrderID = orderID + metadataRecordValue, err := json.Marshal(regMetadata) + if nil != err { + fmt.Println(err) + http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Internal error", http.StatusSeeOther) + return + } + metaRecord.Value = string(metadataRecordValue) + namestoreResponse.Records = []RecordData{metaRecord, newZkeyRecord} + err = t.createOrUpdateRegistration(&namestoreResponse) + if nil != err { + fmt.Println(err) + http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Internal error", http.StatusSeeOther) + return + } + } else { + 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) + return + } } encodedPng := base64.StdEncoding.EncodeToString(qrPng) fullData := map[string]interface{}{ "qrCode": template.URL("data:image/png;base64," + encodedPng), "payto": template.URL(payto), - "fulfillmentUrl": template.URL(t.BaseUrl + "/name/" + vars["label"]), + "fulfillmentUrl": template.URL(t.BaseUrl + "/name/" + vars["label"] + "/edit?token=" + regId), "label": vars["label"], "error": errorMsg, "cost": t.localizedAmountString(), @@ -449,13 +562,18 @@ func (t *Registrar) getCurrentRegistrationMetadata(label string, nsRecord *Names if !haveMetadata { return nil, nil } + // Does this registration have an unpaid order? if yes, check payment status and update if necessary. if !regMetadata.Paid { rc, payto, paytoErr := t.Merchant.IsOrderPaid(regMetadata.OrderID) if nil != paytoErr { if rc == http.StatusNotFound { - fmt.Printf("Registration for %s not found, removing\n", label) - t.expireRegistration(label) - return nil, nil + if time.Now().After(time.UnixMicro(int64(regMetadata.Expiration))) { + fmt.Printf("Registration for %s not found, removing\n", label) + t.expireRegistration(label) + return nil, nil + } else { + return &regMetadata, nil + } } return nil, errors.New("Error determining payment status: " + paytoErr.Error()) } @@ -478,10 +596,22 @@ func (t *Registrar) getCurrentRegistrationMetadata(label string, nsRecord *Names newMetaRecord = record } } + // Note how for every time the payment is completed, the registration duration increases + regMetadata.Expiration += uint64(t.RelativeRegistrationExpiration.Microseconds()) + metadataRecordValue, err := json.Marshal(regMetadata) + if nil != err { + return nil, err + } + newMetaRecord.Value = string(metadataRecordValue) nsRecord.Records = []RecordData{newMetaRecord, newZkeyRecord} - t.createOrUpdateRegistration(nsRecord) + err = t.createOrUpdateRegistration(nsRecord) + if nil != err { + return nil, err + } + return &regMetadata, nil } else { - if time.Now().After(regMetadata.NeedsPaymentUntil) { + // Remove metadata if payment limit exceeded and registration expired + if time.Now().After(regMetadata.NeedsPaymentUntil) && time.Now().After(time.UnixMicro(int64(regMetadata.Expiration))) { fmt.Printf("Payment request for %s has expired, removing\n", label) t.expireRegistration(label) return nil, nil @@ -584,6 +714,7 @@ func (t *Registrar) setupHandlers() { t.Router.HandleFunc("/name/{label}", t.namePage).Methods("GET") t.Router.HandleFunc("/name/{label}/buy", t.buyPage).Methods("GET") t.Router.HandleFunc("/name/{label}/edit", t.editRegistration).Methods("GET") + t.Router.HandleFunc("/name/{label}/edit", t.updateRegistration).Methods("POST") t.Router.HandleFunc("/search", t.searchPage).Methods("GET") /* ToS API */ diff --git a/web/templates/edit.html b/web/templates/edit.html @@ -32,14 +32,11 @@ </div> </form> <span>Registration valid until: <i>{{.registeredUntil}}</i></span><br/> - <form action="/name/{{.label}}/renew" method="post" class="align-items-center pt-5"> - <div class="row"> - <div class="col-lg-12"> - <input class="btn btn-primary" type="submit" value="Extend registration until {{.extendedExpiration}} for {{.cost}}"> - <input type="hidden" value="{{.token}}" name="token"> - </div> + <div class="row mt-5"> + <div class="col-lg-12"> + <a class="btn btn-primary" href="/name/{{.label}}/buy">Extend registration until {{.extendedExpiration}} for {{.cost}}</a> </div> - </form> + </div> <a class="btn btn-primary mt-5" href="/">Back</a> </div> </body>