package wellknown import ( "net/url" "github.com/gin-gonic/gin" "github.com/go-ap/jsonld" "github.com/rs/zerolog/log" "go.sebtobie.de/httpserver" "go.sebtobie.de/httpserver/middleware" ) var _ middleware.Middleware = &Middleware{} var _ middleware.PreSetupMiddleware = &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 nothing 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() {} // PreSetup goes through all sites and collects all registrations for wellknown uris func (m *Middleware) PreSetup(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 := jsonld.Marshal(output) if err != nil { c.AbortWithStatus(500) return } c.Data(200, jsonld.ContentType, json) } c.AbortWithStatus(404) }