fix(subscriptions): post notifications not getting access checked

Signed-off-by: Sami Mazouz <sychocouldy@gmail.com>
This commit is contained in:
Sami Mazouz 2023-01-10 15:04:02 +01:00
parent 02556c6ca6
commit e5f05166a0
No known key found for this signature in database
3 changed files with 76 additions and 1 deletions

View File

@ -21,6 +21,7 @@ use Flarum\Post\Event\Posted;
use Flarum\Post\Event\Restored;
use Flarum\Subscriptions\HideIgnoredFromAllDiscussionsPage;
use Flarum\Subscriptions\Listener;
use Flarum\Subscriptions\Notification\FilterVisiblePostsBeforeSending;
use Flarum\Subscriptions\Notification\NewPostBlueprint;
use Flarum\Subscriptions\Query\SubscriptionFilterGambit;
@ -36,7 +37,8 @@ return [
->namespace('flarum-subscriptions', __DIR__.'/views'),
(new Extend\Notification())
->type(NewPostBlueprint::class, BasicDiscussionSerializer::class, ['alert', 'email']),
->type(NewPostBlueprint::class, BasicDiscussionSerializer::class, ['alert', 'email'])
->beforeSending(FilterVisiblePostsBeforeSending::class),
(new Extend\ApiSerializer(DiscussionSerializer::class))
->attribute('subscription', function (DiscussionSerializer $serializer, Discussion $discussion) {

View File

@ -0,0 +1,28 @@
<?php
namespace Flarum\Subscriptions\Notification;
use Flarum\Notification\Blueprint\BlueprintInterface;
class FilterVisiblePostsBeforeSending
{
public function __invoke(BlueprintInterface $blueprint, array $recipients): array
{
if ($blueprint instanceof NewPostBlueprint) {
$newRecipients = [];
// Flarum has built-in access control for the notification subject,
// but subscriptions post notifications has the discussion as the subject.
// We'll add a post visibility check so that users can't get access to hidden replies by subscribing.
foreach ($recipients as $recipient) {
if ($blueprint->post->isVisibleTo($recipient)) {
$newRecipients[] = $recipient;
}
}
return $newRecipients;
}
return $recipients;
}
}

View File

@ -10,7 +10,9 @@
namespace Flarum\Subscriptions\tests\integration\api\discussions;
use Carbon\Carbon;
use Flarum\Extend\ModelVisibility;
use Flarum\Group\Group;
use Flarum\Post\Post;
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
use Flarum\Testing\integration\TestCase;
use Flarum\User\User;
@ -278,4 +280,47 @@ class ReplyNotificationTest extends TestCase
$this->assertEquals(1, $mainUser->getUnreadNotificationCount());
}
/** @test */
public function replying_to_a_discussion_with_a_restricted_post_only_sends_notifications_to_allowed_users()
{
// Add visibility scoper to only allow admin
// to see expected new post with content containing 'restricted-test-post'.
$this->extend(
(new ModelVisibility(Post::class))
->scope(function (User $actor, $query) {
if (! $actor->isAdmin()) {
$query->where('content', 'not like', '%restricted-test-post%');
}
})
);
$this->app();
/** @var User $allowedUser */
$allowedUser = User::query()->find(1);
$normalUser = User::query()->find(2);
$this->assertEquals(0, $allowedUser->getUnreadNotificationCount());
$this->assertEquals(0, $normalUser->getUnreadNotificationCount());
$this->send(
$this->request('POST', '/api/posts', [
'authenticatedAs' => 3,
'json' => [
'data' => [
'attributes' => [
'content' => 'restricted-test-post',
],
'relationships' => [
'discussion' => ['data' => ['id' => 1]],
],
],
],
])
);
$this->assertEquals(1, $allowedUser->getUnreadNotificationCount());
$this->assertEquals(0, $normalUser->getUnreadNotificationCount());
}
}