[bugfix] Check interaction policies properly on incoming Likes (#3416)

This commit is contained in:
tobi 2024-10-11 15:21:56 +02:00 committed by GitHub
parent cb9008fb41
commit 1c895f314c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 84 additions and 14 deletions

View File

@ -213,7 +213,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
visFilter := visibility.NewFilter(state) visFilter := visibility.NewFilter(state)
intFilter := interaction.NewFilter(state) intFilter := interaction.NewFilter(state)
spamFilter := spam.NewFilter(state) spamFilter := spam.NewFilter(state)
federatingDB := federatingdb.New(state, typeConverter, visFilter, spamFilter) federatingDB := federatingdb.New(state, typeConverter, visFilter, intFilter, spamFilter)
transportController := transport.NewController(state, federatingDB, &federation.Clock{}, client) transportController := transport.NewController(state, federatingDB, &federation.Clock{}, client)
federator := federation.NewFederator( federator := federation.NewFederator(
state, state,

View File

@ -445,42 +445,106 @@ func (f *federatingDB) activityFollow(ctx context.Context, asType vocab.Type, re
LIKE HANDLERS LIKE HANDLERS
*/ */
func (f *federatingDB) activityLike(ctx context.Context, asType vocab.Type, receivingAccount *gtsmodel.Account, requestingAccount *gtsmodel.Account) error { func (f *federatingDB) activityLike(
ctx context.Context,
asType vocab.Type,
receivingAcct *gtsmodel.Account,
requestingAcct *gtsmodel.Account,
) error {
like, ok := asType.(vocab.ActivityStreamsLike) like, ok := asType.(vocab.ActivityStreamsLike)
if !ok { if !ok {
return errors.New("activityLike: could not convert type to like") err := gtserror.Newf("could not convert asType %T to ActivityStreamsLike", asType)
return gtserror.SetMalformed(err)
} }
fave, err := f.converter.ASLikeToFave(ctx, like) fave, err := f.converter.ASLikeToFave(ctx, like)
if err != nil { if err != nil {
return fmt.Errorf("activityLike: could not convert Like to fave: %w", err) return gtserror.Newf("could not convert Like to fave: %w", err)
} }
if fave.AccountID != requestingAccount.ID { // Ensure requester not trying to
return fmt.Errorf( // Like on someone else's behalf.
"activityLike: requestingAccount %s is not Like actor account %s", if fave.AccountID != requestingAcct.ID {
requestingAccount.URI, fave.Account.URI, text := fmt.Sprintf(
"requestingAcct %s is not Like actor account %s",
requestingAcct.URI, fave.Account.URI,
) )
return gtserror.NewErrorForbidden(errors.New(text), text)
} }
if !*fave.Status.Local {
// Only process likes of local statuses.
// TODO: process for remote statuses as well.
return nil
}
// Ensure valid Like target for requester.
policyResult, err := f.intFilter.StatusLikeable(ctx,
requestingAcct,
fave.Status,
)
if err != nil {
err := gtserror.Newf("error seeing if status %s is likeable: %w", fave.Status.ID, err)
return gtserror.NewErrorInternalError(err)
}
if policyResult.Forbidden() {
const errText = "requester does not have permission to Like this status"
err := gtserror.New(errText)
return gtserror.NewErrorForbidden(err, errText)
}
// Derive pendingApproval
// and preapproved status.
var (
pendingApproval bool
preApproved bool
)
switch {
case policyResult.WithApproval():
// Requester allowed to do
// this pending approval.
pendingApproval = true
case policyResult.MatchedOnCollection():
// Requester allowed to do this,
// but matched on collection.
// Preapprove Like and have the
// processor send out an Accept.
pendingApproval = true
preApproved = true
case policyResult.Permitted():
// Requester straight up
// permitted to do this,
// no need for Accept.
pendingApproval = false
}
// Set appropriate fields
// on fave and store it.
fave.ID = id.NewULID() fave.ID = id.NewULID()
fave.PendingApproval = &pendingApproval
fave.PreApproved = preApproved
if err := f.state.DB.PutStatusFave(ctx, fave); err != nil { if err := f.state.DB.PutStatusFave(ctx, fave); err != nil {
if errors.Is(err, db.ErrAlreadyExists) { if errors.Is(err, db.ErrAlreadyExists) {
// The Like already exists in the database, which // The fave already exists in the
// means we've already handled side effects. We can // database, which means we've already
// just return nil here and be done with it. // handled side effects. We can just
// return nil here and be done with it.
return nil return nil
} }
return fmt.Errorf("activityLike: database error inserting fave: %w", err) return gtserror.Newf("db error inserting fave: %w", err)
} }
f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{ f.state.Workers.Federator.Queue.Push(&messages.FromFediAPI{
APObjectType: ap.ActivityLike, APObjectType: ap.ActivityLike,
APActivityType: ap.ActivityCreate, APActivityType: ap.ActivityCreate,
GTSModel: fave, GTSModel: fave,
Receiving: receivingAccount, Receiving: receivingAcct,
Requesting: requestingAccount, Requesting: requestingAcct,
}) })
return nil return nil

View File

@ -23,6 +23,7 @@ import (
"github.com/superseriousbusiness/activity/pub" "github.com/superseriousbusiness/activity/pub"
"github.com/superseriousbusiness/activity/streams/vocab" "github.com/superseriousbusiness/activity/streams/vocab"
"github.com/superseriousbusiness/gotosocial/internal/filter/interaction"
"github.com/superseriousbusiness/gotosocial/internal/filter/spam" "github.com/superseriousbusiness/gotosocial/internal/filter/spam"
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
"github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/state"
@ -58,6 +59,7 @@ type federatingDB struct {
state *state.State state *state.State
converter *typeutils.Converter converter *typeutils.Converter
visFilter *visibility.Filter visFilter *visibility.Filter
intFilter *interaction.Filter
spamFilter *spam.Filter spamFilter *spam.Filter
} }
@ -67,12 +69,14 @@ func New(
state *state.State, state *state.State,
converter *typeutils.Converter, converter *typeutils.Converter,
visFilter *visibility.Filter, visFilter *visibility.Filter,
intFilter *interaction.Filter,
spamFilter *spam.Filter, spamFilter *spam.Filter,
) DB { ) DB {
fdb := federatingDB{ fdb := federatingDB{
state: state, state: state,
converter: converter, converter: converter,
visFilter: visFilter, visFilter: visFilter,
intFilter: intFilter,
spamFilter: spamFilter, spamFilter: spamFilter,
} }
return &fdb return &fdb

View File

@ -19,6 +19,7 @@ package testrig
import ( import (
"github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb" "github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb"
"github.com/superseriousbusiness/gotosocial/internal/filter/interaction"
"github.com/superseriousbusiness/gotosocial/internal/filter/spam" "github.com/superseriousbusiness/gotosocial/internal/filter/spam"
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
"github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/state"
@ -31,6 +32,7 @@ func NewTestFederatingDB(state *state.State) federatingdb.DB {
state, state,
typeutils.NewConverter(state), typeutils.NewConverter(state),
visibility.NewFilter(state), visibility.NewFilter(state),
interaction.NewFilter(state),
spam.NewFilter(state), spam.NewFilter(state),
) )
} }