ensure that errors checking status visibility / converting aren't dropped

This commit is contained in:
kim 2024-09-20 15:55:33 +01:00
commit 480522da21
3 changed files with 100 additions and 66 deletions

View file

@ -104,18 +104,20 @@ func (f *Filter) isStatusVisible(
return false, nil return false, nil
} }
if util.PtrOrValue(status.PendingApproval, false) { if util.PtrOrZero(status.PendingApproval) {
// Use a different visibility heuristic // Use a different visibility heuristic
// for pending approval statuses. // for pending approval statuses.
return f.isPendingStatusVisible(ctx, return isPendingStatusVisible(
requester, status, requester, status,
) ), nil
} }
if requester == nil { if requester == nil {
// Use a different visibility // Use a different visibility
// heuristic for unauthed requests. // heuristic for unauthed requests.
return f.isStatusVisibleUnauthed(ctx, status) return f.isStatusVisibleUnauthed(
ctx, status,
)
} }
/* /*
@ -210,45 +212,42 @@ func (f *Filter) isStatusVisible(
} }
} }
func (f *Filter) isPendingStatusVisible( // isPendingStatusVisible returns whether a status pending approval is visible to requester.
_ context.Context, func isPendingStatusVisible(requester *gtsmodel.Account, status *gtsmodel.Status) bool {
requester *gtsmodel.Account,
status *gtsmodel.Status,
) (bool, error) {
if requester == nil { if requester == nil {
// Any old tom, dick, and harry can't // Any old tom, dick, and harry can't
// see pending-approval statuses, // see pending-approval statuses,
// no matter what their visibility. // no matter what their visibility.
return false, nil return false
} }
if status.AccountID == requester.ID { if status.AccountID == requester.ID {
// This is requester's status, // This is requester's status,
// so they can always see it. // so they can always see it.
return true, nil return true
} }
if status.InReplyToAccountID == requester.ID { if status.InReplyToAccountID == requester.ID {
// This status replies to requester, // This status replies to requester,
// so they can always see it (else // so they can always see it (else
// they can't approve it). // they can't approve it).
return true, nil return true
} }
if status.BoostOfAccountID == requester.ID { if status.BoostOfAccountID == requester.ID {
// This status boosts requester, // This status boosts requester,
// so they can always see it. // so they can always see it.
return true, nil return true
} }
// Nobody else can see this. // Nobody else
return false, nil // can see this.
return false
} }
func (f *Filter) isStatusVisibleUnauthed( // isStatusVisibleUnauthed returns whether status is visible without any unauthenticated account.
ctx context.Context, func (f *Filter) isStatusVisibleUnauthed(ctx context.Context, status *gtsmodel.Status) (bool, error) {
status *gtsmodel.Status,
) (bool, error) {
// For remote accounts, only show // For remote accounts, only show
// Public statuses via the web. // Public statuses via the web.
if status.Account.IsRemote() { if status.Account.IsRemote() {
@ -275,8 +274,7 @@ func (f *Filter) isStatusVisibleUnauthed(
} }
} }
webVisibility := status.Account.Settings.WebVisibility switch webvis := status.Account.Settings.WebVisibility; webvis {
switch webVisibility {
// public_only: status must be Public. // public_only: status must be Public.
case gtsmodel.VisibilityPublic: case gtsmodel.VisibilityPublic:
@ -296,7 +294,7 @@ func (f *Filter) isStatusVisibleUnauthed(
default: default:
return false, gtserror.Newf( return false, gtserror.Newf(
"unrecognized web visibility for account %s: %s", "unrecognized web visibility for account %s: %s",
status.Account.ID, webVisibility, status.Account.ID, webvis,
) )
} }
} }

View file

@ -25,6 +25,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/federation/dereferencing" "github.com/superseriousbusiness/gotosocial/internal/federation/dereferencing"
statusfilter "github.com/superseriousbusiness/gotosocial/internal/filter/status" statusfilter "github.com/superseriousbusiness/gotosocial/internal/filter/status"
"github.com/superseriousbusiness/gotosocial/internal/filter/usermute"
"github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log" "github.com/superseriousbusiness/gotosocial/internal/log"
@ -174,14 +175,72 @@ func (p *Processor) GetAPIStatus(
apiStatus *apimodel.Status, apiStatus *apimodel.Status,
errWithCode gtserror.WithCode, errWithCode gtserror.WithCode,
) { ) {
apiStatus, err := p.converter.StatusToAPIStatus(ctx, target, requester, statusfilter.FilterContextNone, nil, nil) apiStatus, err := p.converter.StatusToAPIStatus(ctx,
target,
requester,
statusfilter.FilterContextNone,
nil,
nil,
)
if err != nil { if err != nil {
err = gtserror.Newf("error converting status: %w", err) err := gtserror.Newf("error converting status: %w", err)
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
return apiStatus, nil return apiStatus, nil
} }
// GetVisibleAPIStatuses converts a slice of statuses to API
// model statuses, filtering according to visibility to requester
// along with given filter context, filters and user mutes.
func (p *Processor) GetVisibleAPIStatuses(
ctx context.Context,
requester *gtsmodel.Account,
statuses []*gtsmodel.Status,
filterContext statusfilter.FilterContext,
filters []*gtsmodel.Filter,
userMutes []*gtsmodel.UserMute,
) []apimodel.Status {
// Compile mutes to useable user mutes for type converter.
compUserMutes := usermute.NewCompiledUserMuteList(userMutes)
// Iterate filtered statuses for conversion to API model.
apiStatuses := make([]apimodel.Status, 0, len(statuses))
for _, status := range statuses {
// Check whether status is visible to requester.
visible, err := p.visFilter.StatusVisible(ctx,
requester,
status,
)
if err != nil {
log.Errorf(ctx, "error checking status visibility: %v", err)
continue
}
if !visible {
continue
}
// Convert to API status, taking mute / filter into account.
apiStatus, err := p.converter.StatusToAPIStatus(ctx,
status,
requester,
filterContext,
filters,
compUserMutes,
)
if err != nil && !errors.Is(err, statusfilter.ErrHideStatus) {
log.Errorf(ctx, "error converting to api model: %v", err)
continue
}
// Append converted status to return slice.
apiStatuses = append(apiStatuses, *apiStatus)
}
return apiStatuses
}
// InvalidateTimelinedStatus is a shortcut function for invalidating the cached // InvalidateTimelinedStatus is a shortcut function for invalidating the cached
// representation one status in the home timeline and all list timelines of the // representation one status in the home timeline and all list timelines of the
// given accountID. It should only be called in cases where a status update // given accountID. It should only be called in cases where a status update

View file

@ -24,7 +24,6 @@ import (
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
statusfilter "github.com/superseriousbusiness/gotosocial/internal/filter/status" statusfilter "github.com/superseriousbusiness/gotosocial/internal/filter/status"
"github.com/superseriousbusiness/gotosocial/internal/filter/usermute"
"github.com/superseriousbusiness/gotosocial/internal/gtscontext" "github.com/superseriousbusiness/gotosocial/internal/gtscontext"
"github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
@ -308,22 +307,7 @@ func (p *Processor) ContextGet(
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
convert := func( // Retrieve the full thread context.
ctx context.Context,
status *gtsmodel.Status,
requestingAccount *gtsmodel.Account,
) (*apimodel.Status, error) {
return p.converter.StatusToAPIStatus(
ctx,
status,
requestingAccount,
statusfilter.FilterContextThread,
filters,
usermute.NewCompiledUserMuteList(mutes),
)
}
// Retrieve the thread context.
threadContext, errWithCode := p.contextGet( threadContext, errWithCode := p.contextGet(
ctx, ctx,
requester, requester,
@ -333,34 +317,27 @@ func (p *Processor) ContextGet(
return nil, errWithCode return nil, errWithCode
} }
apiContext := &apimodel.ThreadContext{ var apiContext apimodel.ThreadContext
Ancestors: make([]apimodel.Status, 0, len(threadContext.ancestors)),
Descendants: make([]apimodel.Status, 0, len(threadContext.descendants)),
}
// Convert ancestors + filter // Convert and filter the thread context ancestors.
// out ones that aren't visible. apiContext.Ancestors = p.c.GetVisibleAPIStatuses(ctx,
for _, status := range threadContext.ancestors { requester,
if v, err := p.visFilter.StatusVisible(ctx, requester, status); err == nil && v { threadContext.ancestors,
status, err := convert(ctx, status, requester) statusfilter.FilterContextThread,
if err == nil { filters,
apiContext.Ancestors = append(apiContext.Ancestors, *status) mutes,
} )
}
}
// Convert descendants + filter // Convert and filter the thread context descendants
// out ones that aren't visible. apiContext.Descendants = p.c.GetVisibleAPIStatuses(ctx,
for _, status := range threadContext.descendants { requester,
if v, err := p.visFilter.StatusVisible(ctx, requester, status); err == nil && v { threadContext.descendants,
status, err := convert(ctx, status, requester) statusfilter.FilterContextThread,
if err == nil { filters,
apiContext.Descendants = append(apiContext.Descendants, *status) mutes,
} )
}
}
return apiContext, nil return &apiContext, nil
} }
// WebContextGet is like ContextGet, but is explicitly // WebContextGet is like ContextGet, but is explicitly