Sign actor fetch request
This fixes federation with Mastodon instances that have Authorized Fetch turned on by signing the GET request to fetch the actor when a blog is first followed. Ref T820
This commit is contained in:
parent
1f6d0e2e70
commit
9484880bca
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2018-2020 A Bunch Tell LLC.
|
* Copyright © 2018-2021 A Bunch Tell LLC.
|
||||||
*
|
*
|
||||||
* This file is part of WriteFreely.
|
* This file is part of WriteFreely.
|
||||||
*
|
*
|
||||||
|
@ -17,10 +17,12 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/writeas/writefreely/config"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -41,6 +43,17 @@ const (
|
||||||
apCacheTime = time.Minute
|
apCacheTime = time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var instanceColl *Collection
|
||||||
|
|
||||||
|
func initActivityPub(cfg *config.Config) {
|
||||||
|
ur, _ := url.Parse(cfg.App.Host)
|
||||||
|
instanceColl = &Collection{
|
||||||
|
ID: 0,
|
||||||
|
Alias: ur.Host,
|
||||||
|
Title: ur.Host,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type RemoteUser struct {
|
type RemoteUser struct {
|
||||||
ID int64
|
ID int64
|
||||||
ActorID string
|
ActorID string
|
||||||
|
@ -76,12 +89,17 @@ func handleFetchCollectionActivities(app *App, w http.ResponseWriter, r *http.Re
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
alias := vars["alias"]
|
alias := vars["alias"]
|
||||||
|
if alias == "" {
|
||||||
|
alias = filepath.Base(r.RequestURI)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: enforce visibility
|
// TODO: enforce visibility
|
||||||
// Get base Collection data
|
// Get base Collection data
|
||||||
var c *Collection
|
var c *Collection
|
||||||
var err error
|
var err error
|
||||||
if app.cfg.App.SingleUser {
|
if alias == r.Host {
|
||||||
|
c = instanceColl
|
||||||
|
} else if app.cfg.App.SingleUser {
|
||||||
c, err = app.db.GetCollectionByID(1)
|
c, err = app.db.GetCollectionByID(1)
|
||||||
} else {
|
} else {
|
||||||
c, err = app.db.GetCollection(alias)
|
c, err = app.db.GetCollection(alias)
|
||||||
|
@ -546,6 +564,22 @@ func resolveIRI(hostName, url string) ([]byte, error) {
|
||||||
r.Header.Add("Accept", "application/activity+json")
|
r.Header.Add("Accept", "application/activity+json")
|
||||||
r.Header.Set("User-Agent", ServerUserAgent(hostName))
|
r.Header.Set("User-Agent", ServerUserAgent(hostName))
|
||||||
|
|
||||||
|
p := instanceColl.PersonObject()
|
||||||
|
h := sha256.New()
|
||||||
|
h.Write([]byte{})
|
||||||
|
r.Header.Add("Digest", "SHA-256="+base64.StdEncoding.EncodeToString(h.Sum(nil)))
|
||||||
|
|
||||||
|
// Sign using the 'Signature' header
|
||||||
|
privKey, err := activitypub.DecodePrivateKey(p.GetPrivKey())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
signer := httpsig.NewSigner(p.PublicKey.ID, privKey, httpsig.RSASHA256, []string{"(request-target)", "date", "host", "digest"})
|
||||||
|
err = signer.SignSigHeader(r)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Can't sign: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if debugging {
|
if debugging {
|
||||||
dump, err := httputil.DumpRequestOut(r, true)
|
dump, err := httputil.DumpRequestOut(r, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
8
app.go
8
app.go
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2018-2019 A Bunch Tell LLC.
|
* Copyright © 2018-2021 A Bunch Tell LLC.
|
||||||
*
|
*
|
||||||
* This file is part of WriteFreely.
|
* This file is part of WriteFreely.
|
||||||
*
|
*
|
||||||
|
@ -384,6 +384,8 @@ func Initialize(apper Apper, debug bool) (*App, error) {
|
||||||
|
|
||||||
apper.App().InitDecoder()
|
apper.App().InitDecoder()
|
||||||
|
|
||||||
|
apper.App().InitActivityPub()
|
||||||
|
|
||||||
err = ConnectToDatabase(apper.App())
|
err = ConnectToDatabase(apper.App())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("connect to DB: %s", err)
|
return nil, fmt.Errorf("connect to DB: %s", err)
|
||||||
|
@ -499,6 +501,10 @@ func (app *App) InitDecoder() {
|
||||||
app.formDecoder.RegisterConverter(sql.NullFloat64{}, converter.ConvertSQLNullFloat64)
|
app.formDecoder.RegisterConverter(sql.NullFloat64{}, converter.ConvertSQLNullFloat64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *App) InitActivityPub() {
|
||||||
|
initActivityPub(app.cfg)
|
||||||
|
}
|
||||||
|
|
||||||
// ConnectToDatabase validates and connects to the configured database, then
|
// ConnectToDatabase validates and connects to the configured database, then
|
||||||
// tests the connection.
|
// tests the connection.
|
||||||
func ConnectToDatabase(app *App) error {
|
func ConnectToDatabase(app *App) error {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2018-2019 A Bunch Tell LLC.
|
* Copyright © 2018-2021 A Bunch Tell LLC.
|
||||||
*
|
*
|
||||||
* This file is part of WriteFreely.
|
* This file is part of WriteFreely.
|
||||||
*
|
*
|
||||||
|
@ -12,6 +12,7 @@ package writefreely
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -125,9 +126,13 @@ func InitRoutes(apper Apper, r *mux.Router) *mux.Router {
|
||||||
|
|
||||||
write.HandleFunc("/api/markdown", handler.All(handleRenderMarkdown)).Methods("POST")
|
write.HandleFunc("/api/markdown", handler.All(handleRenderMarkdown)).Methods("POST")
|
||||||
|
|
||||||
|
instanceURL, _ := url.Parse(apper.App().Config().App.Host)
|
||||||
|
host := instanceURL.Host
|
||||||
|
|
||||||
// Handle collections
|
// Handle collections
|
||||||
write.HandleFunc("/api/collections", handler.All(newCollection)).Methods("POST")
|
write.HandleFunc("/api/collections", handler.All(newCollection)).Methods("POST")
|
||||||
apiColls := write.PathPrefix("/api/collections/").Subrouter()
|
apiColls := write.PathPrefix("/api/collections/").Subrouter()
|
||||||
|
apiColls.HandleFunc("/"+host, handler.AllReader(fetchCollection)).Methods("GET")
|
||||||
apiColls.HandleFunc("/{alias:[0-9a-zA-Z\\-]+}", handler.AllReader(fetchCollection)).Methods("GET")
|
apiColls.HandleFunc("/{alias:[0-9a-zA-Z\\-]+}", handler.AllReader(fetchCollection)).Methods("GET")
|
||||||
apiColls.HandleFunc("/{alias:[0-9a-zA-Z\\-]+}", handler.All(existingCollection)).Methods("POST", "DELETE")
|
apiColls.HandleFunc("/{alias:[0-9a-zA-Z\\-]+}", handler.All(existingCollection)).Methods("POST", "DELETE")
|
||||||
apiColls.HandleFunc("/{alias}/posts", handler.AllReader(fetchCollectionPosts)).Methods("GET")
|
apiColls.HandleFunc("/{alias}/posts", handler.AllReader(fetchCollectionPosts)).Methods("GET")
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2018-2020 A Bunch Tell LLC.
|
* Copyright © 2018-2021 A Bunch Tell LLC.
|
||||||
*
|
*
|
||||||
* This file is part of WriteFreely.
|
* This file is part of WriteFreely.
|
||||||
*
|
*
|
||||||
|
@ -32,7 +32,9 @@ var wfUserNotFoundErr = impart.HTTPError{http.StatusNotFound, "User not found."}
|
||||||
func (wfr wfResolver) FindUser(username string, host, requestHost string, r []webfinger.Rel) (*webfinger.Resource, error) {
|
func (wfr wfResolver) FindUser(username string, host, requestHost string, r []webfinger.Rel) (*webfinger.Resource, error) {
|
||||||
var c *Collection
|
var c *Collection
|
||||||
var err error
|
var err error
|
||||||
if wfr.cfg.App.SingleUser {
|
if username == host {
|
||||||
|
c = instanceColl
|
||||||
|
} else if wfr.cfg.App.SingleUser {
|
||||||
c, err = wfr.db.GetCollectionByID(1)
|
c, err = wfr.db.GetCollectionByID(1)
|
||||||
} else {
|
} else {
|
||||||
c, err = wfr.db.GetCollection(username)
|
c, err = wfr.db.GetCollection(username)
|
||||||
|
|
Loading…
Reference in New Issue