From 6d14d0c39b17c166f17b759c4b3e3bf1a867184f Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Sun, 11 Nov 2018 16:58:08 +1030 Subject: [PATCH] Perform visibility checks on notification subjects at the query level This will prevent a notification from being seen by a user if its subject is deleted or undergoes some kind of permission change (eg. a discussion is moved into a private tag) ref #1380 --- .../ListNotificationsController.php | 4 --- src/Notification/Notification.php | 34 +++++++++++++++++++ src/Notification/NotificationRepository.php | 1 + src/User/User.php | 1 + 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/Api/Controller/ListNotificationsController.php b/src/Api/Controller/ListNotificationsController.php index 8a5a5ef7d..b8aaeef53 100644 --- a/src/Api/Controller/ListNotificationsController.php +++ b/src/Api/Controller/ListNotificationsController.php @@ -100,10 +100,6 @@ class ListNotificationsController extends AbstractListController $areMoreResults ? null : 0 ); - $notifications = array_filter($notifications, function ($notification) { - return ! $notification->subjectModel || $notification->subject; - }); - if (in_array('subject.discussion', $include)) { $this->loadSubjectDiscussions($notifications); } diff --git a/src/Notification/Notification.php b/src/Notification/Notification.php index 33e3f98a7..4c97a93aa 100644 --- a/src/Notification/Notification.php +++ b/src/Notification/Notification.php @@ -13,7 +13,9 @@ namespace Flarum\Notification; use Carbon\Carbon; use Flarum\Database\AbstractModel; +use Flarum\Event\ScopeModelVisibility; use Flarum\User\User; +use Illuminate\Database\Eloquent\Builder; /** * Models a notification record in the database. @@ -135,6 +137,38 @@ class Notification extends AbstractModel return $this->morphTo('subject', 'subjectModel'); } + /** + * Scope the query to include only notifications whose subjects are visible + * to the given user. + * + * @param Builder $query + */ + public function scopeWhereSubjectVisibleTo(Builder $query, User $actor) + { + $query->where(function ($query) use ($actor) { + $classes = []; + + foreach (static::$subjectModels as $type => $class) { + $classes[$class][] = $type; + } + + foreach ($classes as $class => $types) { + $query->orWhere(function ($query) use ($types, $class, $actor) { + $query->whereIn('type', $types) + ->whereExists(function ($query) use ($class, $actor) { + $query->selectRaw(1) + ->from((new $class)->getTable()) + ->whereColumn('id', 'subject_id'); + + static::$dispatcher->dispatch( + new ScopeModelVisibility($class::query()->setQuery($query), $actor, 'view') + ); + }); + }); + } + }); + } + /** * Get the type-to-subject-model map. * diff --git a/src/Notification/NotificationRepository.php b/src/Notification/NotificationRepository.php index b8e2b6681..9b0e608eb 100644 --- a/src/Notification/NotificationRepository.php +++ b/src/Notification/NotificationRepository.php @@ -33,6 +33,7 @@ class NotificationRepository ->where('user_id', $user->id) ->whereIn('type', $user->getAlertableNotificationTypes()) ->where('is_deleted', false) + ->whereSubjectVisibleTo($user) ->groupBy('type', 'subject_id') ->orderByRaw('MAX(created_at) DESC') ->skip($offset) diff --git a/src/User/User.php b/src/User/User.php index cd3b7c035..323ee59f5 100644 --- a/src/User/User.php +++ b/src/User/User.php @@ -437,6 +437,7 @@ class User extends AbstractModel ->whereIn('type', $this->getAlertableNotificationTypes()) ->whereNull('read_at') ->where('is_deleted', false) + ->whereSubjectVisibleTo($this) ->get(); }