From 79ce7085c25ff661d4c29c71de9dae2374c2dfc2 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 4 Mar 2020 11:10:23 -0500 Subject: [PATCH] SECURITY: Ensure the invite JSON API matches the UX Anonymous users could query the invite json and see counts and summaries which is not allowed in the UX of Discourse. This commit has those endpoints return a 403 unless the user is allowed to invite. --- app/controllers/users_controller.rb | 4 ++ spec/requests/users_controller_spec.rb | 61 ++++++++++++++++++++------ 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 4fd276da5b..3da6474c22 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -274,6 +274,8 @@ class UsersController < ApplicationController end def invited + guardian.ensure_can_invite_to_forum! + inviter = fetch_user_from_params(include_inactive: current_user.try(:staff?) || (current_user && SiteSetting.show_inactive_accounts)) offset = params[:offset].to_i || 0 filter_by = params[:filter] @@ -290,6 +292,8 @@ class UsersController < ApplicationController end def invited_count + guardian.ensure_can_invite_to_forum! + inviter = fetch_user_from_params(include_inactive: current_user.try(:staff?) || (current_user && SiteSetting.show_inactive_accounts)) pending_count = Invite.find_pending_invites_count(inviter) diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb index a452f4e897..18727eeca6 100644 --- a/spec/requests/users_controller_spec.rb +++ b/spec/requests/users_controller_spec.rb @@ -1429,16 +1429,47 @@ describe UsersController do end end - describe '#invited' do - it 'returns success' do + describe "#invited_count" do + it "fails for anonymous users" do user = Fabricate(:user) + get "/u/#{user.username}/invited_count.json" + expect(response.status).to eq(403) + end + + it "works for users who can see invites" do + inviter = Fabricate(:user, trust_level: 2) + sign_in(inviter) + invitee = Fabricate(:user) + invite = Fabricate(:invite, invited_by: inviter, user: invitee) + get "/u/#{user.username}/invited_count.json" + expect(response.status).to eq(200) + + json = JSON.parse(response.body) + expect(json).to be_present + expect(json['counts']).to be_present + end + end + + describe '#invited' do + it 'fails for anonymous users' do + user = Fabricate(:user) + get "/u/#{user.username}/invited.json", params: { username: user.username } + + expect(response.status).to eq(403) + end + + it 'returns success' do + user = Fabricate(:user, trust_level: 2) + sign_in(user) get "/u/#{user.username}/invited.json", params: { username: user.username } expect(response.status).to eq(200) end it 'filters by email' do - inviter = Fabricate(:user) + inviter = Fabricate(:user, trust_level: 2) + sign_in(inviter) + invitee = Fabricate(:user) Fabricate( :invite, @@ -1454,6 +1485,7 @@ describe UsersController do ) get "/u/#{inviter.username}/invited.json", params: { search: 'billybob' } + expect(response.status).to eq(200) invites = JSON.parse(response.body)['invites'] expect(invites.size).to eq(1) @@ -1461,7 +1493,9 @@ describe UsersController do end it 'filters by username' do - inviter = Fabricate(:user) + inviter = Fabricate(:user, trust_level: 2) + sign_in(inviter) + invitee = Fabricate(:user, username: 'billybob') _invite = Fabricate( :invite, @@ -1476,6 +1510,7 @@ describe UsersController do ) get "/u/#{inviter.username}/invited.json", params: { search: 'billybob' } + expect(response.status).to eq(200) invites = JSON.parse(response.body)['invites'] expect(invites.size).to eq(1) @@ -1489,19 +1524,19 @@ describe UsersController do Fabricate(:invite, invited_by: inviter) get "/u/#{user.username}/invited/pending.json" - - invites = JSON.parse(response.body)['invites'] - expect(invites).to be_empty + expect(response.status).to eq(403) end end context 'with redeemed invites' do it 'returns invites' do - inviter = Fabricate(:user) + inviter = Fabricate(:user, trust_level: 2) + sign_in(inviter) invitee = Fabricate(:user) invite = Fabricate(:invite, invited_by: inviter, user: invitee) get "/u/#{inviter.username}/invited.json" + expect(response.status).to eq(200) invites = JSON.parse(response.body)['invites'] expect(invites.size).to eq(1) @@ -1514,11 +1549,12 @@ describe UsersController do context 'with pending invites' do context 'with permission to see pending invites' do it 'returns invites' do - inviter = Fabricate(:user) + inviter = Fabricate(:user, trust_level: 2) invite = Fabricate(:invite, invited_by: inviter) sign_in(inviter) get "/u/#{inviter.username}/invited/pending.json" + expect(response.status).to eq(200) invites = JSON.parse(response.body)['invites'] expect(invites.size).to eq(1) @@ -1538,21 +1574,20 @@ describe UsersController do end get "/u/#{inviter.username}/invited/pending.json" - - json = JSON.parse(response.body)['invites'] - expect(json).to be_empty + expect(response.status).to eq(403) end end end context 'with redeemed invites' do it 'returns invites' do - _user = sign_in(Fabricate(:user)) + sign_in(Fabricate(:user, trust_level: 2)) inviter = Fabricate(:user) invitee = Fabricate(:user) invite = Fabricate(:invite, invited_by: inviter, user: invitee) get "/u/#{inviter.username}/invited.json" + expect(response.status).to eq(200) invites = JSON.parse(response.body)['invites'] expect(invites.size).to eq(1)