taldir

Directory service to resolve wallet mailboxes by messenger addresses
Log | Files | Refs | Submodules | README | LICENSE

commit 8beaa21429a1b3f72a36b5d9059f7721b54340e9
Author: Martin Schanzenbach <mschanzenbach@posteo.de>
Date:   Sun, 17 Apr 2022 19:24:17 +0200

Initial commit

Diffstat:
ADockerfile | 8++++++++
Aconfig.json | 6++++++
Ago.mod | 11+++++++++++
Ataldir.go | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 226 insertions(+), 0 deletions(-)

diff --git a/Dockerfile b/Dockerfile @@ -0,0 +1,8 @@ +FROM golang:1.16.15-alpine AS build +WORKDIR /src +ENV CGO_ENABLED=0 +COPY . . +RUN go build -o /out/taldir . + +FROM scratch AS bin +COPY --from=build /out/taldir / diff --git a/config.json b/config.json @@ -0,0 +1,6 @@ +{ + "production": false, + "db_backend": "sqlite", + "validators": ["email","phone"], + "email_sender": "taldir@taler.net" +} diff --git a/go.mod b/go.mod @@ -0,0 +1,11 @@ +module taler.net/taldir + +go 1.16 + +require ( + github.com/gorilla/mux v1.8.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/mattn/go-sqlite3 v1.14.12 // indirect + gorm.io/driver/sqlite v1.3.1 // indirect + gorm.io/gorm v1.23.4 // indirect +) diff --git a/taldir.go b/taldir.go @@ -0,0 +1,201 @@ +package main + +import ( + "os" + "fmt" + "log" + "net/http" + "encoding/json" + "github.com/gorilla/mux" + "gorm.io/gorm" + "gorm.io/driver/sqlite" + "encoding/base32" + "math/rand" + "net/smtp" +) + +type Validator interface { + TriggerValidation() string +} + +type Configuration struct { + Production bool + DbBackend string `json:"db_backend"` + Validators []string + EmailSender string `json:"email_sender"` +} + +type Entry struct { + gorm.Model + IdentityKey string `json:"identity_key"` + IdentityKeyType string `json:"identity_key_type"` + TalerWalletKey string `json:"taler_wallet_key"` + Status string `json:"status"` +} + +type Validation struct { + gorm.Model + IdentityKey string `json:"identity_key"` + IdentityKeyType string `json:"type"` + ValidationReference string `json:"reference"` +} + + +// let's declare a global Articles array +// that we can then populate in our main function +// to simulate a database +var Entries []Entry + +var db *gorm.DB + +var config Configuration + +func sendEmail(recipient string, ref Validation) { + + from := config.EmailSender + to := []string{ + recipient, + } + + // smtp server configuration. + smtpHost := "localhost" + smtpPort := "587" + + // Message. + message := []byte("Please click here to validate your Taldir identity: " + ref.ValidationReference) + + // Sending email. + err := smtp.SendMail(smtpHost+":"+smtpPort, nil, from, to, message) + if err != nil { + fmt.Println(err) + return + } + fmt.Println("Email Sent Successfully!") +} + +func returnSingleEntry(w http.ResponseWriter, r *http.Request){ + vars := mux.Vars(r) + // Loop over all of our Articles + // if the article.Id equals the key we pass in + // return the article encoded as JSON + var entry Entry + var err = db.First(&entry, "identity_key = ?", vars["identity_key"]).Error + if err == nil { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(entry) + return + } + w.WriteHeader(http.StatusNotFound) +} + +func validateSingleEntry(w http.ResponseWriter, r *http.Request){ + vars := mux.Vars(r) + var entry Entry + var ref Validation + //TODO actually validate + var err = db.First(&ref, "validation_reference = ?", vars["reference"]).Error + if err != nil { + w.WriteHeader(http.StatusNotFound) + } + err = db.First(&entry, "identity_key = ?", entry.IdentityKey).Error + if err != nil { + w.WriteHeader(http.StatusNotFound) + } + err = db.Model(&entry).Update("status", "validated").Error + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + } +} + +func generateToken() string { + randBytes := make([]byte, 32) + _, err := rand.Read(randBytes) + if err != nil { + panic(err) + } + return base32.StdEncoding.EncodeToString(randBytes) +} + +func addSingleEntry(w http.ResponseWriter, r *http.Request){ + // Loop over all of our Articles + // if the article.Id equals the key we pass in + // return the article encoded as JSON + var entry Entry + if r.Body == nil { + http.Error(w, "No request body", 400) + return + } + err := json.NewDecoder(r.Body).Decode(&entry) + if err != nil { + http.Error(w, err.Error(), 400) + return + } + ref := Validation{IdentityKey: entry.IdentityKey, ValidationReference: generateToken() } + fmt.Println("Got ID key:", entry.IdentityKey) + err = db.First(&entry, "identity_key = ?", entry.IdentityKey).Error + if err == nil { + w.WriteHeader(http.StatusConflict) + return + } + entry.Status = "unvalidated" + err = db.Create(&entry).Error + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + err = db.Create(&ref).Error + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + fmt.Println("Validation reference created:", ref) + sendEmail(ref.IdentityKey, ref) + json.NewEncoder(w).Encode(entry) +} + + + +func handleRequests() { + // creates a new instance of a mux router + myRouter := mux.NewRouter().StrictSlash(true) + // replace http.HandleFunc with myRouter.HandleFunc + myRouter.HandleFunc("/directory/{id}", returnSingleEntry).Methods("GET") + myRouter.HandleFunc("/validation/{reference}", validateSingleEntry).Methods("POST") + myRouter.HandleFunc("/directory/", addSingleEntry).Methods("POST") + // finally, instead of passing in nil, we want + // to pass in our newly created router as the second + // argument + log.Fatal(http.ListenAndServe(":10000", myRouter)) +} + +func main() { + file, _ := os.Open("config.json") + defer file.Close() + decoder := json.NewDecoder(file) + config := Configuration{} + err := decoder.Decode(&config) + if err != nil { + fmt.Println("error:", err) + } + fmt.Println("Configuration:", config) + _db, err := gorm.Open(sqlite.Open("./taldir.db"), &gorm.Config{}) + if err != nil { + panic(err) + } + db = _db + if err := db.AutoMigrate(&Entry{}); err != nil { + panic(err) + } + if err := db.AutoMigrate(&Validation{}); err != nil { + panic(err) + } + var entry Entry; + db.Create(&Entry{IdentityKey: "jdoe@example.com", IdentityKeyType: "email", TalerWalletKey: "OIU123"}) + db.Create(&Entry{IdentityKey: "+12345678", IdentityKeyType: "phone", TalerWalletKey: "OIU123"}) + handleRequests() + db.First(&entry, "identity_key = ?", "jdoe@example.com") + db.Delete(&entry) + db.First(&entry, "identity_key = ?", "+12345678") + db.Delete(&entry) + +}