mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2024-11-27 06:40:32 +00:00
[bugfix] unwrap boosts when checking in-reply-to status (#2702)
* add stronger checks on status being replied to * update error code test is expecting
This commit is contained in:
parent
c2a691fd83
commit
fcecd0c952
2 changed files with 46 additions and 41 deletions
|
@ -284,13 +284,13 @@ func (suite *StatusCreateTestSuite) TestReplyToNonexistentStatus() {
|
||||||
|
|
||||||
// check response
|
// check response
|
||||||
|
|
||||||
suite.EqualValues(http.StatusBadRequest, recorder.Code)
|
suite.EqualValues(http.StatusNotFound, recorder.Code)
|
||||||
|
|
||||||
result := recorder.Result()
|
result := recorder.Result()
|
||||||
defer result.Body.Close()
|
defer result.Body.Close()
|
||||||
b, err := ioutil.ReadAll(result.Body)
|
b, err := ioutil.ReadAll(result.Body)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Equal(`{"error":"Bad Request: cannot reply to status that does not exist"}`, string(b))
|
suite.Equal(`{"error":"Not Found: target status not found"}`, string(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Post a reply to the status of a local user that allows replies.
|
// Post a reply to the status of a local user that allows replies.
|
||||||
|
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"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/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/text"
|
"github.com/superseriousbusiness/gotosocial/internal/text"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
|
@ -40,12 +41,20 @@ import (
|
||||||
// Create processes the given form to create a new status, returning the api model representation of that status if it's OK.
|
// Create processes the given form to create a new status, returning the api model representation of that status if it's OK.
|
||||||
//
|
//
|
||||||
// Precondition: the form's fields should have already been validated and normalized by the caller.
|
// Precondition: the form's fields should have already been validated and normalized by the caller.
|
||||||
func (p *Processor) Create(ctx context.Context, requestingAccount *gtsmodel.Account, application *gtsmodel.Application, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, gtserror.WithCode) {
|
func (p *Processor) Create(
|
||||||
|
ctx context.Context,
|
||||||
|
requester *gtsmodel.Account,
|
||||||
|
application *gtsmodel.Application,
|
||||||
|
form *apimodel.AdvancedStatusCreateForm,
|
||||||
|
) (
|
||||||
|
*apimodel.Status,
|
||||||
|
gtserror.WithCode,
|
||||||
|
) {
|
||||||
// Generate new ID for status.
|
// Generate new ID for status.
|
||||||
statusID := id.NewULID()
|
statusID := id.NewULID()
|
||||||
|
|
||||||
// Generate necessary URIs for username, to build status URIs.
|
// Generate necessary URIs for username, to build status URIs.
|
||||||
accountURIs := uris.GenerateURIsForAccount(requestingAccount.Username)
|
accountURIs := uris.GenerateURIsForAccount(requester.Username)
|
||||||
|
|
||||||
// Get current time.
|
// Get current time.
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
@ -57,9 +66,9 @@ func (p *Processor) Create(ctx context.Context, requestingAccount *gtsmodel.Acco
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
Local: util.Ptr(true),
|
Local: util.Ptr(true),
|
||||||
Account: requestingAccount,
|
Account: requester,
|
||||||
AccountID: requestingAccount.ID,
|
AccountID: requester.ID,
|
||||||
AccountURI: requestingAccount.URI,
|
AccountURI: requester.URI,
|
||||||
ActivityStreamsType: ap.ObjectNote,
|
ActivityStreamsType: ap.ObjectNote,
|
||||||
Sensitive: &form.Sensitive,
|
Sensitive: &form.Sensitive,
|
||||||
CreatedWithApplicationID: application.ID,
|
CreatedWithApplicationID: application.ID,
|
||||||
|
@ -86,7 +95,12 @@ func (p *Processor) Create(ctx context.Context, requestingAccount *gtsmodel.Acco
|
||||||
status.PollID = status.Poll.ID
|
status.PollID = status.Poll.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
if errWithCode := p.processReplyToID(ctx, form, requestingAccount.ID, status); errWithCode != nil {
|
// Check + attach in-reply-to status.
|
||||||
|
if errWithCode := p.processInReplyTo(ctx,
|
||||||
|
requester,
|
||||||
|
status,
|
||||||
|
form.InReplyToID,
|
||||||
|
); errWithCode != nil {
|
||||||
return nil, errWithCode
|
return nil, errWithCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,15 +108,15 @@ func (p *Processor) Create(ctx context.Context, requestingAccount *gtsmodel.Acco
|
||||||
return nil, errWithCode
|
return nil, errWithCode
|
||||||
}
|
}
|
||||||
|
|
||||||
if errWithCode := p.processMediaIDs(ctx, form, requestingAccount.ID, status); errWithCode != nil {
|
if errWithCode := p.processMediaIDs(ctx, form, requester.ID, status); errWithCode != nil {
|
||||||
return nil, errWithCode
|
return nil, errWithCode
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := processVisibility(form, requestingAccount.Privacy, status); err != nil {
|
if err := processVisibility(form, requester.Privacy, status); err != nil {
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := processLanguage(form, requestingAccount.Language, status); err != nil {
|
if err := processLanguage(form, requester.Language, status); err != nil {
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,58 +142,49 @@ func (p *Processor) Create(ctx context.Context, requestingAccount *gtsmodel.Acco
|
||||||
APObjectType: ap.ObjectNote,
|
APObjectType: ap.ObjectNote,
|
||||||
APActivityType: ap.ActivityCreate,
|
APActivityType: ap.ActivityCreate,
|
||||||
GTSModel: status,
|
GTSModel: status,
|
||||||
OriginAccount: requestingAccount,
|
OriginAccount: requester,
|
||||||
})
|
})
|
||||||
|
|
||||||
if status.Poll != nil {
|
if status.Poll != nil {
|
||||||
// Now that the status is inserted, and side effects queued,
|
// Now that the status is inserted, and side effects queued,
|
||||||
// attempt to schedule an expiry handler for the status poll.
|
// attempt to schedule an expiry handler for the status poll.
|
||||||
if err := p.polls.ScheduleExpiry(ctx, status.Poll); err != nil {
|
if err := p.polls.ScheduleExpiry(ctx, status.Poll); err != nil {
|
||||||
err := gtserror.Newf("error scheduling poll expiry: %w", err)
|
log.Errorf(ctx, "error scheduling poll expiry: %v", err)
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.c.GetAPIStatus(ctx, requestingAccount, status)
|
return p.c.GetAPIStatus(ctx, requester, status)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Processor) processReplyToID(ctx context.Context, form *apimodel.AdvancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) gtserror.WithCode {
|
func (p *Processor) processInReplyTo(ctx context.Context, requester *gtsmodel.Account, status *gtsmodel.Status, inReplyToID string) gtserror.WithCode {
|
||||||
if form.InReplyToID == "" {
|
if inReplyToID == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this status is a reply to another status, we need to do a bit of work to establish whether or not this status can be posted:
|
// Fetch target in-reply-to status (checking visibility).
|
||||||
//
|
inReplyTo, errWithCode := p.c.GetVisibleTargetStatus(ctx,
|
||||||
// 1. Does the replied status exist in the database?
|
requester,
|
||||||
// 2. Is the replied status marked as replyable?
|
inReplyToID,
|
||||||
// 3. Does a block exist between either the current account or the account that posted the status it's replying to?
|
nil,
|
||||||
//
|
)
|
||||||
// If this is all OK, then we fetch the repliedStatus and the repliedAccount for later processing.
|
if errWithCode != nil {
|
||||||
|
return errWithCode
|
||||||
inReplyTo, err := p.state.DB.GetStatusByID(ctx, form.InReplyToID)
|
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
|
||||||
err := gtserror.Newf("error fetching status %s from db: %w", form.InReplyToID, err)
|
|
||||||
return gtserror.NewErrorInternalError(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if inReplyTo == nil {
|
// If this is a boost, unwrap it to get source status.
|
||||||
const text = "cannot reply to status that does not exist"
|
inReplyTo, errWithCode = p.c.UnwrapIfBoost(ctx,
|
||||||
return gtserror.NewErrorBadRequest(errors.New(text), text)
|
requester,
|
||||||
|
inReplyTo,
|
||||||
|
)
|
||||||
|
if errWithCode != nil {
|
||||||
|
return errWithCode
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*inReplyTo.Replyable {
|
if !*inReplyTo.Replyable {
|
||||||
text := fmt.Sprintf("status %s is marked as not replyable", form.InReplyToID)
|
const text = "in-reply-to status marked as not replyable"
|
||||||
return gtserror.NewErrorForbidden(errors.New(text), text)
|
return gtserror.NewErrorForbidden(errors.New(text), text)
|
||||||
}
|
}
|
||||||
|
|
||||||
if blocked, err := p.state.DB.IsEitherBlocked(ctx, thisAccountID, inReplyTo.AccountID); err != nil {
|
|
||||||
err := gtserror.Newf("error checking block in db: %w", err)
|
|
||||||
return gtserror.NewErrorInternalError(err)
|
|
||||||
} else if blocked {
|
|
||||||
text := fmt.Sprintf("status %s is not replyable", form.InReplyToID)
|
|
||||||
return gtserror.NewErrorNotFound(errors.New(text), text)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set status fields from inReplyTo.
|
// Set status fields from inReplyTo.
|
||||||
status.InReplyToID = inReplyTo.ID
|
status.InReplyToID = inReplyTo.ID
|
||||||
status.InReplyTo = inReplyTo
|
status.InReplyTo = inReplyTo
|
||||||
|
|
Loading…
Reference in a new issue