Refactor a bunch!

This commit is contained in:
Hamcha 2021-02-25 15:14:54 +01:00
parent d6b0c343f7
commit ea68376bf9
Signed by: hamcha
GPG key ID: 41467804B19A3315
3 changed files with 210 additions and 153 deletions

75
data.go Normal file
View file

@ -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"`
}

115
handlers.go Normal file
View file

@ -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, `<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" template="https://%s/.well-known/webfinger?resource={uri}" type="application/xrd+xml" /></XRD>`, 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)
}
}
}

173
main.go
View file

@ -3,7 +3,6 @@ package main
import ( import (
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"encoding/json"
"encoding/pem" "encoding/pem"
"flag" "flag"
"fmt" "fmt"
@ -23,45 +22,11 @@ func mustGetEnv(env string) (val string) {
} }
var host string var host string
var fullhost string
type appDef struct { var prefix *string
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"`
}
func main() { 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") proto := flag.String("proto", "https", "Protocol under which this server is going to be exposed")
flag.Parse() flag.Parse()
@ -71,8 +36,24 @@ func main() {
} }
host = strings.ToLower(mustGetEnv(*prefix + "HOST")) 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 // Find app definitions
envs := os.Environ() envs := os.Environ()
envAppPrefix := *prefix + "APP_" envAppPrefix := *prefix + "APP_"
@ -113,118 +94,4 @@ func main() {
log.Printf("app registered: %s", appName) 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, `<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><Link rel="lrdd" template="https://%s/.well-known/webfinger?resource={uri}" type="application/xrd+xml" /></XRD>`, 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)
}
} }