require 'rails_helper' require_dependency 'single_sign_on' describe Admin::UsersController do it 'is a subclass of AdminController' do expect(Admin::UsersController < Admin::AdminController).to eq(true) end context 'while logged in as an admin' do before do @user = log_in(:admin) end context '#index' do it 'returns success' do get :index, format: :json expect(response).to be_success end it 'returns JSON' do get :index, format: :json expect(::JSON.parse(response.body)).to be_present end context 'when showing emails' do it "returns email for all the users" do get :index, params: { show_emails: "true" }, format: :json data = ::JSON.parse(response.body) data.each do |user| expect(user["email"]).to be_present end end it "logs only 1 enty" do expect(UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: @user.id).count).to eq(0) get :index, params: { show_emails: "true" }, format: :json expect(UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: @user.id).count).to eq(1) end end end describe '#show' do context 'an existing user' do it 'returns success' do get :show, params: { id: @user.id }, format: :json expect(response).to be_success end end context 'an existing user' do it 'returns success' do get :show, params: { id: 0 }, format: :json expect(response).not_to be_success end end end context '#approve_bulk' do let(:evil_trout) { Fabricate(:evil_trout) } it "does nothing without uesrs" do User.any_instance.expects(:approve).never put :approve_bulk, format: :json end it "won't approve the user when not allowed" do Guardian.any_instance.expects(:can_approve?).with(evil_trout).returns(false) User.any_instance.expects(:approve).never put :approve_bulk, params: { users: [evil_trout.id] }, format: :json end it "approves the user when permitted" do Guardian.any_instance.expects(:can_approve?).with(evil_trout).returns(true) User.any_instance.expects(:approve).once put :approve_bulk, params: { users: [evil_trout.id] }, format: :json end end context '#generate_api_key' do let(:evil_trout) { Fabricate(:evil_trout) } it 'calls generate_api_key' do User.any_instance.expects(:generate_api_key).with(@user) post :generate_api_key, params: { user_id: evil_trout.id }, format: :json end end context '#revoke_api_key' do let(:evil_trout) { Fabricate(:evil_trout) } it 'calls revoke_api_key' do User.any_instance.expects(:revoke_api_key) delete :revoke_api_key, params: { user_id: evil_trout.id }, format: :json end end context '#approve' do let(:evil_trout) { Fabricate(:evil_trout) } it "raises an error when the user doesn't have permission" do Guardian.any_instance.expects(:can_approve?).with(evil_trout).returns(false) put :approve, params: { user_id: evil_trout.id }, format: :json expect(response).to be_forbidden end it 'calls approve' do User.any_instance.expects(:approve).with(@user) put :approve, params: { user_id: evil_trout.id }, format: :json end end context '#suspend' do let(:user) { Fabricate(:evil_trout) } it "works properly" do Fabricate(:api_key, user: user) expect(user).not_to be_suspended put( :suspend, params: { user_id: user.id, suspend_until: 5.hours.from_now, reason: "because I said so", format: :json } ) expect(response).to be_success user.reload expect(user).to be_suspended expect(user.suspended_at).to be_present expect(user.suspended_till).to be_present expect(ApiKey.where(user_id: user.id).count).to eq(0) log = UserHistory.where(target_user_id: user.id).order('id desc').first expect(log).to be_present expect(log.details).to match(/because I said so/) end context "with an associated post" do let(:post) { Fabricate(:post) } let(:suspend_params) do { user_id: user.id, suspend_until: 5.hours.from_now, reason: "because of this post", post_id: post.id, format: :json } end it "can have an associated post" do put(:suspend, params: suspend_params) expect(response).to be_success log = UserHistory.where(target_user_id: user.id).order('id desc').first expect(log).to be_present expect(log.post_id).to eq(post.id) end it "can delete an associated post" do put(:suspend, params: suspend_params.merge(post_action: 'delete')) post.reload expect(post.deleted_at).to be_present expect(response).to be_success end it "can edit an associated post" do put(:suspend, params: suspend_params.merge( post_action: 'edit', post_edit: 'this is the edited content' )) post.reload expect(post.deleted_at).to be_blank expect(post.raw).to eq("this is the edited content") expect(response).to be_success end end it "can send a message to the user" do Jobs.expects(:enqueue).with( :critical_user_email, has_entries( type: :account_suspended, user_id: user.id ) ) put( :suspend, params: { user_id: user.id, suspend_until: 10.days.from_now, reason: "short reason", message: "long reason", format: :json } ) expect(response).to be_success log = UserHistory.where(target_user_id: user.id).order('id desc').first expect(log).to be_present expect(log.details).to match(/short reason/) expect(log.details).to match(/long reason/) end it "also revoke any api keys" do User.any_instance.expects(:revoke_api_key) put :suspend, params: { user_id: user.id }, format: :json end end context '#revoke_admin' do before do @another_admin = Fabricate(:admin) end it 'raises an error unless the user can revoke access' do Guardian.any_instance.expects(:can_revoke_admin?).with(@another_admin).returns(false) put :revoke_admin, params: { user_id: @another_admin.id }, format: :json expect(response).to be_forbidden end it 'updates the admin flag' do put :revoke_admin, params: { user_id: @another_admin.id }, format: :json @another_admin.reload expect(@another_admin).not_to be_admin end end context '#grant_admin' do before do @another_user = Fabricate(:coding_horror) end after do $redis.flushall end it "raises an error when the user doesn't have permission" do Guardian.any_instance.expects(:can_grant_admin?).with(@another_user).returns(false) put :grant_admin, params: { user_id: @another_user.id }, format: :json expect(response).to be_forbidden end it "returns a 404 if the username doesn't exist" do put :grant_admin, params: { user_id: 123123 }, format: :json expect(response).to be_forbidden end it 'updates the admin flag' do expect(AdminConfirmation.exists_for?(@another_user.id)).to eq(false) put :grant_admin, params: { user_id: @another_user.id }, format: :json expect(AdminConfirmation.exists_for?(@another_user.id)).to eq(true) end end context '#add_group' do let(:user) { Fabricate(:user) } let(:group) { Fabricate(:group) } it 'adds the user to the group' do post :add_group, params: { group_id: group.id, user_id: user.id }, format: :json expect(response).to be_success expect(GroupUser.where(user_id: user.id, group_id: group.id).exists?).to eq(true) group_history = GroupHistory.last expect(group_history.action).to eq(GroupHistory.actions[:add_user_to_group]) expect(group_history.acting_user).to eq(@user) expect(group_history.target_user).to eq(user) # Doing it again doesn't raise an error post :add_group, params: { group_id: group.id, user_id: user.id }, format: :json expect(response).to be_success end end context '#primary_group' do let(:group) { Fabricate(:group) } before do @another_user = Fabricate(:coding_horror) end it "raises an error when the user doesn't have permission" do Guardian.any_instance.expects(:can_change_primary_group?).with(@another_user).returns(false) put :primary_group, params: { user_id: @another_user.id }, format: :json expect(response).to be_forbidden end it "returns a 404 if the user doesn't exist" do put :primary_group, params: { user_id: 123123 }, format: :json expect(response).to be_forbidden end it "changes the user's primary group" do group.add(@another_user) put :primary_group, params: { user_id: @another_user.id, primary_group_id: group.id }, format: :json @another_user.reload expect(@another_user.primary_group_id).to eq(group.id) end it "doesn't change primary group if they aren't a member of the group" do put :primary_group, params: { user_id: @another_user.id, primary_group_id: group.id }, format: :json @another_user.reload expect(@another_user.primary_group_id).to be_nil end it "remove user's primary group" do group.add(@another_user) put :primary_group, params: { user_id: @another_user.id, primary_group_id: "" }, format: :json @another_user.reload expect(@another_user.primary_group_id).to be(nil) end end context '#trust_level' do before do @another_user = Fabricate(:coding_horror, created_at: 1.month.ago) end it "raises an error when the user doesn't have permission" do Guardian.any_instance.expects(:can_change_trust_level?).with(@another_user).returns(false) put :trust_level, params: { user_id: @another_user.id }, format: :json expect(response).not_to be_success end it "returns a 404 if the username doesn't exist" do put :trust_level, params: { user_id: 123123 }, format: :json expect(response).not_to be_success end it "upgrades the user's trust level" do StaffActionLogger.any_instance.expects(:log_trust_level_change).with(@another_user, @another_user.trust_level, 2).once put :trust_level, params: { user_id: @another_user.id, level: 2 }, format: :json @another_user.reload expect(@another_user.trust_level).to eq(2) expect(response).to be_success end it "raises no error when demoting a user below their current trust level (locks trust level)" do stat = @another_user.user_stat stat.topics_entered = SiteSetting.tl1_requires_topics_entered + 1 stat.posts_read_count = SiteSetting.tl1_requires_read_posts + 1 stat.time_read = SiteSetting.tl1_requires_time_spent_mins * 60 stat.save! @another_user.update_attributes(trust_level: TrustLevel[1]) put :trust_level, params: { user_id: @another_user.id, level: TrustLevel[0] }, format: :json expect(response).to be_success @another_user.reload expect(@another_user.trust_level).to eq(TrustLevel[0]) expect(@another_user.manual_locked_trust_level).to eq(TrustLevel[0]) end end describe '#revoke_moderation' do before do @moderator = Fabricate(:moderator) end it 'raises an error unless the user can revoke access' do Guardian.any_instance.expects(:can_revoke_moderation?).with(@moderator).returns(false) put :revoke_moderation, params: { user_id: @moderator.id }, format: :json expect(response).to be_forbidden end it 'updates the moderator flag' do put :revoke_moderation, params: { user_id: @moderator.id }, format: :json @moderator.reload expect(@moderator.moderator).not_to eq(true) end end context '#grant_moderation' do before do @another_user = Fabricate(:coding_horror) end it "raises an error when the user doesn't have permission" do Guardian.any_instance.expects(:can_grant_moderation?).with(@another_user).returns(false) put :grant_moderation, params: { user_id: @another_user.id }, format: :json expect(response).to be_forbidden end it "returns a 404 if the username doesn't exist" do put :grant_moderation, params: { user_id: 123123 }, format: :json expect(response).to be_forbidden end it 'updates the moderator flag' do put :grant_moderation, params: { user_id: @another_user.id }, format: :json @another_user.reload expect(@another_user.moderator).to eq(true) end end context '#reject_bulk' do let(:reject_me) { Fabricate(:user) } let(:reject_me_too) { Fabricate(:user) } it 'does nothing without users' do UserDestroyer.any_instance.expects(:destroy).never delete :reject_bulk, format: :json end it "won't delete users if not allowed" do Guardian.any_instance.stubs(:can_delete_user?).returns(false) UserDestroyer.any_instance.expects(:destroy).never delete :reject_bulk, params: { users: [reject_me.id] }, format: :json end it "reports successes" do Guardian.any_instance.stubs(:can_delete_user?).returns(true) UserDestroyer.any_instance.stubs(:destroy).returns(true) delete :reject_bulk, params: { users: [reject_me.id, reject_me_too.id] }, format: :json expect(response).to be_success json = ::JSON.parse(response.body) expect(json['success'].to_i).to eq(2) expect(json['failed'].to_i).to eq(0) end context 'failures' do before do Guardian.any_instance.stubs(:can_delete_user?).returns(true) end it 'can handle some successes and some failures' do UserDestroyer.any_instance.stubs(:destroy).with(reject_me, anything).returns(false) UserDestroyer.any_instance.stubs(:destroy).with(reject_me_too, anything).returns(true) delete :reject_bulk, params: { users: [reject_me.id, reject_me_too.id] }, format: :json expect(response).to be_success json = ::JSON.parse(response.body) expect(json['success'].to_i).to eq(1) expect(json['failed'].to_i).to eq(1) end it 'reports failure due to a user still having posts' do UserDestroyer.any_instance.expects(:destroy).with(reject_me, anything).raises(UserDestroyer::PostsExistError) delete :reject_bulk, params: { users: [reject_me.id] }, format: :json expect(response).to be_success json = ::JSON.parse(response.body) expect(json['success'].to_i).to eq(0) expect(json['failed'].to_i).to eq(1) end end end context '#destroy' do let(:delete_me) { Fabricate(:user) } it "returns a 403 if the user doesn't exist" do delete :destroy, params: { id: 123123 }, format: :json expect(response).to be_forbidden end context "user has post" do let(:topic) { create_topic(user: delete_me) } before do _post = create_post(topic: topic, user: delete_me) end it "returns an api response that the user can't be deleted because it has posts" do delete :destroy, params: { id: delete_me.id }, format: :json expect(response).to be_success json = ::JSON.parse(response.body) expect(json['deleted']).to eq(false) end it "doesn't return an error if delete_posts == true" do delete :destroy, params: { id: delete_me.id, delete_posts: true }, format: :json expect(response).to be_success end end it "deletes the user record" do UserDestroyer.any_instance.expects(:destroy).returns(true) delete :destroy, params: { id: delete_me.id }, format: :json end end context 'activate' do before do @reg_user = Fabricate(:inactive_user) end it "returns success" do put :activate, params: { user_id: @reg_user.id }, format: :json expect(response).to be_success json = ::JSON.parse(response.body) expect(json['success']).to eq("OK") end it "should confirm email even when the tokens are expired" do @reg_user.email_tokens.update_all(confirmed: false, expired: true) @reg_user.reload expect(@reg_user.email_confirmed?).to eq(false) put :activate, params: { user_id: @reg_user.id }, format: :json expect(response).to be_success @reg_user.reload expect(@reg_user.email_confirmed?).to eq(true) end end context 'log_out' do before do @reg_user = Fabricate(:user) end it "returns success" do put :log_out, params: { user_id: @reg_user.id }, format: :json expect(response).to be_success json = ::JSON.parse(response.body) expect(json['success']).to eq("OK") end it "returns 404 when user_id does not exist" do put :log_out, params: { user_id: 123123 }, format: :json expect(response).not_to be_success end end context 'silence' do before do @reg_user = Fabricate(:user) end it "raises an error when the user doesn't have permission" do Guardian.any_instance.expects(:can_silence_user?).with(@reg_user).returns(false) put :silence, params: { user_id: @reg_user.id }, format: :json expect(response).to be_forbidden @reg_user.reload expect(@reg_user).not_to be_silenced end it "returns a 403 if the user doesn't exist" do put :silence, params: { user_id: 123123 }, format: :json expect(response).to be_forbidden end it "punishes the user for spamming" do put :silence, params: { user_id: @reg_user.id }, format: :json expect(response).to be_success @reg_user.reload expect(@reg_user).to be_silenced end it "can have an associated post" do silence_post = Fabricate(:post, user: @reg_user) put :silence, params: { user_id: @reg_user.id, post_id: silence_post.id, post_action: 'edit', post_edit: "this is the new contents for the post" }, format: :json expect(response).to be_success silence_post.reload expect(silence_post.raw).to eq("this is the new contents for the post") log = UserHistory.where( target_user_id: @reg_user.id, action: UserHistory.actions[:silence_user] ).first expect(log).to be_present expect(log.post_id).to eq(silence_post.id) @reg_user.reload expect(@reg_user).to be_silenced end it "will set a length of time if provided" do future_date = 1.month.from_now.to_date put( :silence, params: { user_id: @reg_user.id, silenced_till: future_date }, format: :json ) @reg_user.reload expect(@reg_user.silenced_till).to eq(future_date) end it "will send a message if provided" do Jobs.stubs(:enqueue) Jobs.expects(:enqueue).with( :critical_user_email, has_entries( type: :account_silenced, user_id: @reg_user.id ) ) put( :silence, params: { user_id: @reg_user.id, message: "Email this to the user" }, format: :json ) end end context 'unsilence' do before do @reg_user = Fabricate(:user) end it "raises an error when the user doesn't have permission" do Guardian.any_instance.expects(:can_unsilence_user?).with(@reg_user).returns(false) put :unsilence, params: { user_id: @reg_user.id }, format: :json expect(response).to be_forbidden end it "returns a 403 if the user doesn't exist" do put :unsilence, params: { user_id: 123123 }, format: :json expect(response).to be_forbidden end it "punishes the user for spamming" do UserSilencer.expects(:unsilence).with(@reg_user, @user, anything) put :unsilence, params: { user_id: @reg_user.id }, format: :json end end context 'ip-info' do it "uses ipinfo.io webservice to retrieve the info" do Excon.expects(:get).with("https://ipinfo.io/123.123.123.123/json", read_timeout: 10, connect_timeout: 10) get :ip_info, params: { ip: "123.123.123.123" }, format: :json end end context "delete_other_accounts_with_same_ip" do it "works" do Fabricate(:user, ip_address: "42.42.42.42") Fabricate(:user, ip_address: "42.42.42.42") UserDestroyer.any_instance.expects(:destroy).twice delete :delete_other_accounts_with_same_ip, params: { ip: "42.42.42.42", exclude: -1, order: "trust_level DESC" }, format: :json end end context ".invite_admin" do it "doesn't work when not via API" do controller.stubs(:is_api?).returns(false) post :invite_admin, params: { name: 'Bill', username: 'bill22', email: 'bill@bill.com' }, format: :json expect(response).not_to be_success end it 'should invite admin' do controller.stubs(:is_api?).returns(true) Jobs.expects(:enqueue).with(:critical_user_email, anything).returns(true) post :invite_admin, params: { name: 'Bill', username: 'bill22', email: 'bill@bill.com' }, format: :json expect(response).to be_success u = User.find_by_email('bill@bill.com') expect(u.name).to eq("Bill") expect(u.username).to eq("bill22") expect(u.admin).to eq(true) end it "doesn't send the email with send_email falsy" do controller.stubs(:is_api?).returns(true) Jobs.expects(:enqueue).with(:user_email, anything).never post :invite_admin, params: { name: 'Bill', username: 'bill22', email: 'bill@bill.com', send_email: '0' }, format: :json expect(response).to be_success json = ::JSON.parse(response.body) expect(json["password_url"]).to be_present end end context 'remove_group' do it "also clears the user's primary group" do g = Fabricate(:group) u = Fabricate(:user, primary_group: g) delete :remove_group, params: { group_id: g.id, user_id: u.id }, format: :json expect(u.reload.primary_group).to be_nil end end end context '#sync_sso' do let(:sso) { SingleSignOn.new } let(:sso_secret) { "sso secret" } before do log_in(:admin) SiteSetting.email_editable = false SiteSetting.sso_url = "https://www.example.com/sso" SiteSetting.enable_sso = true SiteSetting.sso_overrides_email = true SiteSetting.sso_overrides_name = true SiteSetting.sso_overrides_username = true SiteSetting.sso_secret = sso_secret sso.sso_secret = sso_secret end it 'can sync up with the sso' do sso.name = "Bob The Bob" sso.username = "bob" sso.email = "bob@bob.com" sso.external_id = "1" user = DiscourseSingleSignOn.parse(sso.payload) .lookup_or_create_user sso.name = "Bill" sso.username = "Hokli$$!!" sso.email = "bob2@bob.com" post :sync_sso, params: Rack::Utils.parse_query(sso.payload), format: :json expect(response).to be_success user.reload expect(user.email).to eq("bob2@bob.com") expect(user.name).to eq("Bill") expect(user.username).to eq("Hokli") end it 'should create new users' do sso.name = "Dr. Claw" sso.username = "dr_claw" sso.email = "dr@claw.com" sso.external_id = "2" post :sync_sso, params: Rack::Utils.parse_query(sso.payload), format: :json expect(response).to be_success user = User.find_by_email('dr@claw.com') expect(user).to be_present expect(user.ip_address).to be_blank end it 'should return the right message if the record is invalid' do sso.email = "" sso.name = "" sso.external_id = "1" post :sync_sso, params: Rack::Utils.parse_query(sso.payload), format: :json expect(response.status).to eq(403) expect(JSON.parse(response.body)["message"]).to include("Primary email can't be blank") end end end