Social features / ActivityPub federation (#1629)

* Support webfinger requests for the live account. Closes https://github.com/owncast/owncast/issues/1193

* Support for actor requests. Returns response for live actor. Closes https://github.com/owncast/owncast/issues/1203

* Handle follow and unfollow requests. Closes
https://github.com/owncast/owncast/issues/1191 and https://github.com/owncast/owncast/issues/1205 and https://github.com/owncast/owncast/issues/1206 and https://github.com/owncast/owncast/issues/1194

* Add basic support for sending out text activities. For https://github.com/owncast/owncast/issues/1192

* Some error handling and passing of dynamic local account names.

* Add hardcoded example image attachment to test post

* Centralize the map of accounts and inboxes

* No longer disable the preview generator based on YP toggle

* Send a federated message to followers when stream starts. For https://github.com/owncast/owncast/issues/1192

* Placeholder for attaching tags

* Add image description

* Save and get to outbox persistence. Return using outbox endpoint for actor

* Pass payloads to be handled through the gochan

* Handle undo follow requests explitly, not all undo requests

* Add API for manually sending simple federated messages. Closes #1215

* Verify inbox requests. Closes #1321

* Add route to fetch a single AP object by ID. For #1329

* Add responses to fediverse nodeinfo requests

* Set and get federation config values for admin

* Handle host-meta requests

* Do not send out message if disabled. Use saved go live message.

* Require AP-compatible content types for AP-related requests

* Rename ap models to apmodels for clarity

* Change how content type matching takes place.

* io -> ioutil

* Add stub delete activity callback

* Handle likes and announces to surface engagement in chat. Part of #1229

* Append url to go live posts

* Do not require specific content types for nodeinfo requests

* Add follow engagement chat message via AP

* add owncast user-agent to requests

* Set note visibility to public (for now)

* Fix saving/fetching a single object

* Add support for x-nodeinfo2 responses

* Point to the dev admin branch for ap

* Bundle in dev admin for testing

* Add error logging

* Add AP middleware back

* Point to the new external compatible logo endpoint

* Clean up more AP logging to help testing

* Tweak go live text and link hashtags

* Fix bug in fetching init time

* Send update actor activities when server details/profile is updated

* Add federation config overview to web client config

* Add additional actor properties

* Make the AP middleware checking more flexible when looking at types

* First pass at remote fediverse follow flow. For #1371

* Added a basic AP actor followers endpoint

* WIP client followers API

* Add profile-page reference to webfinger response

* Add aliases to webfinger response

* Fix content-type returned to be expected activitypub+json

* First pass at followers api

* Point at local dev copy of go-fed/activity

* Add custom toot Hashtag objects to posts

* Store additional user details to followers table

* Fix AP followers endpoint. Closes #1204

* Add owncast hashtag as an invisible tag to go live posts

* Reject AP requests when it is disabled

* Add actor util for generating full account user from person object

* Verify inbox requests before performing any other work

* Accept actor update requests

* Fix linter errors in federation branch

* Migrate AP SQL to sqlc for type safe queries

* Use the @unclearParadigm REST parameter helper

* Fix verifying post ID on AP engagement

* WIP privacy/request approval

* Style the remote follow modal

* First pass at a followers list component w/ mock data. #1370

* Revert "Use the @unclearParadigm REST parameter helper"

This reverts commit c8af8a413f6f53e7d1a15a7d823ff28be2db3c23.

* Fix get followers API

* Add support for requiring approval. Closes https://github.com/owncast/owncast/issues/1208

* Handle Applications as Actors partly for PeerTube support

* add temp todo list

* check route on load, this might change later

* style followers

* account for just 1 tab case

* Remove mock data. Allow showing follow button even when there are no external actions defined

* Point to actual followers API

* Support fallback img for follower views

* Remove duplicate verification. Add some additional verbose logging

* Bundle dev admin

* Add type to host-meta webfinger template response

* Tweak remote follow modal content

* WIP federation followers refactor

* Do not send pointer to middleware

* Update admin

* Add setting for toggling displaying fediverse engagement. Closes #1404

* Add in-development admin

* Do not enable cors on admin followers api

* Add db migration for updating messages table

* Enable empty string go live messages to disable

* Remove debug messages

* Rework some ActivityPub handling.

Create new Actor->Person handling.
Create new Actor->Service handling.
Add engagement handlers to send chat events and store event objects.
Store inbound activities to new ap_inbound_activities table.

* Support federated engagement events.

Store them in the messages table and surface them via chat events.

* Support federated event engatement in the chat

* Tweak web UI followers handling

* Point go.mod at remote fork instead of local

* Update admin

* Merged in develop. Couple fixes

* Update dev admin

* Update fedi engagement posts.

- Fix incorrect action text.
- Add action icons.

* Set public as to instead of cc for ap msg

* Updated styling for federated actions in chat

* Add support for blocking federated domains. Closes #1209

* Force checking of https in verify step

* Update dev admin

* Return user scopes in chat history api. Closes #1586

* Update dev admin

* Add AP outbound request worker pool. Closes #1571

* Disable (temporarily?) owncast tag on AP posts

* Consolidate creating activity+notes in outbound AP messages

* Add inbox worker pool. Closes #1570

* Update dev admin bundle

* Clean up some logs

* Re-enable inbound verfication

* Save full IRI to outbox instead of path

* Reject if full IRI is not found in outbox

* Use full ActivityPub user account in chat event

* Fix and expand follower APIs

- Add missing IDs to AP follower endpoints
- Split AP follower endpoints into initial request and pages.
- Support pagination in AP requests.

* Include IRI in error message

* Hide chat toggle when chat is hidden. Closes #1606

* Updates to followers pagination

* Set default go live message

* Remove log

* indirect -> direct import

* Updates for inbound federated event handling.

- Keep track of existing events and reject duplicates.
- Change what is sent to chat for surfing federated engagement.
- Keep track if outbound events are automated "go live" events or not.

* Update chat federated engagement.

* Update dev admin.

* Move from being a person to a bot (service). Closes #1619

* Only set server init date if not already set

* Only save notes to outbox able

* Rework private-mode followers/approvals

* API for returning a list of federated actions for #1573

* Fix too-small follower cells and jumpy tabs. Closes #1616 and closes #1516

* Fix shortcuts getting fired on inputs. Fixes #1489 and #1201

* Add spinner, autoclose + other fixes to follow modal. Fixes #1593

* Fix fetching a single object by IRI

* SendFederationMessage -> SendFederatedMessage

* Autolink and create tag objects from manual posts. Closes #1620

* Update dev admin bundle

* Handle engagement from non-automated/live posts

* Reject federated engagement actions if they do not match a local post

* Update dev admin bundle

* A bunch of cleanup

* Fix unused assignments and logic

* Remove unused function

* Add content warning and sentive content flag if stream is NSFW. Closes #1624

* Disable fetching objects by IRI when in private mode. Closes #1623

* Update the error message of the remote follow dialog. closes #1622

* Update dev admin

* Fix NREs throwing in test content

* Fix query that wasn't properly filtering out hidden messages

* Test against user being disabled instead of message visibility

* Fix automated test NRE

* Update comment

* Adjust federated engagement chat views. Closes #1617

* Add additional index to users table

* Add support for removing followers/requests. Closes #1630

* Reject federated actions from blocked actors. #1631

* Use fallback avatar if it fails to load. Closes #1635

* Fix styling of follower list. Closes #1636

* Add basic blurb stating they should follow the server. Closes #1641

* Update dev admin

* Set default go live message in migration. Closes #1642

* Reset the messages table on 0.0.11 schema migration

* Fix js error with moderation actions. Closes #1621

* Add a bit more clarification on follow modal. Closes #1599

* Remove todos

* Split out actor and domain blocking checks

* Check for errors on default values being set

* Clean up actor rejection due to being blocked

* Update dev admin

* Add colon to error to make it easier to read

* Remove markdown rendering of go live message. Reorganize text. Remove content warning. Closes #1645

* Break out the sort+render messages logic so it can be fired on visibility change. Closes #1643

* Do not send profile updates if federation is disabled

* Save follow references to inbound activities table

* Update dev admin

* Add blocked actor test

* Remove the overloaded term of Follow from social links

* Fix test running in memory only

* Remove "just" in engagement messags

* Replace star with heart for like action.

* Update dev admin

* Explicitly set cc as public

* Remove overly using the stream name in fediverse engagement messages

* Some federated/follow UI tweaks

* Remove explicit cc and bcc as they are not required

* Explicitly set the audience

* Remove extra margin

* Add Join Fediverse button to follow modal. Closes #1651

* Do not allow multiple follows to send multiple events. Closes #1650

* Give events a min height

* Do not allow old posts to be liked/shared. Closes #1652

* Remove value from log message

* Alert followers on private mode toggle

* Ignore clicks to follow button if disabled

* Remove underline from action buttons

* Add moderator icon to join message

* Update admin

* Post-merge remove unused var

* Remove pointing at feature branch

Co-authored-by: Ginger Wong <omqmail@gmail.com>
This commit is contained in:
Gabe Kangas
2022-01-12 13:53:10 -08:00
committed by GitHub
parent c51d9cdbf4
commit 045a0a2afd
174 changed files with 7295 additions and 404 deletions

View File

@@ -0,0 +1,40 @@
package inbox
import (
"context"
"time"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/core/chat/events"
"github.com/pkg/errors"
)
func handleAnnounceRequest(c context.Context, activity vocab.ActivityStreamsAnnounce) error {
object := activity.GetActivityStreamsObject()
actorReference := activity.GetActivityStreamsActor()
objectIRI := object.At(0).GetIRI().String()
actorIRI := actorReference.At(0).GetIRI().String()
if hasPreviouslyhandled, err := persistence.HasPreviouslyHandledInboundActivity(objectIRI, actorIRI, events.FediverseEngagementRepost); hasPreviouslyhandled || err != nil {
return errors.Wrap(err, "inbound activity of share/re-post has already been handled")
}
// Shares need to match a post we had already sent.
_, isLiveNotification, timestamp, err := persistence.GetObjectByIRI(objectIRI)
if err != nil {
return errors.Wrap(err, "Could not find post locally")
}
// Don't allow old activities to be liked
if time.Since(timestamp) > maxAgeForEngagement {
return errors.New("Activity is too old to be shared")
}
// Save as an accepted activity
if err := persistence.SaveInboundFediverseActivity(objectIRI, actorIRI, events.FediverseEngagementRepost, time.Now()); err != nil {
return errors.Wrap(err, "unable to save inbound share/re-post activity")
}
return handleEngagementActivity(events.FediverseEngagementRepost, isLiveNotification, actorReference, events.FediverseEngagementRepost)
}

62
activitypub/inbox/chat.go Normal file
View File

@@ -0,0 +1,62 @@
package inbox
import (
"fmt"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
)
func handleEngagementActivity(eventType events.EventType, isLiveNotification bool, actorReference vocab.ActivityStreamsActorProperty, action string) error {
// Do nothing if displaying engagement actions has been turned off.
if !data.GetFederationShowEngagement() {
return nil
}
// Do nothing if chat is disabled
if data.GetChatDisabled() {
return nil
}
// Get actor of the action
actor, _ := resolvers.GetResolvedActorFromActorProperty(actorReference)
// Send chat message
actorName := actor.Name
if actorName == "" {
actorName = actor.Username
}
actorIRI := actorReference.Begin().GetIRI().String()
userPrefix := fmt.Sprintf("%s ", actorName)
var suffix string
if isLiveNotification && action == events.FediverseEngagementLike {
suffix = "liked that this stream went live."
} else if action == events.FediverseEngagementLike {
suffix = fmt.Sprintf("liked a post from %s.", data.GetServerName())
} else if isLiveNotification && action == events.FediverseEngagementRepost {
suffix = "shared this stream with their followers."
} else if action == events.FediverseEngagementRepost {
suffix = fmt.Sprintf("shared a post from %s.", data.GetServerName())
} else if action == events.FediverseEngagementFollow {
suffix = "followed this stream."
} else {
return fmt.Errorf("could not handle event for sending to chat: %s", action)
}
body := fmt.Sprintf("%s %s", userPrefix, suffix)
var image *string
if actor.Image != nil {
s := actor.Image.String()
image = &s
}
if err := chat.SendFediverseAction(eventType, actor.FullUsername, image, body, actorIRI); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,7 @@
package inbox
import "time"
const (
maxAgeForEngagement = time.Hour * 36
)

View File

@@ -0,0 +1,88 @@
package inbox
import (
"context"
"fmt"
"time"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/requests"
"github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsFollow) error {
follow, err := resolvers.MakeFollowRequest(c, activity)
if err != nil {
log.Errorln("unable to create follow inbox request", err)
return err
}
if follow == nil {
return fmt.Errorf("unable to handle request")
}
approved := !data.GetFederationIsPrivate()
followRequest := *follow
if err := persistence.AddFollow(followRequest, approved); err != nil {
log.Errorln("unable to save follow request", err)
return err
}
localAccountName := data.GetDefaultFederationUsername()
if approved {
if err := requests.SendFollowAccept(follow.Inbox, follow.FollowRequestIri, localAccountName); err != nil {
log.Errorln("unable to send follow accept", err)
return err
}
}
// Save as an accepted activity
actorReference := activity.GetActivityStreamsActor()
object := activity.GetActivityStreamsObject()
objectIRI := object.At(0).GetIRI().String()
actorIRI := actorReference.At(0).GetIRI().String()
// If this request is approved and we have not previously sent an action to
// chat due to a previous follow request, then do so.
hasPreviouslyhandled := true // Default so we don't send anything if it fails.
if approved {
hasPreviouslyhandled, err = persistence.HasPreviouslyHandledInboundActivity(objectIRI, actorIRI, events.FediverseEngagementFollow)
if err != nil {
log.Errorln("error checking for previously handled follow activity", err)
}
}
// Save this follow action to our activities table.
if err := persistence.SaveInboundFediverseActivity(objectIRI, actorIRI, events.FediverseEngagementFollow, time.Now()); err != nil {
return errors.Wrap(err, "unable to save inbound share/re-post activity")
}
// Send action to chat if it has not been previously handled.
if !hasPreviouslyhandled {
return handleEngagementActivity(events.FediverseEngagementFollow, false, actorReference, events.FediverseEngagementFollow)
}
return nil
}
func handleUnfollowRequest(c context.Context, activity vocab.ActivityStreamsUndo) error {
request := resolvers.MakeUnFollowRequest(c, activity)
if request == nil {
log.Errorf("unable to handle unfollow request")
return errors.New("unable to handle unfollow request")
}
unfollowRequest := *request
log.Traceln("unfollow request:", unfollowRequest)
return persistence.RemoveFollow(unfollowRequest)
}

40
activitypub/inbox/like.go Normal file
View File

@@ -0,0 +1,40 @@
package inbox
import (
"context"
"time"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/core/chat/events"
"github.com/pkg/errors"
)
func handleLikeRequest(c context.Context, activity vocab.ActivityStreamsLike) error {
object := activity.GetActivityStreamsObject()
actorReference := activity.GetActivityStreamsActor()
objectIRI := object.At(0).GetIRI().String()
actorIRI := actorReference.At(0).GetIRI().String()
if hasPreviouslyhandled, err := persistence.HasPreviouslyHandledInboundActivity(objectIRI, actorIRI, events.FediverseEngagementLike); hasPreviouslyhandled || err != nil {
return errors.Wrap(err, "inbound activity of like has already been handled")
}
// Likes need to match a post we had already sent.
_, isLiveNotification, timestamp, err := persistence.GetObjectByIRI(objectIRI)
if err != nil {
return errors.Wrap(err, "Could not find post locally")
}
// Don't allow old activities to be liked
if time.Since(timestamp) > maxAgeForEngagement {
return errors.New("Activity is too old to be liked")
}
// Save as an accepted activity
if err := persistence.SaveInboundFediverseActivity(objectIRI, actorIRI, events.FediverseEngagementLike, time.Now()); err != nil {
return errors.Wrap(err, "unable to save inbound like activity")
}
return handleEngagementActivity(events.FediverseEngagementLike, isLiveNotification, actorReference, events.FediverseEngagementLike)
}

27
activitypub/inbox/undo.go Normal file
View File

@@ -0,0 +1,27 @@
package inbox
import (
"context"
log "github.com/sirupsen/logrus"
"github.com/go-fed/activity/streams/vocab"
)
func handleUndoInboxRequest(c context.Context, activity vocab.ActivityStreamsUndo) error {
// Determine if this is an undo of a follow, favorite, announce, etc.
o := activity.GetActivityStreamsObject()
for iter := o.Begin(); iter != o.End(); iter = iter.Next() {
if iter.IsActivityStreamsFollow() {
// This is an Unfollow request
if err := handleUnfollowRequest(c, activity); err != nil {
return err
}
} else {
log.Traceln("Undo", iter.GetType().GetTypeName(), "ignored")
return nil
}
}
return nil
}

View File

@@ -0,0 +1,25 @@
package inbox
import (
"context"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/resolvers"
log "github.com/sirupsen/logrus"
)
func handleUpdateRequest(c context.Context, activity vocab.ActivityStreamsUpdate) error {
// We only care about update events to followers.
if !activity.GetActivityStreamsObject().At(0).IsActivityStreamsPerson() {
return nil
}
actor, err := resolvers.GetResolvedActorFromActorProperty(activity.GetActivityStreamsActor())
if err != nil {
log.Errorln(err)
return err
}
return persistence.UpdateFollower(actor.ActorIri.String(), actor.Inbox.String(), actor.Name, actor.FullUsername, actor.Image.String())
}

130
activitypub/inbox/worker.go Normal file
View File

@@ -0,0 +1,130 @@
package inbox
import (
"context"
"crypto/x509"
"encoding/pem"
"net/http"
"net/url"
"strings"
"github.com/pkg/errors"
"github.com/go-fed/httpsig"
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/activitypub/resolvers"
"github.com/owncast/owncast/core/data"
log "github.com/sirupsen/logrus"
)
func handle(request apmodels.InboxRequest) {
if verified, err := Verify(request.Request); err != nil {
log.Debugln("Error in attempting to verify request", err)
return
} else if !verified {
log.Errorln("Request failed verification", err)
return
}
// c := context.WithValue(context.Background(), "account", request.ForLocalAccount) //nolint
if err := resolvers.Resolve(context.Background(), request.Body, handleUpdateRequest, handleFollowInboxRequest, handleLikeRequest, handleAnnounceRequest, handleUndoInboxRequest); err != nil {
log.Errorln("resolver error:", err)
}
}
// Verify will Verify the http signature of an inbound request as well as
// check it against the list of blocked domains.
func Verify(request *http.Request) (bool, error) {
verifier, err := httpsig.NewVerifier(request)
if err != nil {
return false, errors.Wrap(err, "failed to create key verifier for request")
}
pubKeyID, err := url.Parse(verifier.KeyId())
if err != nil {
return false, errors.Wrap(err, "failed to parse key to get key ID")
}
// Force federation only via servers using https.
if pubKeyID.Scheme != "https" {
return false, errors.New("federated servers must use https: " + pubKeyID.String())
}
signature := request.Header.Get("signature")
var algorithmString string
signatureComponents := strings.Split(signature, ",")
for _, component := range signatureComponents {
kv := strings.Split(component, "=")
if kv[0] == "algorithm" {
algorithmString = kv[1]
break
}
}
algorithmString = strings.Trim(algorithmString, "\"")
if algorithmString == "" {
return false, errors.New("Unable to determine algorithm to verify request")
}
actor, err := resolvers.GetResolvedActorFromIRI(pubKeyID.String())
if err != nil {
return false, errors.Wrap(err, "failed to resolve actor from IRI to fetch key")
}
// Test to see if the actor is in the list of blocked federated domains.
if isBlockedDomain(actor.ActorIri.Hostname()) {
return false, errors.New("domain is blocked")
}
// If actor is specifically blocked, then fail validation.
if blocked, err := isBlockedActor(actor.ActorIri); err != nil || blocked {
return false, err
}
key := actor.W3IDSecurityV1PublicKey.Begin().Get().GetW3IDSecurityV1PublicKeyPem().Get()
block, _ := pem.Decode([]byte(key))
if block == nil {
log.Errorln("failed to parse PEM block containing the public key")
return false, errors.New("failed to parse PEM block containing the public key")
}
parsedKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
log.Errorln("failed to parse DER encoded public key: " + err.Error())
return false, errors.Wrap(err, "failed to parse DER encoded public key")
}
var algorithm httpsig.Algorithm = httpsig.Algorithm(algorithmString)
// The verifier will verify the Digest in addition to the HTTP signature
if err := verifier.Verify(parsedKey, algorithm); err != nil {
log.Warnln("verification error for", pubKeyID, err)
return false, errors.Wrap(err, "verification error: "+pubKeyID.String())
}
return true, nil
}
func isBlockedDomain(domain string) bool {
blockedDomains := data.GetBlockedFederatedDomains()
for _, blockedDomain := range blockedDomains {
if strings.Contains(domain, blockedDomain) {
return true
}
}
return false
}
func isBlockedActor(actorIRI *url.URL) (bool, error) {
blockedactor, err := persistence.GetFollower(actorIRI.String())
if blockedactor != nil && blockedactor.DisabledAt != nil {
return true, errors.Wrap(err, "remote actor is blocked")
}
return false, nil
}

View File

@@ -0,0 +1,100 @@
package inbox
import (
"net/url"
"testing"
"github.com/go-fed/activity/streams"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/activitypub/apmodels"
"github.com/owncast/owncast/activitypub/persistence"
"github.com/owncast/owncast/core/data"
)
func makeFakePerson() vocab.ActivityStreamsPerson {
iri, _ := url.Parse("https://freedom.eagle/user/mrfoo")
name := "Mr Foo"
username := "foodawg"
inbox, _ := url.Parse("https://fake.fediverse.server/user/mrfoo/inbox")
userAvatarURL, _ := url.Parse("https://fake.fediverse.server/user/mrfoo/avatar.png")
person := streams.NewActivityStreamsPerson()
id := streams.NewJSONLDIdProperty()
id.Set(iri)
person.SetJSONLDId(id)
nameProperty := streams.NewActivityStreamsNameProperty()
nameProperty.AppendXMLSchemaString(name)
person.SetActivityStreamsName(nameProperty)
preferredUsernameProperty := streams.NewActivityStreamsPreferredUsernameProperty()
preferredUsernameProperty.SetXMLSchemaString(username)
person.SetActivityStreamsPreferredUsername(preferredUsernameProperty)
inboxProp := streams.NewActivityStreamsInboxProperty()
inboxProp.SetIRI(inbox)
person.SetActivityStreamsInbox(inboxProp)
image := streams.NewActivityStreamsImage()
imgProp := streams.NewActivityStreamsUrlProperty()
imgProp.AppendIRI(userAvatarURL)
image.SetActivityStreamsUrl(imgProp)
icon := streams.NewActivityStreamsIconProperty()
icon.AppendActivityStreamsImage(image)
person.SetActivityStreamsIcon(icon)
return person
}
func TestMain(m *testing.M) {
data.SetupPersistence(":memory:")
data.SetServerURL("https://my.cool.site.biz")
persistence.Setup(data.GetDatastore())
m.Run()
}
func TestBlockedDomains(t *testing.T) {
person := makeFakePerson()
data.SetBlockedFederatedDomains([]string{"freedom.eagle", "guns.life"})
if len(data.GetBlockedFederatedDomains()) != 2 {
t.Error("Blocked federated domains is not set correctly")
}
for _, domain := range data.GetBlockedFederatedDomains() {
if domain == person.GetJSONLDId().GetIRI().Host {
return
}
}
t.Error("Failed to catch blocked domain")
}
func TestBlockedActors(t *testing.T) {
person := makeFakePerson()
persistence.AddFollow(apmodels.ActivityPubActor{
ActorIri: person.GetJSONLDId().GetIRI(),
Inbox: person.GetJSONLDId().GetIRI(),
FollowRequestIri: person.GetJSONLDId().GetIRI(),
}, false)
persistence.BlockOrRejectFollower(person.GetJSONLDId().GetIRI().String())
blocked, err := isBlockedActor(person.GetJSONLDId().GetIRI())
if err != nil {
t.Error(err)
return
}
if !blocked {
t.Error("Failed to block actor")
}
failedBlockIRI, _ := url.Parse("https://freedom.eagle/user/mrbar")
failedBlock, err := isBlockedActor(failedBlockIRI)
if failedBlock {
t.Error("Invalid blocking of unblocked actor IRI")
}
}

View File

@@ -0,0 +1,44 @@
package inbox
import (
"github.com/owncast/owncast/activitypub/apmodels"
log "github.com/sirupsen/logrus"
)
const (
// InboxWorkerPoolSize defines the number of concurrent ActivityPub handlers.
InboxWorkerPoolSize = 10
)
// Job struct bundling the ActivityPub and the payload in one struct.
type Job struct {
request apmodels.InboxRequest
}
var queue chan Job
// InitInboxWorkerPool starts n go routines that await ActivityPub jobs.
func InitInboxWorkerPool() {
queue = make(chan Job)
// start workers
for i := 1; i <= InboxWorkerPoolSize; i++ {
go worker(i, queue)
}
}
// AddToQueue will queue up an outbound http request.
func AddToQueue(req apmodels.InboxRequest) {
log.Tracef("Queued request for ActivityPub inbox handler")
queue <- Job{req}
}
func worker(workerID int, queue <-chan Job) {
log.Debugf("Started ActivityPub worker %d", workerID)
for job := range queue {
handle(job.request)
log.Tracef("Done with ActivityPub inbox handler using worker %d", workerID)
}
}