mirror of
https://github.com/discourse/discourse.git
synced 2025-01-01 15:33:43 +08:00
SECURITY: Moderators cannot see user emails.
Unless `moderators_view_emails` SiteSetting is enabled, moderators should not be able to discover users’ emails.
This commit is contained in:
parent
023b61ad22
commit
95564a3df2
|
@ -3,10 +3,12 @@
|
||||||
@route="adminLogs.staffActionLogs"
|
@route="adminLogs.staffActionLogs"
|
||||||
@label="admin.logs.staff_actions.title"
|
@label="admin.logs.staff_actions.title"
|
||||||
/>
|
/>
|
||||||
<NavItem
|
{{#if this.currentUser.can_see_emails}}
|
||||||
@route="adminLogs.screenedEmails"
|
<NavItem
|
||||||
@label="admin.logs.screened_emails.title"
|
@route="adminLogs.screenedEmails"
|
||||||
/>
|
@label="admin.logs.screened_emails.title"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
<NavItem
|
<NavItem
|
||||||
@route="adminLogs.screenedIpAddresses"
|
@route="adminLogs.screenedIpAddresses"
|
||||||
@label="admin.logs.screened_ips.title"
|
@label="admin.logs.screened_ips.title"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import getURL from "discourse-common/lib/get-url";
|
import getURL from "discourse-common/lib/get-url";
|
||||||
|
|
||||||
|
export const LOGS_SCREENED_EMAILS_LINK_KEY = "admin_logs_screened_emails";
|
||||||
export const ADMIN_NAV_MAP = [
|
export const ADMIN_NAV_MAP = [
|
||||||
{
|
{
|
||||||
name: "account",
|
name: "account",
|
||||||
|
@ -236,7 +237,7 @@ export const ADMIN_NAV_MAP = [
|
||||||
icon: "up-right-from-square",
|
icon: "up-right-from-square",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "admin_logs_screened_emails",
|
name: LOGS_SCREENED_EMAILS_LINK_KEY,
|
||||||
route: "adminLogs.screenedEmails",
|
route: "adminLogs.screenedEmails",
|
||||||
label: "admin.security.sidebar_link.screened_emails",
|
label: "admin.security.sidebar_link.screened_emails",
|
||||||
icon: "envelope",
|
icon: "envelope",
|
||||||
|
|
|
@ -3,7 +3,10 @@ import { warn } from "@ember/debug";
|
||||||
import { htmlSafe } from "@ember/template";
|
import { htmlSafe } from "@ember/template";
|
||||||
import { adminRouteValid } from "discourse/lib/admin-utilities";
|
import { adminRouteValid } from "discourse/lib/admin-utilities";
|
||||||
import PreloadStore from "discourse/lib/preload-store";
|
import PreloadStore from "discourse/lib/preload-store";
|
||||||
import { ADMIN_NAV_MAP } from "discourse/lib/sidebar/admin-nav-map";
|
import {
|
||||||
|
ADMIN_NAV_MAP,
|
||||||
|
LOGS_SCREENED_EMAILS_LINK_KEY,
|
||||||
|
} from "discourse/lib/sidebar/admin-nav-map";
|
||||||
import BaseCustomSidebarPanel from "discourse/lib/sidebar/base-custom-sidebar-panel";
|
import BaseCustomSidebarPanel from "discourse/lib/sidebar/base-custom-sidebar-panel";
|
||||||
import BaseCustomSidebarSection from "discourse/lib/sidebar/base-custom-sidebar-section";
|
import BaseCustomSidebarSection from "discourse/lib/sidebar/base-custom-sidebar-section";
|
||||||
import BaseCustomSidebarSectionLink from "discourse/lib/sidebar/base-custom-sidebar-section-link";
|
import BaseCustomSidebarSectionLink from "discourse/lib/sidebar/base-custom-sidebar-section-link";
|
||||||
|
@ -407,7 +410,12 @@ export default class AdminSidebarPanel extends BaseCustomSidebarPanel {
|
||||||
|
|
||||||
if (!currentUser.admin && currentUser.moderator) {
|
if (!currentUser.admin && currentUser.moderator) {
|
||||||
navConfig.forEach((section) => {
|
navConfig.forEach((section) => {
|
||||||
section.links = section.links.filterBy("moderator");
|
section.links = section.links.filter((link) => {
|
||||||
|
if (link.name === LOGS_SCREENED_EMAILS_LINK_KEY) {
|
||||||
|
return siteSettings.moderators_view_emails;
|
||||||
|
}
|
||||||
|
return link.moderator;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
navConfig = navConfig.filterBy("links.length");
|
navConfig = navConfig.filterBy("links.length");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Admin::ScreenedEmailsController < Admin::StaffController
|
class Admin::ScreenedEmailsController < Admin::StaffController
|
||||||
|
before_action :ensure_can_see_emails
|
||||||
|
|
||||||
def index
|
def index
|
||||||
screened_emails = ScreenedEmail.limit(200).order("last_match_at desc").to_a
|
screened_emails = ScreenedEmail.limit(200).order("last_match_at desc").to_a
|
||||||
render_serialized(screened_emails, ScreenedEmailSerializer)
|
render_serialized(screened_emails, ScreenedEmailSerializer)
|
||||||
|
@ -11,4 +13,8 @@ class Admin::ScreenedEmailsController < Admin::StaffController
|
||||||
screen.destroy!
|
screen.destroy!
|
||||||
render json: success_json
|
render json: success_json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ensure_can_see_emails
|
||||||
|
guardian.ensure_can_see_emails!
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -77,7 +77,8 @@ class CurrentUserSerializer < BasicUserSerializer
|
||||||
:can_view_raw_email,
|
:can_view_raw_email,
|
||||||
:use_glimmer_topic_list?,
|
:use_glimmer_topic_list?,
|
||||||
:login_method,
|
:login_method,
|
||||||
:has_unseen_features
|
:has_unseen_features,
|
||||||
|
:can_see_emails
|
||||||
|
|
||||||
delegate :user_stat, to: :object, private: true
|
delegate :user_stat, to: :object, private: true
|
||||||
delegate :any_posts, :draft_count, :pending_posts_count, :read_faq?, to: :user_stat
|
delegate :any_posts, :draft_count, :pending_posts_count, :read_faq?, to: :user_stat
|
||||||
|
@ -329,4 +330,12 @@ class CurrentUserSerializer < BasicUserSerializer
|
||||||
def do_not_disturb_channel_position
|
def do_not_disturb_channel_position
|
||||||
MessageBus.last_id("/do-not-disturb/#{object.id}")
|
MessageBus.last_id("/do-not-disturb/#{object.id}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_see_emails
|
||||||
|
scope.can_see_emails?
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_can_see_emails?
|
||||||
|
object.staff?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -539,6 +539,7 @@ class Guardian
|
||||||
def can_export_entity?(entity)
|
def can_export_entity?(entity)
|
||||||
return false if anonymous?
|
return false if anonymous?
|
||||||
return true if is_admin?
|
return true if is_admin?
|
||||||
|
return can_see_emails? if entity == "screened_email"
|
||||||
return entity != "user_list" if is_moderator?
|
return entity != "user_list" if is_moderator?
|
||||||
|
|
||||||
# Regular users can only export their archives
|
# Regular users can only export their archives
|
||||||
|
@ -549,6 +550,11 @@ class Guardian
|
||||||
).count == 0
|
).count == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_see_emails?
|
||||||
|
return true if is_admin?
|
||||||
|
SiteSetting.moderators_view_emails && is_moderator?
|
||||||
|
end
|
||||||
|
|
||||||
def can_mute_user?(target_user)
|
def can_mute_user?(target_user)
|
||||||
can_mute_users? && @user.id != target_user.id && !target_user.staff?
|
can_mute_users? && @user.id != target_user.id && !target_user.staff?
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,8 +23,11 @@ RSpec.describe Admin::ScreenedEmailsController do
|
||||||
include_examples "screened emails accessible"
|
include_examples "screened emails accessible"
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when logged in as a moderator" do
|
context "when logged in as a moderator and has permission to view emails" do
|
||||||
before { sign_in(moderator) }
|
before do
|
||||||
|
sign_in(moderator)
|
||||||
|
SiteSetting.moderators_view_emails = true
|
||||||
|
end
|
||||||
|
|
||||||
include_examples "screened emails accessible"
|
include_examples "screened emails accessible"
|
||||||
end
|
end
|
||||||
|
@ -39,6 +42,17 @@ RSpec.describe Admin::ScreenedEmailsController do
|
||||||
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
|
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when logged in as a moderator but no permission to view emails" do
|
||||||
|
before { sign_in(moderator) }
|
||||||
|
|
||||||
|
it "denies access with a 403 response" do
|
||||||
|
get "/admin/logs/screened_emails.json"
|
||||||
|
|
||||||
|
expect(response.status).to eq(403)
|
||||||
|
expect(response.parsed_body["errors"]).to include(I18n.t("invalid_access"))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#destroy" do
|
describe "#destroy" do
|
||||||
|
@ -58,8 +72,11 @@ RSpec.describe Admin::ScreenedEmailsController do
|
||||||
include_examples "screened email deletion possible"
|
include_examples "screened email deletion possible"
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when logged in as a moderator" do
|
context "when logged in as a moderator and has permission to view emails" do
|
||||||
before { sign_in(moderator) }
|
before do
|
||||||
|
sign_in(moderator)
|
||||||
|
SiteSetting.moderators_view_emails = true
|
||||||
|
end
|
||||||
|
|
||||||
include_examples "screened email deletion possible"
|
include_examples "screened email deletion possible"
|
||||||
end
|
end
|
||||||
|
@ -74,5 +91,16 @@ RSpec.describe Admin::ScreenedEmailsController do
|
||||||
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
|
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when logged in as a moderator but no permission to view emails" do
|
||||||
|
before { sign_in(moderator) }
|
||||||
|
|
||||||
|
it "prevents deletion with a 403 response" do
|
||||||
|
delete "/admin/logs/screened_emails/#{screened_email.id}.json"
|
||||||
|
|
||||||
|
expect(response.status).to eq(403)
|
||||||
|
expect(response.parsed_body["errors"]).to include(I18n.t("invalid_access"))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -97,6 +97,23 @@ RSpec.describe ExportCsvController do
|
||||||
expect(response.status).to eq(422)
|
expect(response.status).to eq(422)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "does not allow moderators to export screened_email if they has no permission to view emails" do
|
||||||
|
SiteSetting.moderators_view_emails = false
|
||||||
|
post "/export_csv/export_entity.json", params: { entity: "screened_email" }
|
||||||
|
expect(response.status).to eq(422)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "allows moderator to export screened_email if they has permission to view emails" do
|
||||||
|
SiteSetting.moderators_view_emails = true
|
||||||
|
post "/export_csv/export_entity.json", params: { entity: "screened_email" }
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(response.parsed_body["success"]).to eq("OK")
|
||||||
|
|
||||||
|
job_data = Jobs::ExportCsvFile.jobs.first["args"].first
|
||||||
|
expect(job_data["entity"]).to eq("screened_email")
|
||||||
|
expect(job_data["user_id"]).to eq(moderator.id)
|
||||||
|
end
|
||||||
|
|
||||||
it "allows moderator to export other entities" do
|
it "allows moderator to export other entities" do
|
||||||
post "/export_csv/export_entity.json", params: { entity: "staff_action" }
|
post "/export_csv/export_entity.json", params: { entity: "staff_action" }
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
|
|
|
@ -292,6 +292,29 @@ describe "Admin Revamp | Sidebar Navigation", type: :system do
|
||||||
"What's New",
|
"What's New",
|
||||||
"All",
|
"All",
|
||||||
"Watched Words",
|
"Watched Words",
|
||||||
|
"Screened IPs",
|
||||||
|
"Screened URLs",
|
||||||
|
"Search Logs",
|
||||||
|
"Staff Action Logs",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "displays limited links for moderator with screened emails if allowed" do
|
||||||
|
SiteSetting.moderators_view_emails = true
|
||||||
|
sign_in(moderator)
|
||||||
|
visit("/admin")
|
||||||
|
|
||||||
|
sidebar.toggle_all_sections
|
||||||
|
|
||||||
|
links = page.all(".sidebar-section-link-content-text")
|
||||||
|
expect(links.map(&:text)).to eq(
|
||||||
|
[
|
||||||
|
"Dashboard",
|
||||||
|
"Users",
|
||||||
|
"What's New",
|
||||||
|
"All",
|
||||||
|
"Watched Words",
|
||||||
"Screened Emails",
|
"Screened Emails",
|
||||||
"Screened IPs",
|
"Screened IPs",
|
||||||
"Screened URLs",
|
"Screened URLs",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user