gnunet-gns-registrar

GNU Name System registrar
Log | Files | Refs | README

commit 3997503ee1339a957a8275cb7e8c724ad618d25d
parent a84002ca1ad4f70cc7f6c5105424f7a8ab1af6d8
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Wed,  6 Dec 2023 16:40:05 +0100

Towards reservation flows.

Diffstat:
Mpkg/rest/gnsregistrar.go | 179+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mweb/templates/name.html | 12++++--------
2 files changed, 110 insertions(+), 81 deletions(-)

diff --git a/pkg/rest/gnsregistrar.go b/pkg/rest/gnsregistrar.go @@ -118,9 +118,6 @@ type Registrar struct { // Gnunet REST API basename GnunetUrl string - // Registration policy; fcfs or paid - RegistrationPolicy string - // Cost for a registration RegistrationCost *talerutil.Amount @@ -231,11 +228,29 @@ func (t *Registrar) expireRegistration(label string) (error) { return nil } +func (t *Registrar) createOrUpdateRegistration(nsRecord *NamestoreRecord) (error) { + var gnunetError GnunetError + reqString, _ := json.Marshal(nsRecord) + fmt.Println(nsRecord) + client := &http.Client{} + req, _ := http.NewRequest(http.MethodPut,t.GnunetUrl+"/namestore/" + t.RootZoneName, bytes.NewBuffer(reqString)) + resp, err := client.Do(req) + if nil != err { + return err + } + resp.Body.Close() + if http.StatusNoContent != resp.StatusCode { + fmt.Printf("Got error: %d\n", resp.StatusCode) + err = json.NewDecoder(resp.Body).Decode(&gnunetError) + return errors.New("GNUnet REST API error: " + gnunetError.Description) + } + return nil +} + func (t *Registrar) setupRegistrationMetadataBeforePayment(label string, zkey string, orderId string, paymentUntil time.Time) (error) { var namestoreRequest NamestoreRecord var delegationRecord RecordData var metadataRecord RecordData - var gnunetError GnunetError var registrationMetadata RegistrationMetadata delegationRecord.IsPrivate = true // Private until payment is through delegationRecord.IsRelativeExpiration = true @@ -265,30 +280,15 @@ func (t *Registrar) setupRegistrationMetadataBeforePayment(label string, zkey st metadataRecord.Value = string(metadataRecordValue) namestoreRequest.RecordName = label namestoreRequest.Records = []RecordData{delegationRecord,metadataRecord} - reqString, _ := json.Marshal(namestoreRequest) - // FIXME handle errors here - fmt.Println(namestoreRequest) - resp, err := http.Post(t.GnunetUrl+"/namestore/" + t.RootZoneName, "application/json", bytes.NewBuffer(reqString)) - resp.Body.Close() - if http.StatusNoContent != resp.StatusCode { - fmt.Printf("Got error: %d\n", resp.StatusCode) - err = json.NewDecoder(resp.Body).Decode(&gnunetError) - return errors.New("GNUnet REST API error: " + gnunetError.Description) - } - return nil + return t.createOrUpdateRegistration(&namestoreRequest) } + func (t *Registrar) buyPage(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - /*if t.RegistrationCost == 0 { - http.Redirect(w, r, "/name/"+vars["label"]+"/register?zkey="+r.URL.Query().Get("zkey"), http.StatusSeeOther) - return - }*/ - var metadataResponse RegistrationMetadata var namestoreResponse NamestoreRecord + var regMetadata *RegistrationMetadata w.Header().Set("Content-Type", "text/html; charset=utf-8") - var metadataExists = false - // FIXME redirect back if label empty resp, err := http.Get(t.GnunetUrl + "/namestore/" + t.RootZoneName + "/" + vars["label"] + "?include_maintenance=yes") if err != nil { fmt.Printf("Failed to get zone contents") @@ -306,35 +306,19 @@ func (t *Registrar) buyPage(w http.ResponseWriter, r *http.Request) { fmt.Printf("Failed to get zone contents" + err.Error()) return } - for _, record := range namestoreResponse.Records { - if record.RecordType == "PKEY" { - continue - } - if record.RecordType == "TXT" { - err = json.Unmarshal([]byte(record.Value), &metadataResponse) - if err != nil { - fmt.Printf("Failed to get zone contents" + err.Error()) - return - } - metadataExists = true - } + regMetadata, err = t.getCurrentRegistrationMetadata(vars["label"], &namestoreResponse) + if err != nil { + fmt.Printf("Failed to get registration metadata" + err.Error()) + return } - } else { - fmt.Printf("Failed to get zone contents" + err.Error()) + } else if http.StatusNotFound != resp.StatusCode { + http.Redirect(w, r, "/name/" + vars["label"] + "?error=Registration failed: Error determining zone status", http.StatusSeeOther) return } var errorMsg = "" - // FIXME check for order expiration - if metadataExists { - if metadataResponse.Paid { - http.Redirect(w, r, "/name/" +vars["label"] + "?error=Registration failed: Already paid", http.StatusSeeOther) - return - } - if !time.Now().After(metadataResponse.NeedsPaymentUntil) { - http.Redirect(w, r, "/name/" + vars["label"] + "?error=Registration failed: Name is being paid", http.StatusSeeOther) - return - } - t.expireRegistration(vars["label"]) + if nil != regMetadata { + http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Pending buy order", http.StatusSeeOther) + return } orderID, newOrderErr := t.Merchant.AddNewOrder(*t.RegistrationCost, "GNS registrar name registration", "/name/" + vars["label"]) if newOrderErr != nil { @@ -342,9 +326,7 @@ func (t *Registrar) buyPage(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Unable to create order", http.StatusSeeOther) return } - metadataResponse.OrderID = orderID - - payto, paytoErr := t.Merchant.IsOrderPaid(metadataResponse.OrderID) + payto, paytoErr := t.Merchant.IsOrderPaid(orderID) if paytoErr != nil { http.Redirect(w, r, "/name/"+vars["label"] + "?error=Registration failed: Error getting payment data", http.StatusSeeOther) return @@ -355,7 +337,12 @@ func (t *Registrar) buyPage(w http.ResponseWriter, r *http.Request) { return } paymentUntil := time.Now().Add(t.PaymentExpiration) - err = t.setupRegistrationMetadataBeforePayment(vars["label"], vars["zkey"], orderID, paymentUntil) + err = t.setupRegistrationMetadataBeforePayment(vars["label"], r.URL.Query().Get("zkey"), orderID, paymentUntil) + 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), @@ -370,14 +357,60 @@ func (t *Registrar) buyPage(w http.ResponseWriter, r *http.Request) { return } +func (t *Registrar) getCurrentRegistrationMetadata(label string, nsRecord *NamestoreRecord) (*RegistrationMetadata, error) { + var regMetadata RegistrationMetadata + var haveMetadata = false + for _, record := range nsRecord.Records { + if record.RecordType == "TXT" { + err := json.Unmarshal([]byte(record.Value), &regMetadata) + if err != nil { + fmt.Printf("Failed to get zone contents" + err.Error()) + return nil, err + } + haveMetadata = true + } + } + if !haveMetadata { + return nil, nil + } + if !regMetadata.Paid { + payto, paytoErr := t.Merchant.IsOrderPaid(regMetadata.OrderID) + if nil != paytoErr { + return nil, errors.New("Error determining payment status") + } + if "" == payto { + // Order was paid! + regMetadata.Paid = true + for _, record := range nsRecord.Records { + if record.RecordType == "PKEY" { + record.IsPrivate = false + } + if record.RecordType == "TXT" { + metadataRecordValue, err := json.Marshal(regMetadata) + if nil != err { + return nil, err + } + record.Value = string(metadataRecordValue) + } + } + t.createOrUpdateRegistration(nsRecord) + } else { + if time.Now().After(regMetadata.NeedsPaymentUntil) { + t.expireRegistration(label) + return nil, nil + } + } + } + return &regMetadata, nil +} + func (t *Registrar) namePage(w http.ResponseWriter, r *http.Request) { - var metadataResponse RegistrationMetadata var namestoreResponse NamestoreRecord vars := mux.Vars(r) w.Header().Set("Content-Type", "text/html; charset=utf-8") - var available = false var value = "" - var registeredUntil = "unkown" + var registeredUntil = "" + var regMetadata *RegistrationMetadata var registered = r.URL.Query().Get("registered") == "true" // FIXME redirect back if label empty resp, err := http.Get(t.GnunetUrl + "/namestore/" + t.RootZoneName + "/" + vars["label"] + "?include_maintenance=yes") @@ -389,40 +422,40 @@ func (t *Registrar) namePage(w http.ResponseWriter, r *http.Request) { if http.StatusOK == resp.StatusCode { respData, err := io.ReadAll(resp.Body) if err != nil { - fmt.Printf("Failed to get zone contents" + err.Error()) + fmt.Println("Failed to get zone contents: " + err.Error()) return } err = json.NewDecoder(bytes.NewReader(respData)).Decode(&namestoreResponse) if err != nil { - fmt.Printf("Failed to get zone contents" + err.Error()) + fmt.Println("Failed to get zone contents: " + err.Error()) return } - for _, record := range namestoreResponse.Records { - if record.RecordType == "PKEY" { - value = record.Value - } - if record.RecordType == "TXT" { - err = json.Unmarshal([]byte(record.Value), &metadataResponse) - if err != nil { - fmt.Printf("Failed to get zone contents" + err.Error()) - return - } - registeredUntil = time.UnixMicro(int64(metadataResponse.Expiration)).String() - } + regMetadata, err = t.getCurrentRegistrationMetadata(vars["label"], &namestoreResponse) + if err != nil { + fmt.Println("Failed to get registration metadata: " + err.Error()) + return } - } else { - fmt.Printf("Failed to get zone contents" + err.Error()) + } else if http.StatusNotFound != resp.StatusCode { + http.Redirect(w, r, "/name/" + vars["label"] + "?error=Error retrieving zone information.", http.StatusSeeOther) return } - available = value == "" + for _, record := range namestoreResponse.Records { + if record.RecordType == "PKEY" { + value = record.Value + } + } + if regMetadata != nil { + if regMetadata.Paid { + registeredUntil = time.UnixMicro(int64(regMetadata.Expiration)).String() + } + } fullData := map[string]interface{}{ "label": vars["label"], "error": r.URL.Query().Get("error"), "cost": t.RegistrationCost, - "available": available, + "available": regMetadata == nil, "currentValue": value, "suffixHint": t.SuffixHint, - "registrationPolicy": t.RegistrationPolicy, "registeredUntil": registeredUntil, "registrationSuccess": registered, } diff --git a/web/templates/name.html b/web/templates/name.html @@ -32,25 +32,21 @@ <form action="/name/{{.label}}/buy" method="get" class="align-items-center"> <div class="input-group mb-2 w-75"> <span class="input-group-text" id="reg-prefix"><i class="text-primary">{{.label}}</i>.<i class="text-secondary">{{.suffixHint}}</i>: </span> - <input type="hidden" name="label" value="{{.label}}"> <input name="zkey" class="form-control form-control-lg" maxlength="63" type="text" placeholder="Enter your zone key here!" required autofocus> <input class="btn btn-primary" type="submit" value="Register for {{.cost}}"> </div> </form> {{else}} - <h1 class="mb-5"><i class="text-primary">{{.label}}</i> is already <span class="text-danger">taken</span>!</h1> + <h1 class="mb-5"><i class="text-primary">{{.label}}</i> is already <span class="text-danger">reserved</span>!</h1> <form action="/name/{{.label}}/renew" method="get" class="align-items-center"> <div class="input-group mb-2 w-75"> <span class="input-group-text" id="reg-prefix"><i class="text-primary">{{.label}}</i>.<i class="text-secondary">{{.suffixHint}}</i>: </span> <input name="label" disabled="{{.modificationAllowed}}" class="form-control form-control-lg" maxlength="63" type="text" placeholder="Enter your zone key here!" value="{{.currentValue}}" required autofocus> - {{if .modificationAllowed}} - <input class="btn btn-primary" disabled="{{.modificationAllowed}}" type="submit" value="Update"> - {{end}} </div> </form> - <form action="/renew" method="get" class="align-items-center"> - <span>Registration valid until: <i>{{.registeredUntil}}</i></span><br/> - </form> + {{if ne .registeredUntil ""}} + <span>Registration valid until: <i>{{.registeredUntil}}</i></span><br/> + {{end}} {{end}} <a class="btn btn-primary mt-5" href="/">Back</a> </div>