mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-22 11:46:40 +00:00
[chore] chore rationalise http return codes for activitypub handlers (#2540)
* some small code fixups and changes * add check in ResolveIncomingActivity for transient activity types (i.e. activity ID is nil) * update test to handle new transient behaviour
This commit is contained in:
parent
906639ad7e
commit
67e11a1a61
|
@ -56,8 +56,9 @@ func putMap(m map[string]any) {
|
||||||
mapPool.Put(m)
|
mapPool.Put(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveActivity is a util function for pulling a pub.Activity type out of an incoming request body.
|
// ResolveActivity is a util function for pulling a pub.Activity type out of an incoming request body,
|
||||||
func ResolveIncomingActivity(r *http.Request) (pub.Activity, gtserror.WithCode) {
|
// returning the resolved activity type, error and whether to accept activity (false = transient i.e. ignore).
|
||||||
|
func ResolveIncomingActivity(r *http.Request) (pub.Activity, bool, gtserror.WithCode) {
|
||||||
// Get "raw" map
|
// Get "raw" map
|
||||||
// destination.
|
// destination.
|
||||||
raw := getMap()
|
raw := getMap()
|
||||||
|
@ -68,7 +69,7 @@ func ResolveIncomingActivity(r *http.Request) (pub.Activity, gtserror.WithCode)
|
||||||
// Decode the JSON body stream into "raw" map.
|
// Decode the JSON body stream into "raw" map.
|
||||||
if err := json.NewDecoder(r.Body).Decode(&raw); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&raw); err != nil {
|
||||||
err := gtserror.Newf("error decoding json: %w", err)
|
err := gtserror.Newf("error decoding json: %w", err)
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, false, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve "raw" JSON to vocab.Type.
|
// Resolve "raw" JSON to vocab.Type.
|
||||||
|
@ -76,25 +77,29 @@ func ResolveIncomingActivity(r *http.Request) (pub.Activity, gtserror.WithCode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !streams.IsUnmatchedErr(err) {
|
if !streams.IsUnmatchedErr(err) {
|
||||||
err := gtserror.Newf("error matching json to type: %w", err)
|
err := gtserror.Newf("error matching json to type: %w", err)
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, false, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Respond with bad request; we just couldn't
|
// Respond with bad request; we just couldn't
|
||||||
// match the type to one that we know about.
|
// match the type to one that we know about.
|
||||||
const text = "body json not resolvable as ActivityStreams type"
|
const text = "body json not resolvable as ActivityStreams type"
|
||||||
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
return nil, false, gtserror.NewErrorBadRequest(errors.New(text), text)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure this is an Activity type.
|
// Ensure this is an Activity type.
|
||||||
activity, ok := t.(pub.Activity)
|
activity, ok := t.(pub.Activity)
|
||||||
if !ok {
|
if !ok {
|
||||||
text := fmt.Sprintf("cannot resolve vocab type %T as pub.Activity", t)
|
text := fmt.Sprintf("cannot resolve vocab type %T as pub.Activity", t)
|
||||||
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
return nil, false, gtserror.NewErrorBadRequest(errors.New(text), text)
|
||||||
}
|
}
|
||||||
|
|
||||||
if activity.GetJSONLDId() == nil {
|
if activity.GetJSONLDId() == nil {
|
||||||
const text = "missing ActivityStreams id property"
|
// missing ID indicates a transient ID as per:
|
||||||
return nil, gtserror.NewErrorBadRequest(errors.New(text), text)
|
//
|
||||||
|
// all objects distributed by the ActivityPub protocol MUST have unique global identifiers,
|
||||||
|
// unless they are intentionally transient (short lived activities that are not intended to
|
||||||
|
// be able to be looked up, such as some kinds of chat messages or game notifications).
|
||||||
|
return nil, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize any Statusable, Accountable, Pollable fields found.
|
// Normalize any Statusable, Accountable, Pollable fields found.
|
||||||
|
@ -104,7 +109,7 @@ func ResolveIncomingActivity(r *http.Request) (pub.Activity, gtserror.WithCode)
|
||||||
// Release.
|
// Release.
|
||||||
putMap(raw)
|
putMap(raw)
|
||||||
|
|
||||||
return activity, nil
|
return activity, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResolveStatusable tries to resolve the given bytes into an ActivityPub Statusable representation.
|
// ResolveStatusable tries to resolve the given bytes into an ActivityPub Statusable representation.
|
||||||
|
|
|
@ -478,15 +478,17 @@ func (suite *InboxPostTestSuite) TestPostEmptyCreate() {
|
||||||
targetAccount = suite.testAccounts["local_account_1"]
|
targetAccount = suite.testAccounts["local_account_1"]
|
||||||
)
|
)
|
||||||
|
|
||||||
// Post a create with no object.
|
// Post a create with no object, this
|
||||||
|
// should get accepted and silently dropped
|
||||||
|
// as the lack of ID marks it as transient.
|
||||||
create := streams.NewActivityStreamsCreate()
|
create := streams.NewActivityStreamsCreate()
|
||||||
|
|
||||||
suite.inboxPost(
|
suite.inboxPost(
|
||||||
create,
|
create,
|
||||||
requestingAccount,
|
requestingAccount,
|
||||||
targetAccount,
|
targetAccount,
|
||||||
http.StatusBadRequest,
|
http.StatusAccepted,
|
||||||
`{"error":"Bad Request: missing ActivityStreams id property"}`,
|
`{"status":"Accepted"}`,
|
||||||
suite.signatureCheck,
|
suite.signatureCheck,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,10 +143,12 @@ func (f *federatingActor) PostInboxScheme(ctx context.Context, w http.ResponseWr
|
||||||
have not yet applied authorization (ie., blocks).
|
have not yet applied authorization (ie., blocks).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Obtain the activity; reject unknown activities.
|
// Resolve the activity, rejecting badly formatted / transient.
|
||||||
activity, errWithCode := ap.ResolveIncomingActivity(r)
|
activity, ok, errWithCode := ap.ResolveIncomingActivity(r)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
return false, errWithCode
|
return false, errWithCode
|
||||||
|
} else if !ok { // transient
|
||||||
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set additional context data. Primarily this means
|
// Set additional context data. Primarily this means
|
||||||
|
|
|
@ -45,16 +45,11 @@ func (f *federatingDB) Reject(ctx context.Context, reject vocab.ActivityStreamsR
|
||||||
return nil // Already processed.
|
return nil // Already processed.
|
||||||
}
|
}
|
||||||
|
|
||||||
rejectObject := reject.GetActivityStreamsObject()
|
for _, obj := range ap.ExtractObjects(reject) {
|
||||||
if rejectObject == nil {
|
|
||||||
return errors.New("Reject: no object set on vocab.ActivityStreamsReject")
|
|
||||||
}
|
|
||||||
|
|
||||||
for iter := rejectObject.Begin(); iter != rejectObject.End(); iter = iter.Next() {
|
if obj.IsIRI() {
|
||||||
// check if the object is an IRI
|
|
||||||
if iter.IsIRI() {
|
|
||||||
// we have just the URI of whatever is being rejected, so we need to find out what it is
|
// we have just the URI of whatever is being rejected, so we need to find out what it is
|
||||||
rejectedObjectIRI := iter.GetIRI()
|
rejectedObjectIRI := obj.GetIRI()
|
||||||
if uris.IsFollowPath(rejectedObjectIRI) {
|
if uris.IsFollowPath(rejectedObjectIRI) {
|
||||||
// REJECT FOLLOW
|
// REJECT FOLLOW
|
||||||
followReq, err := f.state.DB.GetFollowRequestByURI(ctx, rejectedObjectIRI.String())
|
followReq, err := f.state.DB.GetFollowRequestByURI(ctx, rejectedObjectIRI.String())
|
||||||
|
@ -71,15 +66,10 @@ func (f *federatingDB) Reject(ctx context.Context, reject vocab.ActivityStreamsR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if iter is an AP object / type
|
if t := obj.GetType(); t != nil {
|
||||||
if iter.GetType() == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if iter.GetType().GetTypeName() == ap.ActivityFollow {
|
|
||||||
// we have the whole object so we can figure out what we're rejecting
|
// we have the whole object so we can figure out what we're rejecting
|
||||||
// REJECT FOLLOW
|
// REJECT FOLLOW
|
||||||
asFollow, ok := iter.GetType().(vocab.ActivityStreamsFollow)
|
asFollow, ok := t.(vocab.ActivityStreamsFollow)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("Reject: couldn't parse follow into vocab.ActivityStreamsFollow")
|
return errors.New("Reject: couldn't parse follow into vocab.ActivityStreamsFollow")
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ func (f *federatingDB) Undo(ctx context.Context, undo vocab.ActivityStreamsUndo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return errs.Combine()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *federatingDB) undoFollow(
|
func (f *federatingDB) undoFollow(
|
||||||
|
|
Loading…
Reference in a new issue