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
This commit is contained in:
Toby Zerner 2018-11-11 16:58:08 +10:30
parent 17fdc0ebe0
commit 6d14d0c39b
4 changed files with 36 additions and 4 deletions

View File

@ -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);
}

View File

@ -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.
*

View File

@ -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)

View File

@ -437,6 +437,7 @@ class User extends AbstractModel
->whereIn('type', $this->getAlertableNotificationTypes())
->whereNull('read_at')
->where('is_deleted', false)
->whereSubjectVisibleTo($this)
->get();
}