Skip to content

Commit

Permalink
Ensure that direct messages are only sent when the user is the recipient
Browse files Browse the repository at this point in the history
  • Loading branch information
akirk committed Dec 19, 2024
1 parent b2e14e9 commit 978ffba
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 69 deletions.
8 changes: 6 additions & 2 deletions includes/class-mailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,12 @@ public static function new_follower( $notification ) {
* @param int $user_id The id of the local blog-user.
*/
public static function direct_message( $activity, $user_id ) {
// Check if Activity is public or not.
if ( is_activity_public( $activity ) ) {
$recipients = extract_recipients_from_activity( $activity );

if (
is_activity_public( $activity ) ||
! in_array( \get_author_posts_url( $user_id ), $recipients )
) {
return;
}

Expand Down
186 changes: 119 additions & 67 deletions tests/includes/class-test-mailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,21 +210,108 @@ public function test_init() {
$this->assertEquals( 10, has_action( 'activitypub_notification_follow', array( Mailer::class, 'new_follower' ) ) );
}

/**
* Data provider for direct message notification.
*
* @return array
*/
public function direct_message_provider() {
return array(
'to' => array(
true,
array(
'actor' => 'https://example.com/author',
'object' => array(
'content' => 'Test direct message',
),
'to' => array( 'user_url' ),
),
),
'none' => array(
false,
array(
'actor' => 'https://example.com/author',
'object' => array(
'content' => 'Test direct message',
),
),
),
'public+reply' => array(
false,
array(
'actor' => 'https://example.com/author',
'object' => array(
'content' => 'Test public reply',
'inReplyTo' => 'https://example.com/post/1',
),
'to' => array( 'https://www.w3.org/ns/activitystreams#Public' ),
),
),
'public+reply+cc' => array(
false,
array(
'actor' => 'https://example.com/author',
'object' => array(
'content' => 'Test public reply',
'inReplyTo' => 'https://example.com/post/1',
),
'to' => array( 'https://www.w3.org/ns/activitystreams#Public' ),
'cc' => array( 'user_url' ),
),
),
'public+followers' => array(
false,
array(
'actor' => 'https://example.com/author',
'object' => array(
'content' => 'Test public activity',
'inReplyTo' => null,
),
'to' => array( 'https://www.w3.org/ns/activitystreams#Public' ),
'cc' => array( 'https://example.com/followers' ),
),
),
'followers' => array(
false,
array(
'actor' => 'https://example.com/author',
'object' => array(
'content' => 'Test activity just to followers',
'inReplyTo' => null,
),
'to' => array( 'https://example.com/followers' ),
),
),
);
}

/**
* Test direct message notification.
*
* @param bool $send_email Whether email should be sent.
* @param array $activity Activity object.
* @dataProvider direct_message_provider
* @covers ::direct_message
*/
public function test_direct_message() {
public function test_direct_message( $send_email, $activity ) {
$user_id = self::$user_id;
$mock = new \MockAction();

$activity = array(
'actor' => 'https://example.com/author',
'object' => array(
'content' => 'Test direct message',
),
);
// We need to replace back in the user URL because the user_id is not available in the data provider.
$replace = function ( $url ) use ( $user_id ) {
if ( $url === 'user_url' ) {
return get_author_posts_url( $user_id );
}
return $url;
};

foreach ( $activity as $key => $value ) {
if ( is_array( $value ) ) {
$activity[ $key ] = array_map( $replace, $value );
} else {
$activity[ $key ] = $replace( $value );
}
}

// Mock remote metadata.
add_filter(
Expand All @@ -238,69 +325,33 @@ function () {
);
add_filter( 'wp_mail', array( $mock, 'filter' ), 1 );

// Capture email.
add_filter(
'wp_mail',
function ( $args ) use ( $user_id ) {
$this->assertStringContainsString( 'Direct Message', $args['subject'] );
$this->assertStringContainsString( 'Test Sender', $args['subject'] );
$this->assertStringContainsString( 'Test direct message', $args['message'] );
$this->assertStringContainsString( 'https://example.com/author', $args['message'] );
$this->assertEquals( get_user_by( 'id', $user_id )->user_email, $args['to'] );
return $args;
}
);
if ( $send_email ) {
// Capture email.
add_filter(
'wp_mail',
function ( $args ) use ( $user_id, $activity ) {
$this->assertStringContainsString( 'Direct Message', $args['subject'] );
$this->assertStringContainsString( 'Test Sender', $args['subject'] );
$this->assertStringContainsString( $activity['object']['content'], $args['message'] );
$this->assertStringContainsString( 'https://example.com/author', $args['message'] );
$this->assertEquals( get_user_by( 'id', $user_id )->user_email, $args['to'] );
return $args;
}
);
} else {
add_filter(
'wp_mail',
function ( $args ) {
$this->fail( 'Email should not be sent for public activity' );
return $args;
}
);

}

Mailer::direct_message( $activity, $user_id );

// Test public activity (should not send email).
$public_activity = array(
'actor' => 'https://example.com/author',
'object' => array(
'content' => 'Test public reply',
'inReplyTo' => 'https://example.com/post/1',
),
'to' => array( 'https://www.w3.org/ns/activitystreams#Public' ),
);

// Reset email capture.
remove_all_filters( 'wp_mail' );
add_filter( 'wp_mail', array( $mock, 'filter' ), 1 );
add_filter(
'wp_mail',
function ( $args ) {
$this->fail( 'Email should not be sent for public activity' );
return $args;
}
);

Mailer::direct_message( $public_activity, $user_id );

// Test public activity (should not send email).
$public_activity = array(
'actor' => 'https://example.com/author',
'object' => array(
'content' => 'Test public activity',
'inReplyTo' => null,
),
'to' => array( 'https://www.w3.org/ns/activitystreams#Public' ),
'cc' => array( 'https://example.com/followers' ),
);

// Reset email capture.
remove_all_filters( 'wp_mail' );
add_filter( 'wp_mail', array( $mock, 'filter' ), 1 );
add_filter(
'wp_mail',
function ( $args ) {
$this->fail( 'Email should not be sent for public activity' );
return $args;
}
);

Mailer::direct_message( $public_activity, $user_id );

$this->assertEquals( 1, $mock->get_call_count() );
$this->assertEquals( $send_email ? 1 : 0, $mock->get_call_count() );

// Clean up.
remove_all_filters( 'pre_get_remote_metadata_by_actor' );
Expand Down Expand Up @@ -343,6 +394,7 @@ public function test_direct_message_text( $text, $expected ) {
'object' => array(
'content' => $text,
),
'to' => array( get_author_posts_url( $user_id ) ),
);

// Mock remote metadata.
Expand Down

0 comments on commit 978ffba

Please sign in to comment.