diff --git a/app/assets/javascripts/admin/addon/templates/logs.hbs b/app/assets/javascripts/admin/addon/templates/logs.hbs
index e530f3cc507..854b27d01cf 100644
--- a/app/assets/javascripts/admin/addon/templates/logs.hbs
+++ b/app/assets/javascripts/admin/addon/templates/logs.hbs
@@ -3,10 +3,12 @@
@route="adminLogs.staffActionLogs"
@label="admin.logs.staff_actions.title"
/>
-
+ {{#if this.currentUser.can_see_emails}}
+
+ {{/if}}
{
- 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");
}
diff --git a/app/controllers/admin/screened_emails_controller.rb b/app/controllers/admin/screened_emails_controller.rb
index b8688a0998d..e38175cbe26 100644
--- a/app/controllers/admin/screened_emails_controller.rb
+++ b/app/controllers/admin/screened_emails_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Admin::ScreenedEmailsController < Admin::StaffController
+ before_action :ensure_can_see_emails
+
def index
screened_emails = ScreenedEmail.limit(200).order("last_match_at desc").to_a
render_serialized(screened_emails, ScreenedEmailSerializer)
@@ -11,4 +13,8 @@ class Admin::ScreenedEmailsController < Admin::StaffController
screen.destroy!
render json: success_json
end
+
+ def ensure_can_see_emails
+ guardian.ensure_can_see_emails!
+ end
end
diff --git a/app/serializers/current_user_serializer.rb b/app/serializers/current_user_serializer.rb
index 9d9dccf0955..9ec2e31ec70 100644
--- a/app/serializers/current_user_serializer.rb
+++ b/app/serializers/current_user_serializer.rb
@@ -76,7 +76,8 @@ class CurrentUserSerializer < BasicUserSerializer
:can_view_raw_email,
:use_glimmer_topic_list?,
:login_method,
- :render_experimental_about_page
+ :render_experimental_about_page,
+ :can_see_emails
delegate :user_stat, to: :object, private: true
delegate :any_posts, :draft_count, :pending_posts_count, :read_faq?, to: :user_stat
@@ -320,4 +321,12 @@ class CurrentUserSerializer < BasicUserSerializer
def use_glimmer_topic_list?
scope.user.in_any_groups?(SiteSetting.experimental_glimmer_topic_list_groups_map)
end
+
+ def can_see_emails
+ scope.can_see_emails?
+ end
+
+ def include_can_see_emails?
+ object.staff?
+ end
end
diff --git a/lib/guardian.rb b/lib/guardian.rb
index 523b398dd0f..dbd212ebfa7 100644
--- a/lib/guardian.rb
+++ b/lib/guardian.rb
@@ -534,6 +534,7 @@ class Guardian
def can_export_entity?(entity)
return false if anonymous?
return true if is_admin?
+ return can_see_emails? if entity == "screened_email"
return entity != "user_list" if is_moderator?
# Regular users can only export their archives
@@ -544,6 +545,11 @@ class Guardian
).count == 0
end
+ def can_see_emails?
+ return true if is_admin?
+ SiteSetting.moderators_view_emails && is_moderator?
+ end
+
def can_mute_user?(target_user)
can_mute_users? && @user.id != target_user.id && !target_user.staff?
end
diff --git a/spec/requests/admin/screened_emails_controller_spec.rb b/spec/requests/admin/screened_emails_controller_spec.rb
index e8477784232..bfecb1c459a 100644
--- a/spec/requests/admin/screened_emails_controller_spec.rb
+++ b/spec/requests/admin/screened_emails_controller_spec.rb
@@ -23,8 +23,11 @@ RSpec.describe Admin::ScreenedEmailsController do
include_examples "screened emails accessible"
end
- context "when logged in as a moderator" do
- before { sign_in(moderator) }
+ context "when logged in as a moderator and has permission to view emails" do
+ before do
+ sign_in(moderator)
+ SiteSetting.moderators_view_emails = true
+ end
include_examples "screened emails accessible"
end
@@ -39,6 +42,17 @@ RSpec.describe Admin::ScreenedEmailsController do
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
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
describe "#destroy" do
@@ -58,8 +72,11 @@ RSpec.describe Admin::ScreenedEmailsController do
include_examples "screened email deletion possible"
end
- context "when logged in as a moderator" do
- before { sign_in(moderator) }
+ context "when logged in as a moderator and has permission to view emails" do
+ before do
+ sign_in(moderator)
+ SiteSetting.moderators_view_emails = true
+ end
include_examples "screened email deletion possible"
end
@@ -74,5 +91,16 @@ RSpec.describe Admin::ScreenedEmailsController do
expect(response.parsed_body["errors"]).to include(I18n.t("not_found"))
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
diff --git a/spec/requests/export_csv_controller_spec.rb b/spec/requests/export_csv_controller_spec.rb
index d55106cb60f..b54f90452ae 100644
--- a/spec/requests/export_csv_controller_spec.rb
+++ b/spec/requests/export_csv_controller_spec.rb
@@ -97,6 +97,23 @@ RSpec.describe ExportCsvController do
expect(response.status).to eq(422)
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
post "/export_csv/export_entity.json", params: { entity: "staff_action" }
expect(response.status).to eq(200)
diff --git a/spec/system/admin_sidebar_navigation_spec.rb b/spec/system/admin_sidebar_navigation_spec.rb
index 84ebdac0d99..7b9788fa227 100644
--- a/spec/system/admin_sidebar_navigation_spec.rb
+++ b/spec/system/admin_sidebar_navigation_spec.rb
@@ -274,6 +274,29 @@ describe "Admin Revamp | Sidebar Navigation", type: :system do
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 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(
[