[feature] Discover webfinger through host-meta (#1588)

* [feature] Discover webfinger through host-meta

This implements a fallback for discovering the webfinger endpoint in
case the /.well-known/webfinger endpoint wasn't properly redirected.
Some instances do this because the recommendation used to be to use
host-meta for the webfinger redirect in the before times.

Closes #1558.

* [bug] Ensure we only ever update cache on success

* [chore] Move finger tests to their own place

This adds a test suite for transport and moves the finger cache tests
into there instead of abusing the search test suite.

* [chore] cleanup the test a bit more

We don't really need a separate function for the oddly located webfinger
response as we check the full URL string anyway

* Address review comments

* [chore] update config example

* [chore] access DB only through state in controller
This commit is contained in:
Daenney 2023-03-08 13:57:41 +01:00 committed by GitHub
commit e397272fe8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 563 additions and 30 deletions

View file

@ -21,6 +21,7 @@ package testrig
import (
"bytes"
"encoding/json"
"encoding/xml"
"io"
"net/http"
"strings"
@ -52,7 +53,7 @@ const (
// PER TEST rather than per suite, so that the do function can be set on a test by test (or even more granular)
// basis.
func NewTestTransportController(state *state.State, client pub.HttpClient) transport.Controller {
return transport.NewController(state.DB, NewTestFederatingDB(state), &federation.Clock{}, client)
return transport.NewController(state, NewTestFederatingDB(state), &federation.Clock{}, client)
}
type MockHTTPClient struct {
@ -121,6 +122,10 @@ func NewMockHTTPClient(do func(req *http.Request) (*http.Response, error), relat
responseContentLength = len(responseBytes)
} else if strings.Contains(req.URL.String(), ".well-known/webfinger") {
responseCode, responseBytes, responseContentType, responseContentLength = WebfingerResponse(req)
} else if strings.Contains(req.URL.String(), ".weird-webfinger-location/webfinger") {
responseCode, responseBytes, responseContentType, responseContentLength = WebfingerResponse(req)
} else if strings.Contains(req.URL.String(), ".well-known/host-meta") {
responseCode, responseBytes, responseContentType, responseContentLength = HostMetaResponse(req)
} else if note, ok := mockHTTPClient.TestRemoteStatuses[req.URL.String()]; ok {
// the request is for a note that we have stored
noteI, err := streams.Serialize(note)
@ -221,11 +226,47 @@ func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) {
return m.do(req)
}
func HostMetaResponse(req *http.Request) (responseCode int, responseBytes []byte, responseContentType string, responseContentLength int) {
var hm *apimodel.HostMeta
if req.URL.String() == "https://misconfigured-instance.com/.well-known/host-meta" {
hm = &apimodel.HostMeta{
XMLNS: "http://docs.oasis-open.org/ns/xri/xrd-1.0",
Link: []apimodel.Link{
{
Rel: "lrdd",
Type: "application/xrd+xml",
Template: "https://misconfigured-instance.com/.weird-webfinger-location/webfinger?resource={uri}",
},
},
}
}
if hm == nil {
log.Debugf(nil, "hostmeta response not available for %s", req.URL)
responseCode = http.StatusNotFound
responseBytes = []byte(``)
responseContentType = "application/xml"
responseContentLength = len(responseBytes)
return
}
hmXML, err := xml.Marshal(hm)
if err != nil {
panic(err)
}
responseCode = http.StatusOK
responseBytes = hmXML
responseContentType = "application/xml"
responseContentLength = len(hmXML)
return
}
func WebfingerResponse(req *http.Request) (responseCode int, responseBytes []byte, responseContentType string, responseContentLength int) {
var wfr *apimodel.WellKnownResponse
switch req.URL.String() {
case "https://unknown-instance.com/.well-known/webfinger?resource=acct:some_group@unknown-instance.com":
case "https://unknown-instance.com/.well-known/webfinger?resource=acct%3Asome_group%40unknown-instance.com":
wfr = &apimodel.WellKnownResponse{
Subject: "acct:some_group@unknown-instance.com",
Links: []apimodel.Link{
@ -236,7 +277,7 @@ func WebfingerResponse(req *http.Request) (responseCode int, responseBytes []byt
},
},
}
case "https://owncast.example.org/.well-known/webfinger?resource=acct:rgh@owncast.example.org":
case "https://owncast.example.org/.well-known/webfinger?resource=acct%3Argh%40owncast.example.org":
wfr = &apimodel.WellKnownResponse{
Subject: "acct:rgh@example.org",
Links: []apimodel.Link{
@ -247,7 +288,7 @@ func WebfingerResponse(req *http.Request) (responseCode int, responseBytes []byt
},
},
}
case "https://unknown-instance.com/.well-known/webfinger?resource=acct:brand_new_person@unknown-instance.com":
case "https://unknown-instance.com/.well-known/webfinger?resource=acct%3Abrand_new_person%40unknown-instance.com":
wfr = &apimodel.WellKnownResponse{
Subject: "acct:brand_new_person@unknown-instance.com",
Links: []apimodel.Link{
@ -258,7 +299,7 @@ func WebfingerResponse(req *http.Request) (responseCode int, responseBytes []byt
},
},
}
case "https://turnip.farm/.well-known/webfinger?resource=acct:turniplover6969@turnip.farm":
case "https://turnip.farm/.well-known/webfinger?resource=acct%3Aturniplover6969%40turnip.farm":
wfr = &apimodel.WellKnownResponse{
Subject: "acct:turniplover6969@turnip.farm",
Links: []apimodel.Link{
@ -269,7 +310,7 @@ func WebfingerResponse(req *http.Request) (responseCode int, responseBytes []byt
},
},
}
case "https://fossbros-anonymous.io/.well-known/webfinger?resource=acct:foss_satan@fossbros-anonymous.io":
case "https://fossbros-anonymous.io/.well-known/webfinger?resource=acct%3Afoss_satan%40fossbros-anonymous.io":
wfr = &apimodel.WellKnownResponse{
Subject: "acct:foss_satan@fossbros-anonymous.io",
Links: []apimodel.Link{
@ -280,7 +321,7 @@ func WebfingerResponse(req *http.Request) (responseCode int, responseBytes []byt
},
},
}
case "https://example.org/.well-known/webfinger?resource=acct:Some_User@example.org":
case "https://example.org/.well-known/webfinger?resource=acct%3ASome_User%40example.org":
wfr = &apimodel.WellKnownResponse{
Subject: "acct:Some_User@example.org",
Links: []apimodel.Link{
@ -291,6 +332,17 @@ func WebfingerResponse(req *http.Request) (responseCode int, responseBytes []byt
},
},
}
case "https://misconfigured-instance.com/.weird-webfinger-location/webfinger?resource=acct%3Asomeone%40misconfigured-instance.com":
wfr = &apimodel.WellKnownResponse{
Subject: "acct:someone@misconfigured-instance.com",
Links: []apimodel.Link{
{
Rel: "self",
Type: applicationActivityJSON,
Href: "https://misconfigured-instance.com/users/someone",
},
},
}
}
if wfr == nil {