mirror of
https://github.com/discourse/discourse.git
synced 2024-11-30 05:43:41 +08:00
2c45b949ea
There is an edge case where the following occurs: 1. The user sets a bookmark reminder on a post/topic 2. The post/topic is changed to a PM before or after the reminder fires, and the notification remains unread by the user 3. The user opens their bookmark reminder notification list and they can still see the notification even though they cannot access the topic anymore There is a very low chance for information leaking here, since the only thing that could be exposed is the topic title if it changes to something sensitive. This commit filters the bookmark unread notifications by using the bookmarkable can_see? methods and also prevents sending reminder notifications for bookmarks the user can no longer see.
161 lines
4.8 KiB
Ruby
161 lines
4.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class NotificationsController < ApplicationController
|
|
requires_login
|
|
before_action :ensure_admin, only: %i[create update destroy]
|
|
before_action :set_notification, only: %i[update destroy]
|
|
|
|
INDEX_LIMIT = 50
|
|
|
|
def index
|
|
user =
|
|
if params[:username] && !params[:recent]
|
|
user_record = User.find_by(username: params[:username].to_s)
|
|
raise Discourse::NotFound if !user_record
|
|
user_record
|
|
else
|
|
current_user
|
|
end
|
|
|
|
guardian.ensure_can_see_notifications!(user)
|
|
|
|
if notification_types = params[:filter_by_types]&.split(",").presence
|
|
notification_types.map! do |type|
|
|
Notification.types[type.to_sym] ||
|
|
(raise Discourse::InvalidParameters.new("invalid notification type: #{type}"))
|
|
end
|
|
end
|
|
|
|
if params[:recent].present?
|
|
limit = fetch_limit_from_params(default: 15, max: INDEX_LIMIT)
|
|
|
|
include_reviewables = false
|
|
|
|
notifications =
|
|
Notification.prioritized_list(current_user, count: limit, types: notification_types)
|
|
# notification_types is blank for the "all notifications" user menu tab
|
|
include_reviewables = notification_types.blank? && guardian.can_see_review_queue?
|
|
|
|
if notifications.present? && !(params.has_key?(:silent) || @readonly_mode)
|
|
if current_user.bump_last_seen_notification!
|
|
current_user.reload
|
|
current_user.publish_notifications_state
|
|
end
|
|
end
|
|
|
|
if !params.has_key?(:silent) && params[:bump_last_seen_reviewable] && !@readonly_mode &&
|
|
include_reviewables
|
|
current_user_id = current_user.id
|
|
Scheduler::Defer.later "bump last seen reviewable for user" do
|
|
# we lookup current_user again in the background thread to avoid
|
|
# concurrency issues where the user object returned by the
|
|
# current_user controller method is changed by the time the deferred
|
|
# block is executed
|
|
User.find_by(id: current_user_id)&.bump_last_seen_reviewable!
|
|
end
|
|
end
|
|
|
|
notifications =
|
|
Notification.filter_inaccessible_topic_notifications(current_user.guardian, notifications)
|
|
|
|
json = {
|
|
notifications: serialize_data(notifications, NotificationSerializer),
|
|
seen_notification_id: current_user.seen_notification_id,
|
|
}
|
|
|
|
if include_reviewables
|
|
json[:pending_reviewables] = Reviewable.basic_serializers_for_list(
|
|
Reviewable.user_menu_list_for(current_user),
|
|
current_user,
|
|
).as_json
|
|
end
|
|
|
|
render_json_dump(json)
|
|
else
|
|
offset = params[:offset].to_i
|
|
|
|
notifications =
|
|
Notification.where(user_id: user.id).visible.includes(:topic).order(created_at: :desc)
|
|
|
|
notifications = notifications.where(read: true) if params[:filter] == "read"
|
|
|
|
notifications = notifications.where(read: false) if params[:filter] == "unread"
|
|
|
|
total_rows = notifications.dup.count
|
|
notifications = notifications.offset(offset).limit(60)
|
|
notifications =
|
|
Notification.filter_inaccessible_topic_notifications(current_user.guardian, notifications)
|
|
render_json_dump(
|
|
notifications: serialize_data(notifications, NotificationSerializer),
|
|
total_rows_notifications: total_rows,
|
|
seen_notification_id: user.seen_notification_id,
|
|
load_more_notifications:
|
|
notifications_path(username: user.username, offset: offset + 60, filter: params[:filter]),
|
|
)
|
|
end
|
|
end
|
|
|
|
def mark_read
|
|
if params[:id]
|
|
Notification.read(current_user, [params[:id].to_i])
|
|
else
|
|
if types = params[:dismiss_types]&.split(",").presence
|
|
invalid = []
|
|
types.map! do |type|
|
|
type_id = Notification.types[type.to_sym]
|
|
invalid << type if !type_id
|
|
type_id
|
|
end
|
|
if invalid.size > 0
|
|
raise Discourse::InvalidParameters.new("invalid notification types: #{invalid.inspect}")
|
|
end
|
|
end
|
|
|
|
Notification.read_types(current_user, types)
|
|
current_user.bump_last_seen_notification!
|
|
end
|
|
|
|
current_user.reload
|
|
current_user.publish_notifications_state
|
|
|
|
render json: success_json
|
|
end
|
|
|
|
def create
|
|
@notification = Notification.consolidate_or_create!(notification_params)
|
|
render_notification
|
|
end
|
|
|
|
def update
|
|
@notification.update!(notification_params)
|
|
render_notification
|
|
end
|
|
|
|
def destroy
|
|
@notification.destroy!
|
|
render json: success_json
|
|
end
|
|
|
|
private
|
|
|
|
def set_notification
|
|
@notification = Notification.find(params[:id])
|
|
end
|
|
|
|
def notification_params
|
|
params.permit(
|
|
:notification_type,
|
|
:user_id,
|
|
:data,
|
|
:read,
|
|
:topic_id,
|
|
:post_number,
|
|
:post_action_id,
|
|
)
|
|
end
|
|
|
|
def render_notification
|
|
render_json_dump(NotificationSerializer.new(@notification, scope: guardian, root: false))
|
|
end
|
|
end
|