mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-12-17 08:33:00 -06:00
domain blocking more work
This commit is contained in:
parent
d27791bc73
commit
99eb3bf564
11 changed files with 160 additions and 58 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
package admin
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
|
@ -59,6 +60,10 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateCreateDomainBlock(form *model.DomainBlockCreateRequest) error {
|
func validateCreateDomainBlock(form *model.DomainBlockCreateRequest) error {
|
||||||
// TODO: add some validation here later if necessary
|
// add some more validation here later if necessary
|
||||||
|
if form.Domain == "" {
|
||||||
|
return errors.New("empty domain provided")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ func getPublicKeyFromResponse(c context.Context, b []byte, keyID *url.URL) (voca
|
||||||
//
|
//
|
||||||
// Also note that this function *does not* dereference the remote account that the signature key is associated with.
|
// Also note that this function *does not* dereference the remote account that the signature key is associated with.
|
||||||
// Other functions should use the returned URL to dereference the remote account, if required.
|
// Other functions should use the returned URL to dereference the remote account, if required.
|
||||||
func (f *federator) AuthenticateFederatedRequest(requestedUsername string, r *http.Request) (*url.URL, error) {
|
func (f *federator) AuthenticateFederatedRequest(requestedUsername string, r *http.Request) (*url.URL, bool, error) {
|
||||||
|
|
||||||
var publicKey interface{}
|
var publicKey interface{}
|
||||||
var pkOwnerURI *url.URL
|
var pkOwnerURI *url.URL
|
||||||
|
|
@ -126,23 +126,24 @@ func (f *federator) AuthenticateFederatedRequest(requestedUsername string, r *ht
|
||||||
|
|
||||||
verifier, err := httpsig.NewVerifier(r)
|
verifier, err := httpsig.NewVerifier(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not create http sig verifier: %s", err)
|
return nil, false, fmt.Errorf("could not create http sig verifier: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The key ID should be given in the signature so that we know where to fetch it from the remote server.
|
// The key ID should be given in the signature so that we know where to fetch it from the remote server.
|
||||||
// This will be something like https://example.org/users/whatever_requesting_user#main-key
|
// This will be something like https://example.org/users/whatever_requesting_user#main-key
|
||||||
requestingPublicKeyID, err := url.Parse(verifier.KeyId())
|
requestingPublicKeyID, err := url.Parse(verifier.KeyId())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not parse key id into a url: %s", err)
|
return nil, false, fmt.Errorf("could not parse key id into a url: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the domain is blocked we want to make as few calls towards it as possible, so already bail here if that's the case!
|
// if the domain is blocked we want to make as few calls towards it as possible, so already bail here if that's the case!
|
||||||
blockedDomain, err := f.blockedDomain(requestingPublicKeyID.Host)
|
blockedDomain, err := f.blockedDomain(requestingPublicKeyID.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not tell if domain %s was blocked or not: %s", requestingPublicKeyID.Host, err)
|
return nil, false, fmt.Errorf("could not tell if domain %s was blocked or not: %s", requestingPublicKeyID.Host, err)
|
||||||
}
|
}
|
||||||
if blockedDomain {
|
if blockedDomain {
|
||||||
return nil, fmt.Errorf("host %s was domain blocked, aborting auth", requestingPublicKeyID.Host)
|
f.log.Infof("domain %s is blocked", requestingPublicKeyID.Host)
|
||||||
|
return nil, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
requestingRemoteAccount := >smodel.Account{}
|
requestingRemoteAccount := >smodel.Account{}
|
||||||
|
|
@ -152,12 +153,12 @@ func (f *federator) AuthenticateFederatedRequest(requestedUsername string, r *ht
|
||||||
// LOCAL ACCOUNT REQUEST
|
// LOCAL ACCOUNT REQUEST
|
||||||
// the request is coming from INSIDE THE HOUSE so skip the remote dereferencing
|
// the request is coming from INSIDE THE HOUSE so skip the remote dereferencing
|
||||||
if err := f.db.GetWhere([]db.Where{{Key: "public_key_uri", Value: requestingPublicKeyID.String()}}, requestingLocalAccount); err != nil {
|
if err := f.db.GetWhere([]db.Where{{Key: "public_key_uri", Value: requestingPublicKeyID.String()}}, requestingLocalAccount); err != nil {
|
||||||
return nil, fmt.Errorf("couldn't get local account with public key uri %s from the database: %s", requestingPublicKeyID.String(), err)
|
return nil, false, fmt.Errorf("couldn't get local account with public key uri %s from the database: %s", requestingPublicKeyID.String(), err)
|
||||||
}
|
}
|
||||||
publicKey = requestingLocalAccount.PublicKey
|
publicKey = requestingLocalAccount.PublicKey
|
||||||
pkOwnerURI, err = url.Parse(requestingLocalAccount.URI)
|
pkOwnerURI, err = url.Parse(requestingLocalAccount.URI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error parsing url %s: %s", requestingLocalAccount.URI, err)
|
return nil, false, fmt.Errorf("error parsing url %s: %s", requestingLocalAccount.URI, err)
|
||||||
}
|
}
|
||||||
} else if err := f.db.GetWhere([]db.Where{{Key: "public_key_uri", Value: requestingPublicKeyID.String()}}, requestingRemoteAccount); err == nil {
|
} else if err := f.db.GetWhere([]db.Where{{Key: "public_key_uri", Value: requestingPublicKeyID.String()}}, requestingRemoteAccount); err == nil {
|
||||||
// REMOTE ACCOUNT REQUEST WITH KEY CACHED LOCALLY
|
// REMOTE ACCOUNT REQUEST WITH KEY CACHED LOCALLY
|
||||||
|
|
@ -165,7 +166,7 @@ func (f *federator) AuthenticateFederatedRequest(requestedUsername string, r *ht
|
||||||
publicKey = requestingRemoteAccount.PublicKey
|
publicKey = requestingRemoteAccount.PublicKey
|
||||||
pkOwnerURI, err = url.Parse(requestingRemoteAccount.URI)
|
pkOwnerURI, err = url.Parse(requestingRemoteAccount.URI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error parsing url %s: %s", requestingRemoteAccount.URI, err)
|
return nil, false, fmt.Errorf("error parsing url %s: %s", requestingRemoteAccount.URI, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// REMOTE ACCOUNT REQUEST WITHOUT KEY CACHED LOCALLY
|
// REMOTE ACCOUNT REQUEST WITHOUT KEY CACHED LOCALLY
|
||||||
|
|
@ -173,72 +174,55 @@ func (f *federator) AuthenticateFederatedRequest(requestedUsername string, r *ht
|
||||||
// so we need to authenticate the request properly by dereferencing the remote key
|
// so we need to authenticate the request properly by dereferencing the remote key
|
||||||
transport, err := f.GetTransportForUser(requestedUsername)
|
transport, err := f.GetTransportForUser(requestedUsername)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("transport err: %s", err)
|
return nil, false, fmt.Errorf("transport err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The actual http call to the remote server is made right here in the Dereference function.
|
// The actual http call to the remote server is made right here in the Dereference function.
|
||||||
b, err := transport.Dereference(context.Background(), requestingPublicKeyID)
|
b, err := transport.Dereference(context.Background(), requestingPublicKeyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error deferencing key %s: %s", requestingPublicKeyID.String(), err)
|
return nil, false, fmt.Errorf("error deferencing key %s: %s", requestingPublicKeyID.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the key isn't in the response, we can't authenticate the request
|
// if the key isn't in the response, we can't authenticate the request
|
||||||
requestingPublicKey, err := getPublicKeyFromResponse(context.Background(), b, requestingPublicKeyID)
|
requestingPublicKey, err := getPublicKeyFromResponse(context.Background(), b, requestingPublicKeyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting key %s from response %s: %s", requestingPublicKeyID.String(), string(b), err)
|
return nil, false, fmt.Errorf("error getting key %s from response %s: %s", requestingPublicKeyID.String(), string(b), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// we should be able to get the actual key embedded in the vocab.W3IDSecurityV1PublicKey
|
// we should be able to get the actual key embedded in the vocab.W3IDSecurityV1PublicKey
|
||||||
pkPemProp := requestingPublicKey.GetW3IDSecurityV1PublicKeyPem()
|
pkPemProp := requestingPublicKey.GetW3IDSecurityV1PublicKeyPem()
|
||||||
if pkPemProp == nil || !pkPemProp.IsXMLSchemaString() {
|
if pkPemProp == nil || !pkPemProp.IsXMLSchemaString() {
|
||||||
return nil, errors.New("publicKeyPem property is not provided or it is not embedded as a value")
|
return nil, false, errors.New("publicKeyPem property is not provided or it is not embedded as a value")
|
||||||
}
|
}
|
||||||
|
|
||||||
// and decode the PEM so that we can parse it as a golang public key
|
// and decode the PEM so that we can parse it as a golang public key
|
||||||
pubKeyPem := pkPemProp.Get()
|
pubKeyPem := pkPemProp.Get()
|
||||||
block, _ := pem.Decode([]byte(pubKeyPem))
|
block, _ := pem.Decode([]byte(pubKeyPem))
|
||||||
if block == nil || block.Type != "PUBLIC KEY" {
|
if block == nil || block.Type != "PUBLIC KEY" {
|
||||||
return nil, errors.New("could not decode publicKeyPem to PUBLIC KEY pem block type")
|
return nil, false, errors.New("could not decode publicKeyPem to PUBLIC KEY pem block type")
|
||||||
}
|
}
|
||||||
|
|
||||||
publicKey, err = x509.ParsePKIXPublicKey(block.Bytes)
|
publicKey, err = x509.ParsePKIXPublicKey(block.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not parse public key from block bytes: %s", err)
|
return nil, false, fmt.Errorf("could not parse public key from block bytes: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// all good! we just need the URI of the key owner to return
|
// all good! we just need the URI of the key owner to return
|
||||||
pkOwnerProp := requestingPublicKey.GetW3IDSecurityV1Owner()
|
pkOwnerProp := requestingPublicKey.GetW3IDSecurityV1Owner()
|
||||||
if pkOwnerProp == nil || !pkOwnerProp.IsIRI() {
|
if pkOwnerProp == nil || !pkOwnerProp.IsIRI() {
|
||||||
return nil, errors.New("publicKeyOwner property is not provided or it is not embedded as a value")
|
return nil, false, errors.New("publicKeyOwner property is not provided or it is not embedded as a value")
|
||||||
}
|
}
|
||||||
pkOwnerURI = pkOwnerProp.GetIRI()
|
pkOwnerURI = pkOwnerProp.GetIRI()
|
||||||
}
|
}
|
||||||
if publicKey == nil {
|
if publicKey == nil {
|
||||||
return nil, errors.New("returned public key was empty")
|
return nil, false, errors.New("returned public key was empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
// do the actual authentication here!
|
// do the actual authentication here!
|
||||||
algo := httpsig.RSA_SHA256 // TODO: make this more robust
|
algo := httpsig.RSA_SHA256 // TODO: make this more robust
|
||||||
if err := verifier.Verify(publicKey, algo); err != nil {
|
if err := verifier.Verify(publicKey, algo); err != nil {
|
||||||
return nil, fmt.Errorf("error verifying key %s: %s", requestingPublicKeyID.String(), err)
|
return nil, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return pkOwnerURI, nil
|
return pkOwnerURI, true, nil
|
||||||
}
|
|
||||||
|
|
||||||
func (f *federator) blockedDomain(host string) (bool, error) {
|
|
||||||
b := >smodel.DomainBlock{}
|
|
||||||
err := f.db.GetWhere([]db.Where{{Key: "domain", Value: host, CaseInsensitive: true}}, b)
|
|
||||||
if err == nil {
|
|
||||||
// block exists
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := err.(db.ErrNoEntries); ok {
|
|
||||||
// there are no entries so there's no block
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// there's an actual error
|
|
||||||
return false, err
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,10 @@ func (f *federator) DereferenceRemoteAccount(username string, remoteAccountID *u
|
||||||
f.startHandshake(username, remoteAccountID)
|
f.startHandshake(username, remoteAccountID)
|
||||||
defer f.stopHandshake(username, remoteAccountID)
|
defer f.stopHandshake(username, remoteAccountID)
|
||||||
|
|
||||||
|
if blocked, err := f.blockedDomain(remoteAccountID.Host); blocked || err != nil {
|
||||||
|
return nil, fmt.Errorf("DereferenceRemoteAccount: domain %s is blocked", remoteAccountID.Host)
|
||||||
|
}
|
||||||
|
|
||||||
transport, err := f.GetTransportForUser(username)
|
transport, err := f.GetTransportForUser(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("transport err: %s", err)
|
return nil, fmt.Errorf("transport err: %s", err)
|
||||||
|
|
@ -66,6 +70,10 @@ func (f *federator) DereferenceRemoteAccount(username string, remoteAccountID *u
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *federator) DereferenceRemoteStatus(username string, remoteStatusID *url.URL) (typeutils.Statusable, error) {
|
func (f *federator) DereferenceRemoteStatus(username string, remoteStatusID *url.URL) (typeutils.Statusable, error) {
|
||||||
|
if blocked, err := f.blockedDomain(remoteStatusID.Host); blocked || err != nil {
|
||||||
|
return nil, fmt.Errorf("DereferenceRemoteStatus: domain %s is blocked", remoteStatusID.Host)
|
||||||
|
}
|
||||||
|
|
||||||
transport, err := f.GetTransportForUser(username)
|
transport, err := f.GetTransportForUser(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("transport err: %s", err)
|
return nil, fmt.Errorf("transport err: %s", err)
|
||||||
|
|
@ -148,6 +156,10 @@ func (f *federator) DereferenceRemoteStatus(username string, remoteStatusID *url
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *federator) DereferenceRemoteInstance(username string, remoteInstanceURI *url.URL) (*gtsmodel.Instance, error) {
|
func (f *federator) DereferenceRemoteInstance(username string, remoteInstanceURI *url.URL) (*gtsmodel.Instance, error) {
|
||||||
|
if blocked, err := f.blockedDomain(remoteInstanceURI.Host); blocked || err != nil {
|
||||||
|
return nil, fmt.Errorf("DereferenceRemoteInstance: domain %s is blocked", remoteInstanceURI.Host)
|
||||||
|
}
|
||||||
|
|
||||||
transport, err := f.GetTransportForUser(username)
|
transport, err := f.GetTransportForUser(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("transport err: %s", err)
|
return nil, fmt.Errorf("transport err: %s", err)
|
||||||
|
|
@ -186,6 +198,14 @@ func (f *federator) DereferenceStatusFields(status *gtsmodel.Status, requestingU
|
||||||
})
|
})
|
||||||
l.Debug("entering function")
|
l.Debug("entering function")
|
||||||
|
|
||||||
|
statusURI, err := url.Parse(status.URI)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("DereferenceStatusFields: couldn't parse status URI %s: %s", status.URI, err)
|
||||||
|
}
|
||||||
|
if blocked, err := f.blockedDomain(statusURI.Host); blocked || err != nil {
|
||||||
|
return fmt.Errorf("DereferenceStatusFields: domain %s is blocked", statusURI.Host)
|
||||||
|
}
|
||||||
|
|
||||||
t, err := f.GetTransportForUser(requestingUsername)
|
t, err := f.GetTransportForUser(requestingUsername)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating transport: %s", err)
|
return fmt.Errorf("error creating transport: %s", err)
|
||||||
|
|
@ -321,6 +341,14 @@ func (f *federator) DereferenceAccountFields(account *gtsmodel.Account, requesti
|
||||||
"requestingUsername": requestingUsername,
|
"requestingUsername": requestingUsername,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
accountURI, err := url.Parse(account.URI)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("DereferenceAccountFields: couldn't parse account URI %s: %s", account.URI, err)
|
||||||
|
}
|
||||||
|
if blocked, err := f.blockedDomain(accountURI.Host); blocked || err != nil {
|
||||||
|
return fmt.Errorf("DereferenceAccountFields: domain %s is blocked", accountURI.Host)
|
||||||
|
}
|
||||||
|
|
||||||
t, err := f.GetTransportForUser(requestingUsername)
|
t, err := f.GetTransportForUser(requestingUsername)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error getting transport for user: %s", err)
|
return fmt.Errorf("error getting transport for user: %s", err)
|
||||||
|
|
@ -342,12 +370,20 @@ func (f *federator) DereferenceAccountFields(account *gtsmodel.Account, requesti
|
||||||
func (f *federator) DereferenceAnnounce(announce *gtsmodel.Status, requestingUsername string) error {
|
func (f *federator) DereferenceAnnounce(announce *gtsmodel.Status, requestingUsername string) error {
|
||||||
if announce.GTSBoostedStatus == nil || announce.GTSBoostedStatus.URI == "" {
|
if announce.GTSBoostedStatus == nil || announce.GTSBoostedStatus.URI == "" {
|
||||||
// we can't do anything unfortunately
|
// we can't do anything unfortunately
|
||||||
return errors.New("dereferenceAnnounce: no URI to dereference")
|
return errors.New("DereferenceAnnounce: no URI to dereference")
|
||||||
|
}
|
||||||
|
|
||||||
|
boostedStatusURI, err := url.Parse(announce.GTSBoostedStatus.URI)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("DereferenceAnnounce: couldn't parse boosted status URI %s: %s", announce.GTSBoostedStatus.URI, err)
|
||||||
|
}
|
||||||
|
if blocked, err := f.blockedDomain(boostedStatusURI.Host); blocked || err != nil {
|
||||||
|
return fmt.Errorf("DereferenceAnnounce: domain %s is blocked", boostedStatusURI.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we already have the boosted status in the database
|
// check if we already have the boosted status in the database
|
||||||
boostedStatus := >smodel.Status{}
|
boostedStatus := >smodel.Status{}
|
||||||
err := f.db.GetWhere([]db.Where{{Key: "uri", Value: announce.GTSBoostedStatus.URI}}, boostedStatus)
|
err = f.db.GetWhere([]db.Where{{Key: "uri", Value: announce.GTSBoostedStatus.URI}}, boostedStatus)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// nice, we already have it so we don't actually need to dereference it from remote
|
// nice, we already have it so we don't actually need to dereference it from remote
|
||||||
announce.Content = boostedStatus.Content
|
announce.Content = boostedStatus.Content
|
||||||
|
|
@ -364,12 +400,7 @@ func (f *federator) DereferenceAnnounce(announce *gtsmodel.Status, requestingUse
|
||||||
}
|
}
|
||||||
|
|
||||||
// we don't have it so we need to dereference it
|
// we don't have it so we need to dereference it
|
||||||
remoteStatusURI, err := url.Parse(announce.GTSBoostedStatus.URI)
|
statusable, err := f.DereferenceRemoteStatus(requestingUsername, boostedStatusURI)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("dereferenceAnnounce: error parsing url %s: %s", announce.GTSBoostedStatus.URI, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
statusable, err := f.DereferenceRemoteStatus(requestingUsername, remoteStatusURI)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("dereferenceAnnounce: error dereferencing remote status with id %s: %s", announce.GTSBoostedStatus.URI, err)
|
return fmt.Errorf("dereferenceAnnounce: error dereferencing remote status with id %s: %s", announce.GTSBoostedStatus.URI, err)
|
||||||
}
|
}
|
||||||
|
|
@ -460,6 +491,14 @@ func (f *federator) DereferenceAnnounce(announce *gtsmodel.Status, requestingUse
|
||||||
// SIDE EFFECTS: remote header and avatar will be stored in local storage, and the database will be updated
|
// SIDE EFFECTS: remote header and avatar will be stored in local storage, and the database will be updated
|
||||||
// to reflect the creation of these new attachments.
|
// to reflect the creation of these new attachments.
|
||||||
func (f *federator) fetchHeaderAndAviForAccount(targetAccount *gtsmodel.Account, t transport.Transport, refresh bool) error {
|
func (f *federator) fetchHeaderAndAviForAccount(targetAccount *gtsmodel.Account, t transport.Transport, refresh bool) error {
|
||||||
|
accountURI, err := url.Parse(targetAccount.URI)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("fetchHeaderAndAviForAccount: couldn't parse account URI %s: %s", targetAccount.URI, err)
|
||||||
|
}
|
||||||
|
if blocked, err := f.blockedDomain(accountURI.Host); blocked || err != nil {
|
||||||
|
return fmt.Errorf("fetchHeaderAndAviForAccount: domain %s is blocked", accountURI.Host)
|
||||||
|
}
|
||||||
|
|
||||||
if targetAccount.AvatarRemoteURL != "" && (targetAccount.AvatarMediaAttachmentID == "" || refresh) {
|
if targetAccount.AvatarRemoteURL != "" && (targetAccount.AvatarMediaAttachmentID == "" || refresh) {
|
||||||
a, err := f.mediaHandler.ProcessRemoteHeaderOrAvatar(t, >smodel.MediaAttachment{
|
a, err := f.mediaHandler.ProcessRemoteHeaderOrAvatar(t, >smodel.MediaAttachment{
|
||||||
RemoteURL: targetAccount.AvatarRemoteURL,
|
RemoteURL: targetAccount.AvatarRemoteURL,
|
||||||
|
|
|
||||||
|
|
@ -119,10 +119,15 @@ func (f *federator) AuthenticatePostInbox(ctx context.Context, w http.ResponseWr
|
||||||
return nil, false, fmt.Errorf("could not fetch requested account with username %s: %s", username, err)
|
return nil, false, fmt.Errorf("could not fetch requested account with username %s: %s", username, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
publicKeyOwnerURI, err := f.AuthenticateFederatedRequest(requestedAccount.Username, r)
|
publicKeyOwnerURI, authenticated, err := f.AuthenticateFederatedRequest(requestedAccount.Username, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("request not authenticated: %s", err)
|
l.Debugf("request not authenticated: %s", err)
|
||||||
return ctx, false, fmt.Errorf("not authenticated: %s", err)
|
return ctx, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !authenticated {
|
||||||
|
w.WriteHeader(http.StatusForbidden)
|
||||||
|
return ctx, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// authentication has passed, so add an instance entry for this instance if it hasn't been done already
|
// authentication has passed, so add an instance entry for this instance if it hasn't been done already
|
||||||
|
|
@ -230,6 +235,14 @@ func (f *federator) Blocked(ctx context.Context, actorIRIs []*url.URL) (bool, er
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, uri := range actorIRIs {
|
for _, uri := range actorIRIs {
|
||||||
|
blockedDomain, err := f.blockedDomain(uri.Host);
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("error checking domain block: %s", err)
|
||||||
|
}
|
||||||
|
if blockedDomain {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
a := >smodel.Account{}
|
a := >smodel.Account{}
|
||||||
if err := f.db.GetWhere([]db.Where{{Key: "uri", Value: uri.String()}}, a); err != nil {
|
if err := f.db.GetWhere([]db.Where{{Key: "uri", Value: uri.String()}}, a); err != nil {
|
||||||
_, ok := err.(db.ErrNoEntries)
|
_, ok := err.(db.ErrNoEntries)
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,13 @@ type Federator interface {
|
||||||
FederatingDB() federatingdb.DB
|
FederatingDB() federatingdb.DB
|
||||||
// AuthenticateFederatedRequest can be used to check the authenticity of incoming http-signed requests for federating resources.
|
// AuthenticateFederatedRequest can be used to check the authenticity of incoming http-signed requests for federating resources.
|
||||||
// The given username will be used to create a transport for making outgoing requests. See the implementation for more detailed comments.
|
// The given username will be used to create a transport for making outgoing requests. See the implementation for more detailed comments.
|
||||||
AuthenticateFederatedRequest(username string, r *http.Request) (*url.URL, error)
|
//
|
||||||
|
// If the request is valid and passes authentication, the URL of the key owner ID will be returned, as well as true, and nil.
|
||||||
|
//
|
||||||
|
// If the request does not pass authentication, or there's a domain block, nil, false, nil will be returned.
|
||||||
|
//
|
||||||
|
// If something goes wrong during authentication, nil, false, and an error will be returned.
|
||||||
|
AuthenticateFederatedRequest(username string, r *http.Request) (*url.URL, bool, error)
|
||||||
// FingerRemoteAccount performs a webfinger lookup for a remote account, using the .well-known path. It will return the ActivityPub URI for that
|
// FingerRemoteAccount performs a webfinger lookup for a remote account, using the .well-known path. It will return the ActivityPub URI for that
|
||||||
// account, or an error if it doesn't exist or can't be retrieved.
|
// account, or an error if it doesn't exist or can't be retrieved.
|
||||||
FingerRemoteAccount(requestingUsername string, targetUsername string, targetDomain string) (*url.URL, error)
|
FingerRemoteAccount(requestingUsername string, targetUsername string, targetDomain string) (*url.URL, error)
|
||||||
|
|
@ -97,6 +103,7 @@ func NewFederator(db db.DB, federatingDB federatingdb.DB, transportController tr
|
||||||
clock: &Clock{},
|
clock: &Clock{},
|
||||||
typeConverter: typeConverter,
|
typeConverter: typeConverter,
|
||||||
transportController: transportController,
|
transportController: transportController,
|
||||||
|
mediaHandler: mediaHandler,
|
||||||
log: log,
|
log: log,
|
||||||
handshakeSync: &sync.Mutex{},
|
handshakeSync: &sync.Mutex{},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (f *federator) FingerRemoteAccount(requestingUsername string, targetUsername string, targetDomain string) (*url.URL, error) {
|
func (f *federator) FingerRemoteAccount(requestingUsername string, targetUsername string, targetDomain string) (*url.URL, error) {
|
||||||
|
if blocked, err := f.blockedDomain(targetDomain); blocked || err != nil {
|
||||||
|
return nil, fmt.Errorf("FingerRemoteAccount: domain %s is blocked", targetDomain)
|
||||||
|
}
|
||||||
|
|
||||||
t, err := f.GetTransportForUser(requestingUsername)
|
t, err := f.GetTransportForUser(requestingUsername)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
23
internal/federation/util.go
Normal file
23
internal/federation/util.go
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
package federation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *federator) blockedDomain(host string) (bool, error) {
|
||||||
|
b := >smodel.DomainBlock{}
|
||||||
|
err := f.db.GetWhere([]db.Where{{Key: "domain", Value: host, CaseInsensitive: true}}, b)
|
||||||
|
if err == nil {
|
||||||
|
// block exists
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := err.(db.ErrNoEntries); ok {
|
||||||
|
// there are no entries so there's no block
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// there's an actual error
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
@ -35,7 +35,7 @@ func (p *processor) DomainBlockCreate(account *gtsmodel.Account, form *apimodel.
|
||||||
domainBlock := >smodel.DomainBlock{}
|
domainBlock := >smodel.DomainBlock{}
|
||||||
err := p.db.GetWhere([]db.Where{{Key: "domain", Value: form.Domain, CaseInsensitive: true}}, domainBlock)
|
err := p.db.GetWhere([]db.Where{{Key: "domain", Value: form.Domain, CaseInsensitive: true}}, domainBlock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(db.ErrNoEntries); ok {
|
if _, ok := err.(db.ErrNoEntries); !ok {
|
||||||
// something went wrong in the DB
|
// something went wrong in the DB
|
||||||
return nil, gtserror.NewErrorInternalError(fmt.Errorf("DomainBlockCreate: db error checking for existence of domain block %s: %s", form.Domain, err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("DomainBlockCreate: db error checking for existence of domain block %s: %s", form.Domain, err))
|
||||||
}
|
}
|
||||||
|
|
@ -130,6 +130,7 @@ selectAccountsLoop:
|
||||||
}
|
}
|
||||||
// an actual error has occurred
|
// an actual error has occurred
|
||||||
l.Errorf("domainBlockProcessSideEffects: db error selecting accounts for domain %s: %s", block.Domain, err)
|
l.Errorf("domainBlockProcessSideEffects: db error selecting accounts for domain %s: %s", block.Domain, err)
|
||||||
|
break selectAccountsLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, a := range accounts {
|
for i, a := range accounts {
|
||||||
|
|
|
||||||
|
|
@ -106,8 +106,8 @@ func (p *processor) GetFediUser(requestedUsername string, request *http.Request)
|
||||||
}
|
}
|
||||||
} else if util.IsUserPath(request.URL) {
|
} else if util.IsUserPath(request.URL) {
|
||||||
// if it's a user path, we want to fully authenticate the request before we serve any data, and then we can serve a more complete profile
|
// if it's a user path, we want to fully authenticate the request before we serve any data, and then we can serve a more complete profile
|
||||||
requestingAccountURI, err := p.federator.AuthenticateFederatedRequest(requestedUsername, request)
|
requestingAccountURI, authenticated, err := p.federator.AuthenticateFederatedRequest(requestedUsername, request)
|
||||||
if err != nil {
|
if err != nil || !authenticated {
|
||||||
return nil, gtserror.NewErrorNotAuthorized(err)
|
return nil, gtserror.NewErrorNotAuthorized(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,8 +152,8 @@ func (p *processor) GetFediFollowers(requestedUsername string, request *http.Req
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate the request
|
// authenticate the request
|
||||||
requestingAccountURI, err := p.federator.AuthenticateFederatedRequest(requestedUsername, request)
|
requestingAccountURI, authenticated, err := p.federator.AuthenticateFederatedRequest(requestedUsername, request)
|
||||||
if err != nil {
|
if err != nil || !authenticated {
|
||||||
return nil, gtserror.NewErrorNotAuthorized(err)
|
return nil, gtserror.NewErrorNotAuthorized(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,8 +197,8 @@ func (p *processor) GetFediFollowing(requestedUsername string, request *http.Req
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate the request
|
// authenticate the request
|
||||||
requestingAccountURI, err := p.federator.AuthenticateFederatedRequest(requestedUsername, request)
|
requestingAccountURI, authenticated, err := p.federator.AuthenticateFederatedRequest(requestedUsername, request)
|
||||||
if err != nil {
|
if err != nil || !authenticated {
|
||||||
return nil, gtserror.NewErrorNotAuthorized(err)
|
return nil, gtserror.NewErrorNotAuthorized(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -242,8 +242,8 @@ func (p *processor) GetFediStatus(requestedUsername string, requestedStatusID st
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate the request
|
// authenticate the request
|
||||||
requestingAccountURI, err := p.federator.AuthenticateFederatedRequest(requestedUsername, request)
|
requestingAccountURI, authenticated, err := p.federator.AuthenticateFederatedRequest(requestedUsername, request)
|
||||||
if err != nil {
|
if err != nil || !authenticated {
|
||||||
return nil, gtserror.NewErrorNotAuthorized(err)
|
return nil, gtserror.NewErrorNotAuthorized(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -356,6 +356,5 @@ func (p *processor) GetNodeInfo(request *http.Request) (*apimodel.Nodeinfo, gtse
|
||||||
|
|
||||||
func (p *processor) InboxPost(ctx context.Context, w http.ResponseWriter, r *http.Request) (bool, error) {
|
func (p *processor) InboxPost(ctx context.Context, w http.ResponseWriter, r *http.Request) (bool, error) {
|
||||||
contextWithChannel := context.WithValue(ctx, util.APFromFederatorChanKey, p.fromFederator)
|
contextWithChannel := context.WithValue(ctx, util.APFromFederatorChanKey, p.fromFederator)
|
||||||
posted, err := p.federator.FederatingActor().PostInbox(contextWithChannel, w, r)
|
return p.federator.FederatingActor().PostInbox(contextWithChannel, w, r)
|
||||||
return posted, err
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package visibility
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
|
@ -16,6 +17,15 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount
|
||||||
"statusID": targetStatus.ID,
|
"statusID": targetStatus.ID,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
uri, err := url.Parse(targetStatus.URI)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("StatusVisible: error parsing uri: %s", targetStatus.URI)
|
||||||
|
}
|
||||||
|
if blocked, err := f.blockedDomain(uri.Host); blocked || err != nil {
|
||||||
|
l.Debugf("domain %s is blocked", uri.Host)
|
||||||
|
return blocked, err
|
||||||
|
}
|
||||||
|
aaaaaaaaaa
|
||||||
relevantAccounts, err := f.pullRelevantAccountsFromStatus(targetStatus)
|
relevantAccounts, err := f.pullRelevantAccountsFromStatus(targetStatus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error pulling relevant accounts for status %s: %s", targetStatus.ID, err)
|
l.Debugf("error pulling relevant accounts for status %s: %s", targetStatus.ID, err)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package visibility
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -79,3 +80,20 @@ type relevantAccounts struct {
|
||||||
BoostedReplyToAccount *gtsmodel.Account
|
BoostedReplyToAccount *gtsmodel.Account
|
||||||
MentionedAccounts []*gtsmodel.Account
|
MentionedAccounts []*gtsmodel.Account
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *filter) blockedDomain(host string) (bool, error) {
|
||||||
|
b := >smodel.DomainBlock{}
|
||||||
|
err := f.db.GetWhere([]db.Where{{Key: "domain", Value: host, CaseInsensitive: true}}, b)
|
||||||
|
if err == nil {
|
||||||
|
// block exists
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := err.(db.ErrNoEntries); ok {
|
||||||
|
// there are no entries so there's no block
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// there's an actual error
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue