diff options
author | 2024-07-24 13:27:42 +0200 | |
---|---|---|
committer | 2024-07-24 12:27:42 +0100 | |
commit | c9b6220fef01dce80a31436660cd06b4e1db030f (patch) | |
tree | 5fbade865a920a5ea04fdd63763eca1880d60c5d /internal/processing/status | |
parent | [chore] renames the `GTS` caches to `DB` caches (#3127) (diff) | |
download | gotosocial-c9b6220fef01dce80a31436660cd06b4e1db030f.tar.xz |
[chore] Add interaction filter to complement existing visibility filter (#3111)
* [chore] Add interaction filter to complement existing visibility filter
* pass in ptr to visibility and interaction filters to Processor{} to ensure shared
* use int constants for for match type, cache db calls in filterctx
* function name typo :innocent:
---------
Co-authored-by: kim <grufwub@gmail.com>
Diffstat (limited to 'internal/processing/status')
-rw-r--r-- | internal/processing/status/boost.go | 37 | ||||
-rw-r--r-- | internal/processing/status/context.go | 6 | ||||
-rw-r--r-- | internal/processing/status/create.go | 41 | ||||
-rw-r--r-- | internal/processing/status/fave.go | 73 | ||||
-rw-r--r-- | internal/processing/status/status.go | 10 | ||||
-rw-r--r-- | internal/processing/status/status_test.go | 23 |
6 files changed, 160 insertions, 30 deletions
diff --git a/internal/processing/status/boost.go b/internal/processing/status/boost.go index 1b410bb0a..d6a0c2457 100644 --- a/internal/processing/status/boost.go +++ b/internal/processing/status/boost.go @@ -66,7 +66,7 @@ func (p *Processor) BoostCreate( } // Ensure valid boost target for requester. - boostable, err := p.filter.StatusBoostable(ctx, + policyResult, err := p.intFilter.StatusBoostable(ctx, requester, target, ) @@ -75,12 +75,14 @@ func (p *Processor) BoostCreate( return nil, gtserror.NewErrorInternalError(err) } - if !boostable { - err := gtserror.New("status is not boostable") - return nil, gtserror.NewErrorNotFound(err) + if policyResult.Forbidden() { + const errText = "you do not have permission to boost this status" + err := gtserror.New(errText) + return nil, gtserror.NewErrorForbidden(err, errText) } - // Status is visible and boostable. + // Status is visible and boostable + // (though maybe pending approval). boost, err := p.converter.StatusToBoost(ctx, target, requester, @@ -90,6 +92,29 @@ func (p *Processor) BoostCreate( return nil, gtserror.NewErrorInternalError(err) } + // Derive pendingApproval status. + var pendingApproval bool + switch { + case policyResult.WithApproval(): + // We're allowed to do + // this pending approval. + pendingApproval = true + + case policyResult.MatchedOnCollection(): + // We're permitted to do this, but since + // we matched due to presence in a followers + // or following collection, we should mark + // as pending approval and wait for an accept. + pendingApproval = true + + case policyResult.Permitted(): + // We're permitted to do this + // based on another kind of match. + pendingApproval = false + } + + boost.PendingApproval = &pendingApproval + // Store the new boost. if err := p.state.DB.PutStatus(ctx, boost); err != nil { return nil, gtserror.NewErrorInternalError(err) @@ -184,7 +209,7 @@ func (p *Processor) StatusBoostedBy(ctx context.Context, requestingAccount *gtsm targetStatus = boostedStatus } - visible, err := p.filter.StatusVisible(ctx, requestingAccount, targetStatus) + visible, err := p.visFilter.StatusVisible(ctx, requestingAccount, targetStatus) if err != nil { err = fmt.Errorf("BoostedBy: error seeing if status %s is visible: %s", targetStatus.ID, err) return nil, gtserror.NewErrorNotFound(err) diff --git a/internal/processing/status/context.go b/internal/processing/status/context.go index 013cf4827..9f3a7d089 100644 --- a/internal/processing/status/context.go +++ b/internal/processing/status/context.go @@ -341,7 +341,7 @@ func (p *Processor) ContextGet( // Convert ancestors + filter // out ones that aren't visible. for _, status := range threadContext.ancestors { - if v, err := p.filter.StatusVisible(ctx, requester, status); err == nil && v { + if v, err := p.visFilter.StatusVisible(ctx, requester, status); err == nil && v { status, err := convert(ctx, status, requester) if err == nil { apiContext.Ancestors = append(apiContext.Ancestors, *status) @@ -352,7 +352,7 @@ func (p *Processor) ContextGet( // Convert descendants + filter // out ones that aren't visible. for _, status := range threadContext.descendants { - if v, err := p.filter.StatusVisible(ctx, requester, status); err == nil && v { + if v, err := p.visFilter.StatusVisible(ctx, requester, status); err == nil && v { status, err := convert(ctx, status, requester) if err == nil { apiContext.Descendants = append(apiContext.Descendants, *status) @@ -453,7 +453,7 @@ func (p *Processor) WebContextGet( // Ensure status is actually // visible to just anyone, and // hide / don't include it if not. - v, err := p.filter.StatusVisible(ctx, nil, status) + v, err := p.visFilter.StatusVisible(ctx, nil, status) if err != nil || !v { if !inReplies { // Main thread entry hidden. diff --git a/internal/processing/status/create.go b/internal/processing/status/create.go index e6381eb85..10e19ac43 100644 --- a/internal/processing/status/create.go +++ b/internal/processing/status/create.go @@ -169,6 +169,8 @@ func (p *Processor) Create( func (p *Processor) processInReplyTo(ctx context.Context, requester *gtsmodel.Account, status *gtsmodel.Status, inReplyToID string) gtserror.WithCode { if inReplyToID == "" { + // Not a reply. + // Nothing to do. return nil } @@ -191,6 +193,45 @@ func (p *Processor) processInReplyTo(ctx context.Context, requester *gtsmodel.Ac return errWithCode } + // Ensure valid reply target for requester. + policyResult, err := p.intFilter.StatusReplyable(ctx, + requester, + inReplyTo, + ) + if err != nil { + err := gtserror.Newf("error seeing if status %s is replyable: %w", status.ID, err) + return gtserror.NewErrorInternalError(err) + } + + if policyResult.Forbidden() { + const errText = "you do not have permission to reply to this status" + err := gtserror.New(errText) + return gtserror.NewErrorForbidden(err, errText) + } + + // Derive pendingApproval status. + var pendingApproval bool + switch { + case policyResult.WithApproval(): + // We're allowed to do + // this pending approval. + pendingApproval = true + + case policyResult.MatchedOnCollection(): + // We're permitted to do this, but since + // we matched due to presence in a followers + // or following collection, we should mark + // as pending approval and wait for an accept. + pendingApproval = true + + case policyResult.Permitted(): + // We're permitted to do this + // based on another kind of match. + pendingApproval = false + } + + status.PendingApproval = &pendingApproval + // Set status fields from inReplyTo. status.InReplyToID = inReplyTo.ID status.InReplyTo = inReplyTo diff --git a/internal/processing/status/fave.go b/internal/processing/status/fave.go index 49dacf18d..0f5a72b7d 100644 --- a/internal/processing/status/fave.go +++ b/internal/processing/status/fave.go @@ -72,28 +72,73 @@ func (p *Processor) getFaveableStatus( } // FaveCreate adds a fave for the requestingAccount, targeting the given status (no-op if fave already exists). -func (p *Processor) FaveCreate(ctx context.Context, requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) { - targetStatus, existingFave, errWithCode := p.getFaveableStatus(ctx, requestingAccount, targetStatusID) +func (p *Processor) FaveCreate( + ctx context.Context, + requester *gtsmodel.Account, + targetStatusID string, +) (*apimodel.Status, gtserror.WithCode) { + status, existingFave, errWithCode := p.getFaveableStatus(ctx, requester, targetStatusID) if errWithCode != nil { return nil, errWithCode } if existingFave != nil { // Status is already faveed. - return p.c.GetAPIStatus(ctx, requestingAccount, targetStatus) + return p.c.GetAPIStatus(ctx, requester, status) + } + + // Ensure valid fave target for requester. + policyResult, err := p.intFilter.StatusLikeable(ctx, + requester, + status, + ) + if err != nil { + err := gtserror.Newf("error seeing if status %s is likeable: %w", status.ID, err) + return nil, gtserror.NewErrorInternalError(err) + } + + if policyResult.Forbidden() { + const errText = "you do not have permission to fave this status" + err := gtserror.New(errText) + return nil, gtserror.NewErrorForbidden(err, errText) + } + + // Derive pendingApproval status. + var pendingApproval bool + switch { + case policyResult.WithApproval(): + // We're allowed to do + // this pending approval. + pendingApproval = true + + case policyResult.MatchedOnCollection(): + // We're permitted to do this, but since + // we matched due to presence in a followers + // or following collection, we should mark + // as pending approval and wait for an accept. + pendingApproval = true + + case policyResult.Permitted(): + // We're permitted to do this + // based on another kind of match. + pendingApproval = false } - // Create and store a new fave + status.PendingApproval = &pendingApproval + + // Create a new fave, marking it + // as pending approval if necessary. faveID := id.NewULID() gtsFave := >smodel.StatusFave{ ID: faveID, - AccountID: requestingAccount.ID, - Account: requestingAccount, - TargetAccountID: targetStatus.AccountID, - TargetAccount: targetStatus.Account, - StatusID: targetStatus.ID, - Status: targetStatus, - URI: uris.GenerateURIForLike(requestingAccount.Username, faveID), + AccountID: requester.ID, + Account: requester, + TargetAccountID: status.AccountID, + TargetAccount: status.Account, + StatusID: status.ID, + Status: status, + URI: uris.GenerateURIForLike(requester.Username, faveID), + PendingApproval: &pendingApproval, } if err := p.state.DB.PutStatusFave(ctx, gtsFave); err != nil { @@ -106,11 +151,11 @@ func (p *Processor) FaveCreate(ctx context.Context, requestingAccount *gtsmodel. APObjectType: ap.ActivityLike, APActivityType: ap.ActivityCreate, GTSModel: gtsFave, - Origin: requestingAccount, - Target: targetStatus.Account, + Origin: requester, + Target: status.Account, }) - return p.c.GetAPIStatus(ctx, requestingAccount, targetStatus) + return p.c.GetAPIStatus(ctx, requester, status) } // FaveRemove removes a fave for the requesting account, targeting the given status (no-op if fave doesn't exist). diff --git a/internal/processing/status/status.go b/internal/processing/status/status.go index 18f8e741a..7e614cc31 100644 --- a/internal/processing/status/status.go +++ b/internal/processing/status/status.go @@ -19,6 +19,7 @@ package status import ( "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/interaction" "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/processing/common" @@ -35,7 +36,8 @@ type Processor struct { state *state.State federator *federation.Federator converter *typeutils.Converter - filter *visibility.Filter + visFilter *visibility.Filter + intFilter *interaction.Filter formatter *text.Formatter parseMention gtsmodel.ParseMentionFunc @@ -50,7 +52,8 @@ func New( polls *polls.Processor, federator *federation.Federator, converter *typeutils.Converter, - filter *visibility.Filter, + visFilter *visibility.Filter, + intFilter *interaction.Filter, parseMention gtsmodel.ParseMentionFunc, ) Processor { return Processor{ @@ -58,7 +61,8 @@ func New( state: state, federator: federator, converter: converter, - filter: filter, + visFilter: visFilter, + intFilter: intFilter, formatter: text.NewFormatter(state.DB), parseMention: parseMention, polls: polls, diff --git a/internal/processing/status/status_test.go b/internal/processing/status/status_test.go index 9eba78ec6..f0b22b2c1 100644 --- a/internal/processing/status/status_test.go +++ b/internal/processing/status/status_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/federation" + "github.com/superseriousbusiness/gotosocial/internal/filter/interaction" "github.com/superseriousbusiness/gotosocial/internal/filter/visibility" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/media" @@ -89,16 +90,30 @@ func (suite *StatusStandardTestSuite) SetupTest() { suite.mediaManager = testrig.NewTestMediaManager(&suite.state) suite.federator = testrig.NewTestFederator(&suite.state, suite.tc, suite.mediaManager) - filter := visibility.NewFilter(&suite.state) + visFilter := visibility.NewFilter(&suite.state) + intFilter := interaction.NewFilter(&suite.state) testrig.StartTimelines( &suite.state, - filter, + visFilter, suite.typeConverter, ) - common := common.New(&suite.state, suite.mediaManager, suite.typeConverter, suite.federator, filter) + common := common.New(&suite.state, suite.mediaManager, suite.typeConverter, suite.federator, visFilter) polls := polls.New(&common, &suite.state, suite.typeConverter) - suite.status = status.New(&suite.state, &common, &polls, suite.federator, suite.typeConverter, filter, processing.GetParseMentionFunc(&suite.state, suite.federator)) + + suite.status = status.New( + &suite.state, + &common, + &polls, + suite.federator, + suite.typeConverter, + visFilter, + intFilter, + processing.GetParseMentionFunc( + &suite.state, + suite.federator, + ), + ) testrig.StandardDBSetup(suite.db, suite.testAccounts) testrig.StandardStorageSetup(suite.storage, "../../../testrig/media") |