mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[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 😇 --------- Co-authored-by: kim <grufwub@gmail.com>
This commit is contained in:
@@ -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)
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
}
|
||||
|
||||
// Create and store a new fave
|
||||
// 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
|
||||
}
|
||||
|
||||
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).
|
||||
|
@@ -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,
|
||||
|
@@ -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")
|
||||
|
Reference in New Issue
Block a user