mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 20:02:24 -05: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
					
				
			
		
					 5 changed files with 29 additions and 30 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue