diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1eced5531a..275c6ee656 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -269,13 +269,17 @@ def access_denied(options ={}) def admin_only_access_denied respond_to do |format| format.html do - flash[:error] = ts("Sorry, only an authorized admin can access the page you were trying to reach.") + flash[:error] = t("admin.access.page_access_denied") redirect_to root_path end format.json do - errors = [ts("Sorry, only an authorized admin can do that.")] + errors = [t("admin.access.action_access_denied")] render json: { errors: errors }, status: :forbidden end + format.js do + flash[:error] = t("admin.access.page_access_denied") + render js: "window.location.href = '#{root_path}';" + end end end diff --git a/app/controllers/inbox_controller.rb b/app/controllers/inbox_controller.rb index fb7a710f7e..4191691060 100644 --- a/app/controllers/inbox_controller.rb +++ b/app/controllers/inbox_controller.rb @@ -2,7 +2,7 @@ class InboxController < ApplicationController include BlockHelper before_action :load_user - before_action :check_ownership + before_action :check_ownership_or_admin before_action :load_commentable, only: :reply before_action :check_blocked, only: :reply @@ -13,6 +13,7 @@ def load_user end def show + authorize InboxComment if logged_in_as_admin? @inbox_total = @user.inbox_comments.with_bad_comments_removed.count @unread = @user.inbox_comments.with_bad_comments_removed.count_unread @filters = filter_params[:filters] || {} @@ -30,6 +31,7 @@ def reply end def update + authorize InboxComment if logged_in_as_admin? begin @inbox_comments = InboxComment.find(params[:inbox_comments]) if params[:read] diff --git a/app/policies/inbox_comment_policy.rb b/app/policies/inbox_comment_policy.rb new file mode 100644 index 0000000000..8ec7d755b3 --- /dev/null +++ b/app/policies/inbox_comment_policy.rb @@ -0,0 +1,7 @@ +class InboxCommentPolicy < ApplicationPolicy + VIEW_INBOX_ROLES = %w[superadmin policy_and_abuse].freeze + + def show? + user_has_roles?(VIEW_INBOX_ROLES) + end +end diff --git a/app/views/users/_sidebar.html.erb b/app/views/users/_sidebar.html.erb index 429c8ebcc9..a6a8a0295d 100644 --- a/app/views/users/_sidebar.html.erb +++ b/app/views/users/_sidebar.html.erb @@ -1,30 +1,30 @@
" role="navigation region"> -

<%= ts("Choices")%>

+

<%= t(".landmark.choices") %>

-

<%= ts("Pitch")%>

+

<%= t(".landmark.pitch") %>

-<% if @user == current_user %> -

<%= ts("Catch")%>

+<% if @user == current_user || policy(:inbox_comment).show? %> +

<%= t(".landmark.catch") %>

<% end %> -

<%= ts("Switch")%>

+

<%= t(".landmark.switch") %>

diff --git a/config/locales/controllers/en.yml b/config/locales/controllers/en.yml index 4f8ccb303d..5b6fade5c9 100644 --- a/config/locales/controllers/en.yml +++ b/config/locales/controllers/en.yml @@ -1,6 +1,9 @@ --- en: admin: + access: + action_access_denied: Sorry, only an authorized admin can do that. + page_access_denied: Sorry, only an authorized admin can access the page you were trying to reach. admin_invitations: find: user_not_found: No results were found. Try another search. diff --git a/config/locales/views/en.yml b/config/locales/views/en.yml index 96608a413e..91965d6384 100644 --- a/config/locales/views/en.yml +++ b/config/locales/views/en.yml @@ -1791,6 +1791,34 @@ en: privacy_policy: Privacy Policy tos: Terms of Service welcome_html: Hi! It looks like you've just logged in to AO3 for the first time. For help getting started on AO3, check out some %{new_user_tips_link} or browse through %{our_faqs_link}. + sidebar: + catch: + history: History + inbox: Inbox (%{inbox_number}) + statistics: Statistics + subscriptions: Subscriptions + choices: + all_pseuds: All Pseuds (%{pseud_number}) + dashboard: Dashboard + preferences: Preferences + profile: Profile + pseud_switcher: Pseud Switcher + pseuds: Pseuds + skins: Skins + landmark: + catch: Catch + choices: Choices + pitch: Pitch + switch: Switch + pitch: + collections: Collections (%{coll_number}) + drafts: Drafts (%{drafts_number}) + switch: + assignments: Assignments (%{assignment_number}) + claims: Claims (%{claim_number}) + co_creator_requests: Co-Creator Requests (%{count}) + related_works: Related Works (%{related_works_number}) + sign_ups: Sign-ups (%{signup_number}) works: adult: caution: This work could have adult content. If you continue, you have agreed that you are willing to see such content. diff --git a/spec/controllers/inbox_controller_spec.rb b/spec/controllers/inbox_controller_spec.rb index 21815b1bbd..9e29317db9 100644 --- a/spec/controllers/inbox_controller_spec.rb +++ b/spec/controllers/inbox_controller_spec.rb @@ -19,6 +19,111 @@ "Sorry, you don't have permission to access the page you were trying to reach.") end + context "when logged in as an admin" do + context "when the admin does not have the correct authorization" do + context "when the admin has no role" do + let(:admin) { create(:admin, roles: []) } + + before { fake_login_admin(admin) } + + it "redirects with error" do + get :show, params: { user_id: user.login } + + it_redirects_to_with_error(root_path, "Sorry, only an authorized admin can access the page you were trying to reach.") + end + end + + (Admin::VALID_ROLES - %w[superadmin policy_and_abuse]).each do |role| + context "when the admin has the #{role} role" do + let(:admin) { create(:admin, roles: [role]) } + + before { fake_login_admin(admin) } + + it "redirects with error" do + get :show, params: { user_id: user.login } + + it_redirects_to_with_error(root_path, "Sorry, only an authorized admin can access the page you were trying to reach.") + end + end + end + end + + %w[superadmin policy_and_abuse].each do |role| + context "when the admin is authorized with the #{role} role" do + let(:admin) { create(:admin, roles: [role]) } + + before { fake_login_admin(admin) } + + it "renders the user inbox" do + get :show, params: { user_id: user.login } + expect(response).to render_template("show") + expect(assigns(:inbox_total)).to eq(0) + expect(assigns(:unread)).to eq(0) + end + + context "with unread comments" do + let!(:inbox_comments) do + Array.new(3) do |i| + create(:inbox_comment, user: user, created_at: Time.now.utc + i.days) + end + end + + it "renders non-zero unread count" do + get :show, params: { user_id: user.login } + expect(assigns(:inbox_comments)).to eq(inbox_comments.reverse) + expect(assigns(:inbox_total)).to eq(3) + expect(assigns(:unread)).to eq(3) + end + + it "renders oldest first" do + get :show, params: { user_id: user.login, filters: { date: "asc" } } + expect(assigns(:filters)[:date]).to eq("asc") + expect(assigns(:inbox_comments)).to eq(inbox_comments) + expect(assigns(:inbox_total)).to eq(3) + expect(assigns(:unread)).to eq(3) + end + end + + context "with 1 read and 1 unread" do + let!(:read_comment) { create(:inbox_comment, user: user, read: true) } + let!(:unread_comment) { create(:inbox_comment, user: user) } + + it "renders only unread" do + get :show, params: { user_id: user.login, filters: { read: "false" } } + expect(assigns(:filters)[:read]).to eq("false") + expect(assigns(:inbox_comments)).to eq([unread_comment]) + expect(assigns(:inbox_total)).to eq(2) + expect(assigns(:unread)).to eq(1) + end + end + + context "with 1 replied and 1 unreplied" do + let!(:replied_comment) { create(:inbox_comment, user: user, replied_to: true) } + let!(:unreplied_comment) { create(:inbox_comment, user: user) } + + it "renders only unreplied" do + get :show, params: { user_id: user.login, filters: { replied_to: "false" } } + expect(assigns(:filters)[:replied_to]).to eq("false") + expect(assigns(:inbox_comments)).to eq([unreplied_comment]) + expect(assigns(:inbox_total)).to eq(2) + expect(assigns(:unread)).to eq(2) + end + end + + context "with a deleted comment" do + let(:inbox_comment) { create(:inbox_comment, user: user) } + + it "excludes deleted comments" do + inbox_comment.feedback_comment.destroy! + get :show, params: { user_id: user.login } + expect(assigns(:inbox_total)).to eq(0) + expect(assigns(:unread)).to eq(0) + end + end + end + end + end + context "when logged in as the same user" do before { fake_login_known_user(user) } @@ -82,7 +187,7 @@ let(:inbox_comment) { create(:inbox_comment, user: user) } it "excludes deleted comments" do - inbox_comment.feedback_comment.destroy + inbox_comment.feedback_comment.destroy! get :show, params: { user_id: user.login } expect(assigns(:inbox_total)).to eq(0) expect(assigns(:unread)).to eq(0) @@ -149,92 +254,107 @@ end describe "PUT #update" do - before { fake_login_known_user(user) } - - context "with no comments selected" do - it "redirects to inbox with caution and a notice" do - put :update, params: { user_id: user.login, read: "yeah" } - it_redirects_to_with_caution_and_notice(user_inbox_path(user), - "Please select something first", - "Inbox successfully updated.") - end - - it "redirects to the previously viewed page if HTTP_REFERER is set, with a caution and a notice" do - @request.env['HTTP_REFERER'] = root_path - put :update, params: { user_id: user.login, read: "yeah" } - it_redirects_to_with_caution_and_notice(root_path, - "Please select something first", - "Inbox successfully updated.") + %w[superadmin policy_and_abuse].each do |role| + context "when logged in as an admin with the role #{role}" do + let(:admin) { create(:admin, roles: [role]) } + + before { fake_login_admin(admin) } + + it "redirects to root with error" do + put :update, params: { user_id: user.login } + it_redirects_to_with_error(root_path, "Sorry, only an authorized admin can access the page you were trying to reach.") + end end end - context "with unread comments" do - let!(:inbox_comment_1) { create(:inbox_comment, user: user) } - let!(:inbox_comment_2) { create(:inbox_comment, user: user) } - - it "marks all as read and redirects to inbox with a notice" do - parameters = { - user_id: user.login, - inbox_comments: [inbox_comment_1.id, inbox_comment_2.id], - read: "yeah" - } + context "when logged in as the comment receiver" do + before { fake_login_known_user(user) } - put :update, params: parameters - it_redirects_to_with_notice(user_inbox_path(user), "Inbox successfully updated.") + context "with no comments selected" do + it "redirects to inbox with caution and a notice" do + put :update, params: { user_id: user.login, read: "yeah" } + it_redirects_to_with_caution_and_notice(user_inbox_path(user), + "Please select something first", + "Inbox successfully updated.") + end - inbox_comment_1.reload - expect(inbox_comment_1.read).to be_truthy - inbox_comment_2.reload - expect(inbox_comment_2.read).to be_truthy + it "redirects to the previously viewed page if HTTP_REFERER is set, with a caution and a notice" do + @request.env["HTTP_REFERER"] = root_path + put :update, params: { user_id: user.login, read: "yeah" } + it_redirects_to_with_caution_and_notice(root_path, + "Please select something first", + "Inbox successfully updated.") + end end - it "marks one as read and redirects to inbox with a notice" do - put :update, params: { user_id: user.login, inbox_comments: [inbox_comment_1.id], read: "yeah" } - it_redirects_to_with_notice(user_inbox_path(user), "Inbox successfully updated.") + context "with unread comments" do + let!(:inbox_comment1) { create(:inbox_comment, user: user) } + let!(:inbox_comment2) { create(:inbox_comment, user: user) } + + it "marks all as read and redirects to inbox with a notice" do + parameters = { + user_id: user.login, + inbox_comments: [inbox_comment1.id, inbox_comment2.id], + read: "yeah" + } + + put :update, params: parameters + it_redirects_to_with_notice(user_inbox_path(user), "Inbox successfully updated.") + + inbox_comment1.reload + expect(inbox_comment1.read).to be_truthy + inbox_comment2.reload + expect(inbox_comment2.read).to be_truthy + end - inbox_comment_1.reload - expect(inbox_comment_1.read).to be_truthy - inbox_comment_2.reload - expect(inbox_comment_2.read).to be_falsy - end + it "marks one as read and redirects to inbox with a notice" do + put :update, params: { user_id: user.login, inbox_comments: [inbox_comment1.id], read: "yeah" } + it_redirects_to_with_notice(user_inbox_path(user), "Inbox successfully updated.") - it "deletes one and redirects to inbox with a notice" do - put :update, params: { user_id: user.login, inbox_comments: [inbox_comment_1.id], delete: "yeah" } - it_redirects_to_with_notice(user_inbox_path(user), "Inbox successfully updated.") + inbox_comment1.reload + expect(inbox_comment1.read).to be_truthy + inbox_comment2.reload + expect(inbox_comment2.read).to be_falsy + end - expect(InboxComment.find_by(id: inbox_comment_1.id)).to be_nil - inbox_comment_2.reload - expect(inbox_comment_2.read).to be_falsy + it "deletes one and redirects to inbox with a notice" do + put :update, params: { user_id: user.login, inbox_comments: [inbox_comment1.id], delete: "yeah" } + it_redirects_to_with_notice(user_inbox_path(user), "Inbox successfully updated.") + + expect(InboxComment.find_by(id: inbox_comment1.id)).to be_nil + inbox_comment2.reload + expect(inbox_comment2.read).to be_falsy + end end - end - context "with a read comment and redirects to inbox with a notice" do - let!(:inbox_comment) { create(:inbox_comment, user: user, read: true) } + context "with a read comment and redirects to inbox with a notice" do + let!(:inbox_comment) { create(:inbox_comment, user: user, read: true) } - it "marks as unread and redirects to inbox with a notice" do - put :update, params: { user_id: user.login, inbox_comments: [inbox_comment.id], unread: "yeah" } - it_redirects_to_with_notice(user_inbox_path(user), "Inbox successfully updated.") + it "marks as unread and redirects to inbox with a notice" do + put :update, params: { user_id: user.login, inbox_comments: [inbox_comment.id], unread: "yeah" } + it_redirects_to_with_notice(user_inbox_path(user), "Inbox successfully updated.") - inbox_comment.reload - expect(inbox_comment.read).to be_falsy - end + inbox_comment.reload + expect(inbox_comment.read).to be_falsy + end - it "marks as unread and returns a JSON response" do - parameters = { - user_id: user.login, - inbox_comments: [inbox_comment.id], - unread: "yeah", - format: "json" - } + it "marks as unread and returns a JSON response" do + parameters = { + user_id: user.login, + inbox_comments: [inbox_comment.id], + unread: "yeah", + format: "json" + } - put :update, params: parameters + put :update, params: parameters - inbox_comment.reload - expect(inbox_comment.read).to be_falsy + inbox_comment.reload + expect(inbox_comment.read).to be_falsy - parsed_body = JSON.parse(response.body, symbolize_names: true) - expect(parsed_body[:item_success_message]).to eq("Inbox successfully updated.") - expect(response).to have_http_status(:success) + parsed_body = JSON.parse(response.body, symbolize_names: true) + expect(parsed_body[:item_success_message]).to eq("Inbox successfully updated.") + expect(response).to have_http_status(:success) + end end end end