2
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-03-04 01:15:08 +08:00
discourse/spec/requests/user_status_controller_spec.rb
Régis Hanol 20e4134a09
FEATURE: Prevent silenced users from liking and using reactions (#37040)
Silenced users can now no longer like posts or use reactions, which
closes a potential griefing vector that was difficult for moderators
to monitor.

The implementation adds a silenced check to the guardian's post_can_act?
method for likes, and introduces a new can_use_reactions? guardian method
in the discourse-reactions plugin that delegates to the same logic. This
ensures both features share the same authorization path.

Additionally, silenced users' custom status is now shadow-banned: visible
to themselves and staff, but hidden from other users.

A new `can_see_user_status?` guardian method centralizes the visibility
logic, used by serializers and MessageBus publishing. Status updates
from silenced users are now only broadcast to themselves and staff.

Also includes minor CSS fixes for user status spacing and alignment.

Chat reactions already had proper silenced user checks in place via the
can_react? guardian method, so no changes were needed there.

Ref - t/140084
2026-01-13 13:59:57 +01:00

233 lines
6.9 KiB
Ruby
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# frozen_string_literal: true
RSpec.describe UserStatusController do
describe "#get" do
it "requires user to be logged in" do
get "/user-status.json"
expect(response.status).to eq(403)
end
it "returns 404 if the feature is disabled" do
user = Fabricate(:user)
sign_in(user)
SiteSetting.enable_user_status = false
get "/user-status.json"
expect(response.status).to eq(404)
end
describe "when feature is enabled and a user is logged in" do
fab!(:user)
before do
sign_in(user)
SiteSetting.enable_user_status = true
end
it "returns user status" do
status = "off to dentist"
status_emoji = "tooth"
ends_at = "2100-01-01T18:00:00Z"
user.set_status!(status, status_emoji, DateTime.parse(ends_at))
get "/user-status.json"
expect(response.status).to eq(200)
expect(response.parsed_body["description"]).to eq(status)
expect(response.parsed_body["emoji"]).to eq(status_emoji)
expect(response.parsed_body["ends_at"]).to eq(ends_at)
end
end
end
describe "#set" do
it "requires user to be logged in" do
put "/user-status.json", params: { description: "off to dentist" }
expect(response.status).to eq(403)
end
it "returns 404 if the feature is disabled" do
user = Fabricate(:user)
sign_in(user)
SiteSetting.enable_user_status = false
put "/user-status.json", params: { description: "off" }
expect(response.status).to eq(404)
end
describe "feature is enabled and user is logged in" do
fab!(:user)
before do
sign_in(user)
SiteSetting.enable_user_status = true
end
it "the description parameter is mandatory" do
put "/user-status.json", params: { emoji: "tooth" }
expect(response.status).to eq(400)
end
it "the emoji parameter is mandatory" do
put "/user-status.json", params: { description: "off to dentist" }
expect(response.status).to eq(400)
end
it "validates emoji" do
put "/user-status.json",
params: {
emoji: "invalid_emoji_name",
description: "off to dentist",
}
expect(response.status).to eq(422)
end
it "limits descriptions length" do
put "/user-status.json",
params: {
emoji: "tooth",
description: "x" * UserStatus::MAX_DESCRIPTION_LENGTH,
}
expect(response.status).to eq(200)
put "/user-status.json",
params: {
emoji: "tooth",
description: "x" * (UserStatus::MAX_DESCRIPTION_LENGTH + 1),
}
expect(response.status).to eq(422)
end
it "sets user status" do
status = "off to dentist"
status_emoji = "tooth"
ends_at = DateTime.parse("2100-01-01 18:00")
put "/user-status.json",
params: {
description: status,
emoji: status_emoji,
ends_at: ends_at,
}
expect(response.status).to eq(200)
expect(user.user_status.description).to eq(status)
expect(user.user_status.emoji).to eq(status_emoji)
expect(user.user_status.ends_at).to eq_time(ends_at)
end
it "following calls update status" do
status = "off to dentist"
status_emoji = "tooth"
ends_at = DateTime.parse("2100-01-01 18:00")
put "/user-status.json",
params: {
description: status,
emoji: status_emoji,
ends_at: ends_at,
}
expect(response.status).to eq(200)
user.reload
expect(user.user_status.description).to eq(status)
expect(user.user_status.emoji).to eq(status_emoji)
expect(user.user_status.ends_at).to eq_time(ends_at)
new_status = "surfing"
new_status_emoji = "man_surfing"
new_ends_at = DateTime.parse("2100-01-01 18:59")
put "/user-status.json",
params: {
description: new_status,
emoji: new_status_emoji,
ends_at: new_ends_at,
}
expect(response.status).to eq(200)
user.reload
expect(user.user_status.description).to eq(new_status)
expect(user.user_status.emoji).to eq(new_status_emoji)
expect(user.user_status.ends_at).to eq_time(new_ends_at)
end
it "publishes to message bus" do
status = "off to dentist"
emoji = "tooth"
ends_at = "2100-01-01T18:00:00Z"
messages =
MessageBus.track_publish("/user-status") do
put "/user-status.json", params: { description: status, emoji: emoji, ends_at: ends_at }
end
expect(messages.map(&:channel)).to contain_exactly("/user-status")
expect(messages[0].channel).to eq("/user-status")
expect(messages[0].group_ids).to eq([Group::AUTO_GROUPS[:trust_level_0]])
expect(messages[0].data[user.id][:description]).to eq(status)
expect(messages[0].data[user.id][:emoji]).to eq(emoji)
expect(messages[0].data[user.id][:ends_at]).to eq(ends_at)
end
it "only publishes to the silenced user and staff when silenced" do
user.update!(silenced_till: 1.year.from_now)
messages =
MessageBus.track_publish("/user-status") do
put "/user-status.json", params: { description: "off to dentist", emoji: "tooth" }
end
expect(messages.size).to eq(1)
expect(messages[0].user_ids).to contain_exactly(user.id)
expect(messages[0].group_ids).to contain_exactly(Group::AUTO_GROUPS[:staff])
end
end
end
describe "#clear" do
it "requires you to be logged in" do
delete "/user-status.json"
expect(response.status).to eq(403)
end
it "returns 404 if the feature is disabled" do
user = Fabricate(:user)
sign_in(user)
SiteSetting.enable_user_status = false
delete "/user-status.json"
expect(response.status).to eq(404)
end
describe "feature is enabled and user is logged in" do
fab!(:user_status) { Fabricate(:user_status, description: "off to dentist") }
fab!(:user) { Fabricate(:user, user_status: user_status) }
before do
sign_in(user)
SiteSetting.enable_user_status = true
end
it "clears user status" do
delete "/user-status.json"
expect(response.status).to eq(200)
user.reload
expect(user.user_status).to be_nil
end
it "publishes to message bus" do
messages = MessageBus.track_publish("/user-status") { delete "/user-status.json" }
expect(messages.size).to eq(1)
expect(messages[0].channel).to eq("/user-status")
expect(messages[0].group_ids).to eq([Group::AUTO_GROUPS[:trust_level_0]])
expect(messages[0].data[user.id]).to eq(nil)
end
end
end
end