diff --git a/app/controllers/votes_controller.rb b/app/controllers/votes_controller.rb index c270bcb1a..9a171907f 100644 --- a/app/controllers/votes_controller.rb +++ b/app/controllers/votes_controller.rb @@ -7,7 +7,14 @@ def create post = Post.find(params[:post_id]) if post.user == current_user && !SiteSetting['AllowSelfVotes'] - render(json: { status: 'failed', message: 'You may not vote on your own posts.' }, status: :forbidden) + render(json: { status: 'failed', message: I18n.t('votes.limits.own_post') }, + status: :forbidden) + return + end + + unless current_user.can_vote_on?(post) + render(json: { status: 'failed', message: I18n.t('votes.limits.restricted_ability') }, + status: :forbidden) return end diff --git a/app/models/user.rb b/app/models/user.rb index 67f7dd5f0..769d53983 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,3 +1,4 @@ +# rubocop:disable Metrics/ClassLength class User < ApplicationRecord include EmailValidations include UsernameValidations @@ -99,6 +100,15 @@ def post_privilege?(name, post) post.user == self || privilege?(name) end + # Can the user vote on this post? + # This post does not check for the 'AllowSelfVotes' site setting, only + # whether this user has the ability to vote on this post. + # @param post [Post] to check + # @return [Boolean] check result + def can_vote_on?(post) + privilege?('unrestricted') || owns_post_or_parent?(post) + end + # Can the user rename a given comment thread? # @param thread [CommentThread] thread to rename # @return [Boolean] check result @@ -551,3 +561,4 @@ def votes_by_post_type votes.joins(:post).group(Arel.sql('posts.post_type_id')).count(Arel.sql('posts.post_type_id')) end end +# rubocop:enable Metrics/ClassLength diff --git a/config/locales/strings/en.votes.yml b/config/locales/strings/en.votes.yml index 485ef2f6f..4bd316b3e 100644 --- a/config/locales/strings/en.votes.yml +++ b/config/locales/strings/en.votes.yml @@ -1,4 +1,7 @@ en: votes: summary: - post_missing: 'Post not found' \ No newline at end of file + post_missing: 'Post not found' + limits: + own_post: 'You may not vote on your own posts.' + restricted_ability: 'You must have the Participate Everywhere ability to vote on this post.' \ No newline at end of file diff --git a/test/controllers/votes_controller_test.rb b/test/controllers/votes_controller_test.rb index aabc1df04..c328c5521 100644 --- a/test/controllers/votes_controller_test.rb +++ b/test/controllers/votes_controller_test.rb @@ -6,7 +6,7 @@ class VotesControllerTest < ActionController::TestCase test 'should cast upvote' do sign_in users(:standard_user) - post :create, params: { post_id: posts(:question_without_votes).id, vote_type: 1 } + try_create_vote posts(:question_without_votes), 1 assert_json_success end @@ -14,29 +14,29 @@ class VotesControllerTest < ActionController::TestCase test 'should cast downvote' do sign_in users(:standard_user) - post :create, params: { post_id: posts(:question_without_votes).id, vote_type: -1 } + try_create_vote posts(:question_without_votes), -1 assert_json_success end test 'should return correct modified status' do - post_id = posts(:question_without_votes).id + post = posts(:question_without_votes) sign_in users(:standard_user) - post :create, params: { post_id: post_id, vote_type: 1 } - post :create, params: { post_id: post_id, vote_type: -1 } + try_create_vote post, 1 + try_create_vote post, 1 assert_json_success(status: 'modified') end test 'should silently accept duplicate votes' do - post_id = posts(:question_without_votes).id + post = posts(:question_without_votes) sign_in users(:standard_user) - post :create, params: { post_id: post_id, vote_type: 1 } - post :create, params: { post_id: post_id, vote_type: 1 } + try_create_vote post, 1 + try_create_vote post, 1 assert_json_success(status: 'modified') end @@ -44,7 +44,7 @@ class VotesControllerTest < ActionController::TestCase test 'should prevent self voting' do sign_in users(:editor) - post :create, params: { post_id: posts(:question_without_votes).id, vote_type: 1 } + try_create_vote posts(:question_without_votes), 1 assert_response(:forbidden) assert_valid_json_response @@ -92,7 +92,7 @@ class VotesControllerTest < ActionController::TestCase test 'should prevent deleted account casting votes' do sign_in users(:deleted_account) - post :create, params: { post_id: posts(:question_without_votes).id, vote_type: 1 } + try_create_vote posts(:question_without_votes), 1 assert_response(:forbidden) assert_valid_json_response @@ -102,10 +102,27 @@ class VotesControllerTest < ActionController::TestCase test 'should prevent deleted profile casting votes' do sign_in users(:deleted_profile) - post :create, params: { post_id: posts(:question_without_votes).id, vote_type: 1 } + try_create_vote posts(:question_without_votes), 1 assert_response(:forbidden) assert_valid_json_response assert_json_response_message('You must be logged in to vote.') end + + test 'should not allow restricted users to vote' do + sign_in users(:basic_user) + + try_create_vote posts(:question_without_votes), 1 + + assert_response(:forbidden) + assert_valid_json_response + assert_json_response_message(I18n.t('votes.limits.restricted_ability')) + end + + private + + def try_create_vote(target_post, vote_type) + post :create, params: { post_id: target_post.id, + vote_type: vote_type } + end end