Skip to content

Commit db9c0bd

Browse files
pfefferlematticbotobenland
authored
Fix: Add user context to global inbox (#1603)
* Fix: Add user context to global inbox * Add testcases * Add changelog * fix phpcs issues * add slash * test improvements * Update class-test-inbox-controller.php --------- Co-authored-by: Automattic Bot <[email protected]> Co-authored-by: Konstantin Obenland <[email protected]>
1 parent a6d6b3c commit db9c0bd

File tree

3 files changed

+320
-18
lines changed

3 files changed

+320
-18
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: patch
2+
Type: fixed
3+
4+
Include user context in Global-Inbox actions.

includes/rest/class-inbox-controller.php

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@
88
namespace Activitypub\Rest;
99

1010
use Activitypub\Activity\Activity;
11+
use Activitypub\Collection\Actors;
1112
use Activitypub\Debug;
1213

14+
use function Activitypub\is_same_domain;
15+
use function Activitypub\extract_recipients_from_activity;
16+
1317
/**
1418
* Inbox_Controller class.
1519
*
@@ -134,24 +138,38 @@ public function create_item( $request ) {
134138
if ( \wp_check_comment_disallowed_list( $activity->to_json( false ), '', '', '', $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT'] ?? '' ) ) {
135139
Debug::write_log( 'Blocked activity from: ' . $activity->get_actor() );
136140
} else {
137-
/**
138-
* ActivityPub inbox action.
139-
*
140-
* @param array $data The data array.
141-
* @param int|null $user_id The user ID.
142-
* @param string $type The type of the activity.
143-
* @param Activity|\WP_Error $activity The Activity object.
144-
*/
145-
\do_action( 'activitypub_inbox', $data, null, $type, $activity );
146-
147-
/**
148-
* ActivityPub inbox action for specific activity types.
149-
*
150-
* @param array $data The data array.
151-
* @param int|null $user_id The user ID.
152-
* @param Activity|\WP_Error $activity The Activity object.
153-
*/
154-
\do_action( 'activitypub_inbox_' . $type, $data, null, $activity );
141+
$recipients = extract_recipients_from_activity( $data );
142+
143+
foreach ( $recipients as $recipient ) {
144+
if ( ! is_same_domain( $recipient ) ) {
145+
continue;
146+
}
147+
148+
$actor = Actors::get_by_various( $recipient );
149+
150+
if ( ! $actor || \is_wp_error( $actor ) ) {
151+
continue;
152+
}
153+
154+
/**
155+
* ActivityPub inbox action.
156+
*
157+
* @param array $data The data array.
158+
* @param int $user_id The user ID.
159+
* @param string $type The type of the activity.
160+
* @param Activity|\WP_Error $activity The Activity object.
161+
*/
162+
\do_action( 'activitypub_inbox', $data, $actor->get__id(), $type, $activity );
163+
164+
/**
165+
* ActivityPub inbox action for specific activity types.
166+
*
167+
* @param array $data The data array.
168+
* @param int $user_id The user ID.
169+
* @param Activity|\WP_Error $activity The Activity object.
170+
*/
171+
\do_action( 'activitypub_inbox_' . $type, $data, $actor->get__id(), $activity );
172+
}
155173
}
156174

157175
$response = \rest_ensure_response( array() );

tests/includes/rest/class-test-inbox-controller.php

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,27 @@
1414
* @coversDefaultClass \Activitypub\Rest\Inbox_Controller
1515
*/
1616
class Test_Inbox_Controller extends \Activitypub\Tests\Test_REST_Controller_Testcase {
17+
/**
18+
* User ID.
19+
*
20+
* @var int
21+
*/
22+
protected static $user_id;
23+
24+
/**
25+
* Create fake data before tests run.
26+
*/
27+
public static function set_up_before_class() {
28+
self::$user_id = self::factory()->user->create( array( 'role' => 'author' ) );
29+
}
30+
31+
/**
32+
* Delete fake data after tests run.
33+
*/
34+
public static function tear_down_after_class() {
35+
\wp_delete_user( self::$user_id );
36+
}
37+
1738
/**
1839
* Test follow request global inbox.
1940
*
@@ -231,4 +252,263 @@ public function test_get_item() {
231252
public function test_get_item_schema() {
232253
// Controller does not implement get_item_schema().
233254
}
255+
256+
/**
257+
* Test creating an inbox item with blog user context.
258+
*
259+
* @covers ::create_item
260+
*/
261+
public function test_create_item_with_blog_user() {
262+
\add_filter( 'activitypub_defer_signature_verification', '__return_true' );
263+
264+
\update_option( 'activitypub_actor_mode', ACTIVITYPUB_BLOG_MODE );
265+
266+
$blog_actor = \Activitypub\Collection\Actors::get_by_id( \Activitypub\Collection\Actors::BLOG_USER_ID );
267+
268+
// Set up mock action.
269+
$inbox_action = new \MockAction();
270+
\add_action( 'activitypub_inbox', array( $inbox_action, 'action' ) );
271+
272+
$json = array(
273+
'id' => 'https://remote.example/@id',
274+
'type' => 'Create',
275+
'actor' => 'https://remote.example/@test',
276+
'object' => array(
277+
'id' => 'https://remote.example/post/test',
278+
'type' => 'Note',
279+
'content' => 'Hello, World!',
280+
'to' => array( $blog_actor->get_id() ),
281+
'published' => '2020-01-01T00:00:00Z',
282+
),
283+
'to' => array( $blog_actor->get_id() ),
284+
);
285+
286+
$request = new \WP_REST_Request( 'POST', '/' . ACTIVITYPUB_REST_NAMESPACE . '/inbox' );
287+
$request->set_header( 'Content-Type', 'application/activity+json' );
288+
$request->set_body( \wp_json_encode( $json ) );
289+
290+
$response = \rest_do_request( $request );
291+
$this->assertEquals( 202, $response->get_status() );
292+
293+
// Verify the action was triggered exactly once for a single recipient.
294+
$this->assertEquals( 1, $inbox_action->get_call_count() );
295+
296+
\remove_filter( 'activitypub_defer_signature_verification', '__return_true' );
297+
\delete_option( 'activitypub_actor_mode' );
298+
}
299+
300+
/**
301+
* Test creating an inbox item with multiple recipients.
302+
*
303+
* @covers ::create_item
304+
*/
305+
public function test_create_item_with_multiple_recipients() {
306+
\add_filter( 'activitypub_defer_signature_verification', '__return_true' );
307+
308+
\update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE );
309+
310+
$user_actor = \Activitypub\Collection\Actors::get_by_id( self::$user_id );
311+
$blog_actor = \Activitypub\Collection\Actors::get_by_id( \Activitypub\Collection\Actors::BLOG_USER_ID );
312+
313+
// Set up mock action.
314+
$inbox_action = new \MockAction();
315+
\add_action( 'activitypub_inbox', array( $inbox_action, 'action' ) );
316+
317+
$json = array(
318+
'id' => 'https://remote.example/@id',
319+
'type' => 'Create',
320+
'actor' => 'https://remote.example/@test',
321+
'object' => array(
322+
'id' => 'https://remote.example/post/test',
323+
'type' => 'Note',
324+
'content' => 'Hello, World!',
325+
'to' => array( $user_actor->get_id(), $blog_actor->get_id() ),
326+
'published' => '2020-01-01T00:00:00Z',
327+
),
328+
'to' => array( $user_actor->get_id(), $blog_actor->get_id() ),
329+
);
330+
331+
$request = new \WP_REST_Request( 'POST', '/' . ACTIVITYPUB_REST_NAMESPACE . '/inbox' );
332+
$request->set_header( 'Content-Type', 'application/activity+json' );
333+
$request->set_body( \wp_json_encode( $json ) );
334+
335+
$response = \rest_do_request( $request );
336+
$this->assertEquals( 202, $response->get_status() );
337+
338+
// Verify the action was triggered exactly once for each recipient.
339+
$this->assertEquals( 2, $inbox_action->get_call_count() );
340+
341+
\remove_filter( 'activitypub_defer_signature_verification', '__return_true' );
342+
\delete_option( 'activitypub_actor_mode' );
343+
}
344+
345+
/**
346+
* Test creating an inbox item with multiple recipients and invalid recipient.
347+
*
348+
* @covers ::create_item
349+
*/
350+
public function test_create_item_with_multiple_recipients_and_invalid_recipient() {
351+
\add_filter( 'activitypub_defer_signature_verification', '__return_true' );
352+
353+
\update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE );
354+
355+
$user_actor = \Activitypub\Collection\Actors::get_by_id( self::$user_id );
356+
$blog_actor = \Activitypub\Collection\Actors::get_by_id( \Activitypub\Collection\Actors::BLOG_USER_ID );
357+
358+
// Set up mock action.
359+
$inbox_action = new \MockAction();
360+
\add_action( 'activitypub_inbox', array( $inbox_action, 'action' ) );
361+
362+
$json = array(
363+
'id' => 'https://remote.example/@id',
364+
'type' => 'Create',
365+
'actor' => 'https://remote.example/@test',
366+
'object' => array(
367+
'id' => 'https://remote.example/post/test',
368+
'type' => 'Note',
369+
'content' => 'Hello, World!',
370+
'to' => array( $user_actor->get_id(), $blog_actor->get_id() ),
371+
'published' => '2020-01-01T00:00:00Z',
372+
),
373+
'to' => array( $user_actor->get_id(), $blog_actor->get_id(), 'https://invalid.example/@test' ),
374+
);
375+
376+
$request = new \WP_REST_Request( 'POST', '/' . ACTIVITYPUB_REST_NAMESPACE . '/inbox' );
377+
$request->set_header( 'Content-Type', 'application/activity+json' );
378+
$request->set_body( \wp_json_encode( $json ) );
379+
380+
$response = \rest_do_request( $request );
381+
$this->assertEquals( 202, $response->get_status() );
382+
383+
// Verify the action was triggered exactly once for each recipient.
384+
$this->assertEquals( 2, $inbox_action->get_call_count() );
385+
386+
\remove_filter( 'activitypub_defer_signature_verification', '__return_true' );
387+
\delete_option( 'activitypub_actor_mode' );
388+
}
389+
390+
/**
391+
* Test creating an inbox item with multiple recipients and inactive recipient.
392+
*
393+
* @covers ::create_item
394+
*/
395+
public function test_create_item_with_multiple_recipients_and_inactive_recipient() {
396+
\add_filter( 'activitypub_defer_signature_verification', '__return_true' );
397+
398+
\update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_AND_BLOG_MODE );
399+
400+
$user_actor = \Activitypub\Collection\Actors::get_by_id( self::$user_id );
401+
$blog_actor = \Activitypub\Collection\Actors::get_by_id( \Activitypub\Collection\Actors::BLOG_USER_ID );
402+
403+
// Set up mock action.
404+
$inbox_action = new \MockAction();
405+
\add_action( 'activitypub_inbox', array( $inbox_action, 'action' ) );
406+
407+
$json = array(
408+
'id' => 'https://remote.example/@id',
409+
'type' => 'Create',
410+
'actor' => 'https://remote.example/@test',
411+
'object' => array(
412+
'id' => 'https://remote.example/post/test',
413+
'type' => 'Note',
414+
'content' => 'Hello, World!',
415+
'to' => array( $user_actor->get_id(), $blog_actor->get_id() ),
416+
'published' => '2020-01-01T00:00:00Z',
417+
),
418+
'to' => array( $user_actor->get_id(), $blog_actor->get_id() ),
419+
);
420+
421+
\update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE );
422+
423+
$request = new \WP_REST_Request( 'POST', '/' . ACTIVITYPUB_REST_NAMESPACE . '/inbox' );
424+
$request->set_header( 'Content-Type', 'application/activity+json' );
425+
$request->set_body( \wp_json_encode( $json ) );
426+
427+
$response = \rest_do_request( $request );
428+
$this->assertEquals( 202, $response->get_status() );
429+
430+
// Verify the action was triggered exactly once for each recipient.
431+
$this->assertEquals( 1, $inbox_action->get_call_count() );
432+
433+
\remove_filter( 'activitypub_defer_signature_verification', '__return_true' );
434+
\delete_option( 'activitypub_actor_mode' );
435+
}
436+
437+
/**
438+
* Test creating an inbox item with different activity types.
439+
*
440+
* @covers ::create_item
441+
*/
442+
public function test_create_item_with_different_activity_types() {
443+
\add_filter( 'activitypub_defer_signature_verification', '__return_true' );
444+
445+
\update_option( 'activitypub_actor_mode', ACTIVITYPUB_ACTOR_MODE );
446+
447+
$user_actor = \Activitypub\Collection\Actors::get_by_id( self::$user_id );
448+
$activity_types = array( 'Update', 'Delete', 'Follow', 'Accept', 'Reject', 'Announce', 'Like' );
449+
450+
foreach ( $activity_types as $type ) {
451+
// Set up mock action.
452+
$inbox_action = new \MockAction();
453+
\add_action( 'activitypub_inbox', array( $inbox_action, 'action' ) );
454+
455+
$json = array(
456+
'id' => 'https://remote.example/@id',
457+
'type' => $type,
458+
'actor' => 'https://remote.example/@test',
459+
'object' => array(
460+
'id' => 'https://remote.example/post/test',
461+
'type' => 'Note',
462+
'content' => 'Hello, World!',
463+
'to' => array( $user_actor->get_id() ),
464+
'published' => '2020-01-01T00:00:00Z',
465+
),
466+
'to' => array( $user_actor->get_id() ),
467+
);
468+
469+
$request = new \WP_REST_Request( 'POST', '/' . ACTIVITYPUB_REST_NAMESPACE . '/inbox' );
470+
$request->set_header( 'Content-Type', 'application/activity+json' );
471+
$request->set_body( \wp_json_encode( $json ) );
472+
473+
$response = \rest_do_request( $request );
474+
$this->assertEquals( 202, $response->get_status(), "Failed for activity type: {$type}" );
475+
476+
// Verify the action was triggered exactly once for a single recipient.
477+
$this->assertEquals( 1, $inbox_action->get_call_count() );
478+
}
479+
480+
\remove_filter( 'activitypub_defer_signature_verification', '__return_true' );
481+
\delete_option( 'activitypub_actor_mode' );
482+
}
483+
484+
/**
485+
* Test creating an inbox item with invalid request.
486+
*
487+
* @covers ::create_item
488+
*/
489+
public function test_create_item_with_invalid_request() {
490+
\add_filter( 'activitypub_defer_signature_verification', '__return_true' );
491+
492+
// Test with missing required fields.
493+
$json = array(
494+
'type' => 'Create',
495+
);
496+
497+
$request = new \WP_REST_Request( 'POST', '/' . ACTIVITYPUB_REST_NAMESPACE . '/inbox' );
498+
$request->set_header( 'Content-Type', 'application/activity+json' );
499+
$request->set_body( \wp_json_encode( $json ) );
500+
501+
$response = \rest_do_request( $request );
502+
$this->assertEquals( 400, $response->get_status() );
503+
504+
// Test with invalid content type.
505+
$request = new \WP_REST_Request( 'POST', '/' . ACTIVITYPUB_REST_NAMESPACE . '/inbox' );
506+
$request->set_header( 'Content-Type', 'application/json' );
507+
$request->set_body( \wp_json_encode( $json ) );
508+
509+
$response = \rest_do_request( $request );
510+
$this->assertEquals( 400, $response->get_status() );
511+
512+
\remove_filter( 'activitypub_defer_signature_verification', '__return_true' );
513+
}
234514
}

0 commit comments

Comments
 (0)