From ea68376bf92b6fc0522687bca6d4d651900cf83e Mon Sep 17 00:00:00 2001 From: Hamcha Date: Thu, 25 Feb 2021 15:14:54 +0100 Subject: [PATCH] Refactor a bunch! --- data.go | 75 +++++++++++++++++++++++ handlers.go | 115 ++++++++++++++++++++++++++++++++++ main.go | 173 ++++++---------------------------------------------- 3 files changed, 210 insertions(+), 153 deletions(-) create mode 100644 data.go create mode 100644 handlers.go diff --git a/data.go b/data.go new file mode 100644 index 0000000..176c710 --- /dev/null +++ b/data.go @@ -0,0 +1,75 @@ +package main + +import ( + "crypto/rsa" + "time" +) + +type appDef struct { + Name string + WebhookURL string + PublicKey *rsa.PublicKey +} + +var apps map[string]appDef + +type profileLink struct { + Rel string `json:"rel"` + Type string `json:"type"` + Href string `json:"href"` +} + +type profileWebFingerData struct { + Subject string `json:"subject"` + Links []profileLink `json:"links"` +} + +type pubKeyBlock struct { + ID string `json:"id"` + Owner string `json:"owner"` + PEM string `json:"publicKeyPem"` +} + +type profilePage struct { + Context []string `json:"@context"` + ID string `json:"id"` + Type string `json:"type"` + PreferredUsername string `json:"preferredUsername"` + Name string `json:"name"` + Inbox string `json:"inbox"` + Followers string `json:"followers"` + PublicKey *pubKeyBlock `json:"publicKey,omitempty"` +} + +type inboxPush struct { + Actor string `json:"actor"` + CC []string `json:"cc"` + ID string `json:"id"` + Object interface{} `json:"object"` + To []string `json:"to"` + Type string `json:"type"` + Context string `json:"context,omitempty"` + Published time.Time `json:"published,omitempty"` +} + +type inboxCreatePayload struct { + Actor string `json:"actor"` + AttributedTo string `json:"attributedTo"` + CC []string `json:"cc"` + Content string `json:"content"` + Context string `json:"context"` + Conversation string `json:"conversation"` + ID string `json:"id"` + Published time.Time `json:"published"` + Sensitive bool `json:"sensitive"` + Source string `json:"source"` + Tag []tag `json:"tag"` + To []string `json:"to"` + Type string `json:"Type"` +} + +type tag struct { + Href string `json:"href"` + Name string `json:"name"` + Type string `json:"type"` +} diff --git a/handlers.go b/handlers.go new file mode 100644 index 0000000..593d969 --- /dev/null +++ b/handlers.go @@ -0,0 +1,115 @@ +package main + +import ( + "crypto/x509" + "encoding/json" + "encoding/pem" + "fmt" + "net/http" + "strings" +) + +func hostMetaHandler(w http.ResponseWriter, r *http.Request) { + headers := w.Header() + headers.Add("Content-Type", "application/xrd+xml") + headers.Add("Content-Type", "charset=utf-8") + fmt.Fprintf(w, ``, host) +} + +func handlerUserPage(w http.ResponseWriter, r *http.Request) { + parts := strings.SplitN(strings.Trim(r.URL.Path, " /"), "/", 2) + if len(parts) < 2 { + http.Error(w, "page not found", http.StatusNotFound) + return + } + name := parts[1] + app, ok := apps[name] + if !ok { + http.Error(w, "page not found", http.StatusNotFound) + return + } + userURL := fmt.Sprintf("%s/u/%s", fullhost, name) + + contextes := []string{ + "https://www.w3.org/ns/activitystreams", + } + + var pubkey *pubKeyBlock = nil + if app.PublicKey != nil { + pubKeyBytes := x509.MarshalPKCS1PublicKey(app.PublicKey) + pubKeyPem := string(pem.EncodeToMemory(&pem.Block{ + Type: "PUBLIC KEY", + Bytes: pubKeyBytes, + })) + contextes = append(contextes, "https://w3id.org/security/v1") + pubkey = &pubKeyBlock{ + ID: userURL + "#main-key", + Owner: userURL, + PEM: pubKeyPem, + } + } + headers := w.Header() + headers.Add("Content-Type", "application/activity+json") + headers.Add("Content-Type", "charset=utf-8") + err := json.NewEncoder(w).Encode(profilePage{ + Context: contextes, + ID: userURL, + Type: "Person", + PreferredUsername: name, + Name: app.Name, + Inbox: fmt.Sprintf("%s/inbox", fullhost), + Followers: fmt.Sprintf("%s/followers", userURL), + PublicKey: pubkey, + }) + if err != nil { + http.Error(w, "Error encoding page: "+err.Error(), http.StatusInternalServerError) + } +} + +func handlerWebFinger(w http.ResponseWriter, r *http.Request) { + params := r.URL.Query() + resource := params.Get("resource") + if resource == "" { + http.Error(w, "must include a valid resource", http.StatusBadRequest) + return + } + + parts := strings.SplitN(resource, ":", 2) + if len(parts) < 2 { + http.Error(w, "invalid resource specified", http.StatusBadRequest) + return + } + switch parts[0] { + case "acct": + // Split user by host + userParts := strings.SplitN(parts[1], "@", 2) + if len(userParts) < 2 || strings.ToLower(userParts[1]) != host { + http.Error(w, "Invalid user specified", http.StatusBadRequest) + return + } + // Check if user is a registered app + _, ok := apps[userParts[0]] + if !ok { + http.Error(w, "user not found", http.StatusNotFound) + return + } + headers := w.Header() + headers.Add("Content-Type", "application/json") + headers.Add("Content-Type", "charset=utf-8") + + err := json.NewEncoder(w).Encode(profileWebFingerData{ + Subject: resource, + Links: []profileLink{ + { + Rel: "self", + Type: "application/activity+json", + Href: fmt.Sprintf("%s/u/%s", fullhost, userParts[0]), + }, + }, + }) + + if err != nil { + http.Error(w, "Error encoding webfinger: "+err.Error(), http.StatusInternalServerError) + } + } +} diff --git a/main.go b/main.go index cb88e9c..db1c459 100644 --- a/main.go +++ b/main.go @@ -3,7 +3,6 @@ package main import ( "crypto/rsa" "crypto/x509" - "encoding/json" "encoding/pem" "flag" "fmt" @@ -23,45 +22,11 @@ func mustGetEnv(env string) (val string) { } var host string - -type appDef struct { - Name string - WebhookURL string - PublicKey *rsa.PublicKey -} - -var apps map[string]appDef - -type profileLink struct { - Rel string `json:"rel"` - Type string `json:"type"` - Href string `json:"href"` -} - -type profileWebFingerData struct { - Subject string `json:"subject"` - Links []profileLink `json:"links"` -} - -type pubKeyBlock struct { - ID string `json:"id"` - Owner string `json:"owner"` - PEM string `json:"publicKeyPem"` -} - -type profilePage struct { - Context []string `json:"@context"` - ID string `json:"id"` - Type string `json:"type"` - PreferredUsername string `json:"preferredUsername"` - Name string `json:"name"` - Inbox string `json:"inbox"` - Followers string `json:"followers"` - PublicKey *pubKeyBlock `json:"publicKey,omitempty"` -} +var fullhost string +var prefix *string func main() { - prefix := flag.String("prefix", "AW_", "Environment var prefix") + prefix = flag.String("prefix", "AW_", "Environment var prefix") proto := flag.String("proto", "https", "Protocol under which this server is going to be exposed") flag.Parse() @@ -71,8 +36,24 @@ func main() { } host = strings.ToLower(mustGetEnv(*prefix + "HOST")) - fullhost := fmt.Sprintf("%s://%s", *proto, host) + fullhost = fmt.Sprintf("%s://%s", *proto, host) + discoverApps() + + http.HandleFunc("/.well-known/host-meta", hostMetaHandler) + http.HandleFunc("/.well-known/webfinger", handlerWebFinger) + http.HandleFunc("/u/", handlerUserPage) + http.HandleFunc("/inbox", func(w http.ResponseWriter, r *http.Request) { + byt, _ := ioutil.ReadAll(r.Body) + fmt.Println(string(byt)) + }) + + if err := http.ListenAndServe(bind, nil); err != nil { + log.Fatalf("error while listening: %f", err) + } +} + +func discoverApps() { // Find app definitions envs := os.Environ() envAppPrefix := *prefix + "APP_" @@ -113,118 +94,4 @@ func main() { log.Printf("app registered: %s", appName) } - - http.HandleFunc("/.well-known/host-meta", func(w http.ResponseWriter, r *http.Request) { - headers := w.Header() - headers.Add("Content-Type", "application/xrd+xml") - headers.Add("Content-Type", "charset=utf-8") - fmt.Fprintf(w, ``, host) - }) - - http.HandleFunc("/.well-known/webfinger", func(w http.ResponseWriter, r *http.Request) { - params := r.URL.Query() - resource := params.Get("resource") - if resource == "" { - http.Error(w, "must include a valid resource", http.StatusBadRequest) - return - } - - parts := strings.SplitN(resource, ":", 2) - if len(parts) < 2 { - http.Error(w, "invalid resource specified", http.StatusBadRequest) - return - } - switch parts[0] { - case "acct": - // Split user by host - userParts := strings.SplitN(parts[1], "@", 2) - if len(userParts) < 2 || strings.ToLower(userParts[1]) != host { - http.Error(w, "Invalid user specified", http.StatusBadRequest) - return - } - // Check if user is a registered app - _, ok := apps[userParts[0]] - if !ok { - http.Error(w, "user not found", http.StatusNotFound) - return - } - headers := w.Header() - headers.Add("Content-Type", "application/json") - headers.Add("Content-Type", "charset=utf-8") - - err := json.NewEncoder(w).Encode(profileWebFingerData{ - Subject: resource, - Links: []profileLink{ - { - Rel: "self", - Type: "application/activity+json", - Href: fmt.Sprintf("%s/u/%s", fullhost, userParts[0]), - }, - }, - }) - - if err != nil { - http.Error(w, "Error encoding webfinger: "+err.Error(), http.StatusInternalServerError) - } - } - }) - - http.HandleFunc("/u/", func(w http.ResponseWriter, r *http.Request) { - parts := strings.SplitN(strings.Trim(r.URL.Path, " /"), "/", 2) - if len(parts) < 2 { - http.Error(w, "page not found", http.StatusNotFound) - return - } - name := parts[1] - app, ok := apps[name] - if !ok { - http.Error(w, "page not found", http.StatusNotFound) - return - } - userURL := fmt.Sprintf("%s/u/%s", fullhost, name) - - contextes := []string{ - "https://www.w3.org/ns/activitystreams", - } - - var pubkey *pubKeyBlock = nil - if app.PublicKey != nil { - pubKeyBytes := x509.MarshalPKCS1PublicKey(app.PublicKey) - pubKeyPem := string(pem.EncodeToMemory(&pem.Block{ - Type: "PUBLIC KEY", - Bytes: pubKeyBytes, - })) - contextes = append(contextes, "https://w3id.org/security/v1") - pubkey = &pubKeyBlock{ - ID: userURL + "#main-key", - Owner: userURL, - PEM: pubKeyPem, - } - } - headers := w.Header() - headers.Add("Content-Type", "application/activity+json") - headers.Add("Content-Type", "charset=utf-8") - err := json.NewEncoder(w).Encode(profilePage{ - Context: contextes, - ID: userURL, - Type: "Person", - PreferredUsername: name, - Name: app.Name, - Inbox: fmt.Sprintf("%s/inbox", fullhost), - Followers: fmt.Sprintf("%s/followers", userURL), - PublicKey: pubkey, - }) - if err != nil { - http.Error(w, "Error encoding page: "+err.Error(), http.StatusInternalServerError) - } - }) - - http.HandleFunc("/inbox", func(w http.ResponseWriter, r *http.Request) { - byt, _ := ioutil.ReadAll(r.Body) - fmt.Println(string(byt)) - }) - - if err := http.ListenAndServe(bind, nil); err != nil { - log.Fatalf("error while listening: %f", err) - } }