2022-11-06 09:53:38 +00:00
|
|
|
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=<protocol>:) 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{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-06 10:05:16 +00:00
|
|
|
// Defaults returns nothing
|
2022-11-06 09:53:38 +00:00
|
|
|
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)
|
|
|
|
}
|