Files
owncast/activitypub/inbox/follow.go
T
Gabe KangasandGitHub a82efb0e9a Add a layer of safety around required ActivityPub Actor fields (#4703)
* fix(ap): add safe constructors, getters, and validators to ActivityPub actors to address #4701

* chore: replace nil check with the new Validate() method

* chore(test): added test to verify the values extracted from actors

* fix: return the specific error type + test for it

* fix(ap): add recovery for each AP inbox worker for a worst case scenario

* fix(ap): add additional safe accessor methods to other AP entities other than actors

* chore(tests): add tests for the new safe accessors

* fix(ap): handle empty public keys in AP actors
2026-01-17 14:25:06 -08:00

100 lines
3.1 KiB
Go

package inbox
import (
"context"
"fmt"
"time"
"github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/activitypub/apmodels"
"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/webhooks"
"github.com/owncast/owncast/persistence/configrepository"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsFollow) error {
configRepository := configrepository.Get()
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 := !configRepository.GetFederationIsPrivate()
followRequest := *follow
if err := persistence.AddFollow(followRequest, approved); err != nil {
log.Errorln("unable to save follow request", err)
return err
}
localAccountName := configRepository.GetDefaultFederationUsername()
objectIRI, err := apmodels.GetIRIStringFromObjectProperty(activity.GetActivityStreamsObject())
if err != nil {
return errors.Wrap(err, "follow activity is missing object IRI")
}
actorIRI, err := apmodels.GetIRIStringFromActorProperty(activity.GetActivityStreamsActor())
if err != nil {
return errors.Wrap(err, "follow activity is missing actor IRI")
}
actorReference := activity.GetActivityStreamsActor()
if approved {
if err := requests.SendFollowAccept(follow.Inbox, activity, localAccountName); err != nil {
log.Errorln("unable to send follow accept", err)
return err
}
go webhooks.SendFediverseEngagementFollowEvent(actorIRI)
}
// 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)
}