summaryrefslogtreecommitdiff
path: root/internal/processing/status
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2024-07-24 13:27:42 +0200
committerLibravatar GitHub <noreply@github.com>2024-07-24 12:27:42 +0100
commitc9b6220fef01dce80a31436660cd06b4e1db030f (patch)
tree5fbade865a920a5ea04fdd63763eca1880d60c5d /internal/processing/status
parent[chore] renames the `GTS` caches to `DB` caches (#3127) (diff)
downloadgotosocial-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.go37
-rw-r--r--internal/processing/status/context.go6
-rw-r--r--internal/processing/status/create.go41
-rw-r--r--internal/processing/status/fave.go73
-rw-r--r--internal/processing/status/status.go10
-rw-r--r--internal/processing/status/status_test.go23
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 := &gtsmodel.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")