From 93c1b7a8b0fc502a5ea9172493b40c351c508527 Mon Sep 17 00:00:00 2001 From: Sebastian Tobie Date: Sun, 6 Nov 2022 10:53:38 +0100 Subject: [PATCH] added an wellknown middleware --- middleware/wellknown/doc.go | 2 + middleware/wellknown/site.go | 15 ++++ middleware/wellknown/wellknown.go | 130 ++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 middleware/wellknown/doc.go create mode 100644 middleware/wellknown/site.go create mode 100644 middleware/wellknown/wellknown.go diff --git a/middleware/wellknown/doc.go b/middleware/wellknown/doc.go new file mode 100644 index 0000000..deaee9a --- /dev/null +++ b/middleware/wellknown/doc.go @@ -0,0 +1,2 @@ +// Package wellknown provides an middleware that lets sites to register themselfs to wellknown uris +package wellknown diff --git a/middleware/wellknown/site.go b/middleware/wellknown/site.go new file mode 100644 index 0000000..e09a1cb --- /dev/null +++ b/middleware/wellknown/site.go @@ -0,0 +1,15 @@ +package wellknown + +import "go.sebtobie.de/httpserver" + +// Finger is an Simple Type for site to transport the information about the webfinger to the middleware +type Finger struct { + Webfinger Webfinger + Protocol string +} + +// FingerSite is an type of site that provide content and want to publish it with webfinger +type FingerSite interface { + httpserver.Site + RegisterFingers() []*Finger +} diff --git a/middleware/wellknown/wellknown.go b/middleware/wellknown/wellknown.go new file mode 100644 index 0000000..dc728cd --- /dev/null +++ b/middleware/wellknown/wellknown.go @@ -0,0 +1,130 @@ +package wellknown + +import ( + "encoding/json" + "net/url" + + "github.com/gin-gonic/gin" + "github.com/rs/zerolog/log" + "go.sebtobie.de/httpserver" + "go.sebtobie.de/httpserver/middleware" +) + +var _ middleware.Middleware = &Middleware{} +var _ httpserver.Site = &Middleware{} + +type ( + + // Link is the reference where the application can find the object + Link struct { + Rel string + Type string + Href *url.URL + } + + // Middleware is an middleware for sites to register themselfs to provide wellknown urls + Middleware struct { + // webfinger is an map consisting of protocol as key and an array of WebfingerAccount as value + webfinger map[string][]Webfinger + } + + // Webfinger is an function that accepts the account part(after ?resource=:) and returns the aliases and the links. + // The bool value if for faster detection if the account was found. + Webfinger func(resource string) ([]string, []*Link, bool) +) + +// New creates a new initialized Middleware +func New() *Middleware { + return &Middleware{ + webfinger: map[string][]Webfinger{}, + } +} + +// Defaults returns the default config values for the middleware +func (*Middleware) Defaults() middleware.Config { + return nil +} + +// Gin does nothing. +func (*Middleware) Gin(c *gin.Context) { + c.Next() +} + +// Setup does nothing +func (*Middleware) Setup(middleware.Config) {} + +// Teardown does nothing +func (*Middleware) Teardown() {} + +// Sites goes through all sites and collects all registrations for wellknown uris +func (m *Middleware) Sites(s []any) error { + for _, site := range s { + if wfs, ok := site.(FingerSite); ok { + for _, finger := range wfs.RegisterFingers() { + if _, ok := m.webfinger[finger.Protocol]; !ok { + m.webfinger[finger.Protocol] = []Webfinger{} + } + m.webfinger[finger.Protocol] = append(m.webfinger[finger.Protocol], finger.Webfinger) + } + } + } + return nil +} + +// Init initializes the routergroup. It must always run on / +func (m *Middleware) Init(rg *gin.RouterGroup) { + if len(m.webfinger) > 0 { + rg.GET(".well-known/webfinger", m.webfingerhf) + } +} + +func (m *Middleware) webfingerhf(c *gin.Context) { + resource := c.Query("resource") + if resource == "" { + c.AbortWithStatus(404) + return + } + uri, err := url.Parse(resource) + if err != nil { + log.Error().Err(err).Str("uri", resource).Msg("Failed to parse uri") + c.AbortWithStatus(404) + return + } + var wf []Webfinger + var found bool + if wf, found = m.webfinger[uri.Scheme]; !found { + c.AbortWithStatus(404) + return + } + var output = struct { + Subject string `json:"subject,omitempty"` + Aliases []string `json:"aliases,omitempty"` + Links []*Link `json:"links,omitempty"` + }{Subject: resource, Aliases: []string{}, Links: []*Link{}} + var aliases = []string{} + var links = []*Link{} + var input = uri.Opaque + if uri.Opaque == "" { + input = resource + } + outputok := false + for _, finger := range wf { + aliases, links, found = finger(input) + if !found { + continue + } + output.Aliases = append(output.Aliases, aliases...) + output.Links = append(output.Links, links...) + outputok = true + } + if outputok { + c.Header("Access-Control-Allow-Origin", "*") + json, err := json.Marshal(output) + if err != nil { + c.AbortWithStatus(500) + return + } + c.Data(200, "application/jrd+json", json) + } + c.AbortWithStatus(404) +}