require 'rails_helper' describe UsersController do describe '.show' do context "anon" do let(:user) { Discourse.system_user } it "returns success" do xhr :get, :show, username: user.username, format: :json expect(response).to be_success end it "raises an error for anon when profiles are hidden" do SiteSetting.stubs(:hide_user_profiles_from_public).returns(true) xhr :get, :show, username: user.username, format: :json expect(response).not_to be_success end end context "logged in" do let(:user) { log_in } it 'returns success' do xhr :get, :show, username: user.username, format: :json expect(response).to be_success json = JSON.parse(response.body) expect(json["user"]["has_title_badges"]).to eq(false) end it "returns not found when the username doesn't exist" do xhr :get, :show, username: 'madeuppity' expect(response).not_to be_success end it 'returns not found when the user is inactive' do inactive = Fabricate(:user, active: false) xhr :get, :show, username: inactive.username expect(response).not_to be_success end it "raises an error on invalid access" do Guardian.any_instance.expects(:can_see?).with(user).returns(false) xhr :get, :show, username: user.username expect(response).to be_forbidden end describe "user profile views" do let(:other_user) { Fabricate(:user) } it "should track a user profile view for a signed in user" do UserProfileView.expects(:add).with(other_user.user_profile.id, request.remote_ip, user.id) xhr :get, :show, username: other_user.username end it "should not track a user profile view for a user viewing his own profile" do UserProfileView.expects(:add).never xhr :get, :show, username: user.username end it "should track a user profile view for an anon user" do UserProfileView.expects(:add).with(other_user.user_profile.id, request.remote_ip, nil) xhr :get, :show, username: other_user.username end it "skips tracking" do UserProfileView.expects(:add).never xhr :get, :show, { username: user.username, skip_track_visit: true } end end context "fetching a user by external_id" do before { user.create_single_sign_on_record(external_id: '997', last_payload: '') } it "returns fetch for a matching external_id" do xhr :get, :show, external_id: '997' expect(response).to be_success end it "returns not found when external_id doesn't match" do xhr :get, :show, external_id: '99' expect(response).not_to be_success end end end end describe '.user_preferences_redirect' do it 'requires the user to be logged in' do expect { get :user_preferences_redirect }.to raise_error(Discourse::NotLoggedIn) end it "redirects to their profile when logged in" do user = log_in get :user_preferences_redirect expect(response).to redirect_to("/users/#{user.username_lower}/preferences") end end describe '.activate_account' do before do UsersController.any_instance.stubs(:honeypot_or_challenge_fails?).returns(false) end context 'invalid token' do it 'return success' do EmailToken.expects(:confirm).with('asdfasdf').returns(nil) put :perform_account_activation, token: 'asdfasdf' expect(response).to be_success expect(flash[:error]).to be_present end end context 'valid token' do let(:user) { Fabricate(:user) } context 'welcome message' do before do EmailToken.expects(:confirm).with('asdfasdf').returns(user) end it 'enqueues a welcome message if the user object indicates so' do user.send_welcome_message = true user.expects(:enqueue_welcome_message).with('welcome_user') put :perform_account_activation, token: 'asdfasdf' end it "doesn't enqueue the welcome message if the object returns false" do user.send_welcome_message = false user.expects(:enqueue_welcome_message).with('welcome_user').never put :perform_account_activation, token: 'asdfasdf' end end context "honeypot" do it "raises an error if the honeypot is invalid" do UsersController.any_instance.stubs(:honeypot_or_challenge_fails?).returns(true) put :perform_account_activation, token: 'asdfasdf' expect(response).not_to be_success end end context 'response' do before do Guardian.any_instance.expects(:can_access_forum?).returns(true) EmailToken.expects(:confirm).with('asdfasdf').returns(user) put :perform_account_activation, token: 'asdfasdf' end it 'returns success' do expect(response).to be_success end it "doesn't set an error" do expect(flash[:error]).to be_blank end it 'logs in as the user' do expect(session[:current_user_id]).to be_present end it "doesn't set @needs_approval" do expect(assigns[:needs_approval]).to be_blank end end context 'user is not approved' do before do Guardian.any_instance.expects(:can_access_forum?).returns(false) EmailToken.expects(:confirm).with('asdfasdf').returns(user) put :perform_account_activation, token: 'asdfasdf' end it 'returns success' do expect(response).to be_success end it 'sets @needs_approval' do expect(assigns[:needs_approval]).to be_present end it "doesn't set an error" do expect(flash[:error]).to be_blank end it "doesn't log the user in" do expect(session[:current_user_id]).to be_blank end end end end describe '.password_reset' do let(:user) { Fabricate(:user) } context "you can view it even if login is required" do it "returns success" do SiteSetting.login_required = true get :password_reset, token: 'asdfasdf' expect(response).to be_success end end context 'missing token' do before do get :password_reset, token: SecureRandom.hex end it 'disallows login' do expect(assigns[:error]).to be_present expect(session[:current_user_id]).to be_blank expect(assigns[:invalid_token]).to eq(nil) expect(response).to be_success end end context 'invalid token' do before do get :password_reset, token: "evil_trout!" end it 'disallows login' do expect(assigns[:error]).to be_present expect(session[:current_user_id]).to be_blank expect(assigns[:invalid_token]).to eq(true) expect(response).to be_success end end context 'valid token' do context 'when rendered' do render_views it 'renders referrer never on get requests' do user = Fabricate(:user, auth_token: SecureRandom.hex(16)) token = user.email_tokens.create(email: user.email).token get :password_reset, token: token expect(response.body).to include('') end end it 'returns success' do user = Fabricate(:user, auth_token: SecureRandom.hex(16)) token = user.email_tokens.create(email: user.email).token old_token = user.auth_token get :password_reset, token: token put :password_reset, token: token, password: 'hg9ow8yhg98o' expect(response).to be_success expect(assigns[:error]).to be_blank user.reload expect(user.auth_token).to_not eq old_token expect(user.auth_token.length).to eq 32 expect(session["password-#{token}"]).to be_blank end it 'disallows double password reset' do user = Fabricate(:user, auth_token: SecureRandom.hex(16)) token = user.email_tokens.create(email: user.email).token get :password_reset, token: token put :password_reset, token: token, password: 'hg9ow8yhg98o' put :password_reset, token: token, password: 'test123123Asdfsdf' user.reload expect(user.confirm_password?('hg9ow8yhg98o')).to eq(true) end it "redirects to the wizard if you're the first admin" do user = Fabricate(:admin, auth_token: SecureRandom.hex(16), auth_token_updated_at: Time.now) token = user.email_tokens.create(email: user.email).token get :password_reset, token: token put :password_reset, token: token, password: 'hg9ow8yhg98oadminlonger' expect(response).to be_redirect end it "doesn't invalidate the token when loading the page" do user = Fabricate(:user, auth_token: SecureRandom.hex(16)) email_token = user.email_tokens.create(email: user.email) get :password_reset, token: email_token.token email_token.reload expect(email_token.confirmed).to eq(false) end end context 'submit change' do let(:token) { EmailToken.generate_token } before do EmailToken.expects(:confirm).with(token).returns(user) end it "fails when the password is blank" do put :password_reset, token: token, password: '' expect(assigns(:user).errors).to be_present expect(session[:current_user_id]).to be_blank end it "fails when the password is too long" do put :password_reset, token: token, password: ('x' * (User.max_password_length + 1)) expect(assigns(:user).errors).to be_present expect(session[:current_user_id]).to be_blank end it "logs in the user" do put :password_reset, token: token, password: 'ksjafh928r' expect(assigns(:user).errors).to be_blank expect(session[:current_user_id]).to be_present end it "doesn't log in the user when not approved" do SiteSetting.expects(:must_approve_users?).returns(true) put :password_reset, token: token, password: 'ksjafh928r' expect(assigns(:user).errors).to be_blank expect(session[:current_user_id]).to be_blank end end end describe '.confirm_email_token' do let(:user) { Fabricate(:user) } it "token doesn't match any records" do email_token = user.email_tokens.create(email: user.email) get :confirm_email_token, token: SecureRandom.hex, format: :json expect(response).to be_success expect(email_token.reload.confirmed).to eq(false) end it "token matches" do email_token = user.email_tokens.create(email: user.email) get :confirm_email_token, token: email_token.token, format: :json expect(response).to be_success expect(email_token.reload.confirmed).to eq(true) end end describe '.admin_login' do let(:admin) { Fabricate(:admin) } let(:user) { Fabricate(:user) } context 'enqueues mail' do it 'enqueues mail with admin email and sso enabled' do Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :admin_login, user_id: admin.id)) put :admin_login, email: admin.email end end context 'logs in admin' do it 'does not log in admin with invalid token' do SiteSetting.enable_sso = true get :admin_login, token: "invalid" expect(session[:current_user_id]).to be_blank end it 'does log in admin with valid token and SSO disabled' do SiteSetting.enable_sso = false token = admin.email_tokens.create(email: admin.email).token get :admin_login, token: token expect(response).to redirect_to('/') expect(session[:current_user_id]).to eq(admin.id) end it 'logs in admin with valid token and SSO enabled' do SiteSetting.enable_sso = true token = admin.email_tokens.create(email: admin.email).token get :admin_login, token: token expect(response).to redirect_to('/') expect(session[:current_user_id]).to eq(admin.id) end end end describe '#toggle_anon' do it 'allows you to toggle anon if enabled' do SiteSetting.allow_anonymous_posting = true user = log_in user.trust_level = 1 user.save post :toggle_anon expect(response).to be_success expect(session[:current_user_id]).to eq(AnonymousShadowCreator.get(user).id) post :toggle_anon expect(response).to be_success expect(session[:current_user_id]).to eq(user.id) end end describe '#create' do before do UsersController.any_instance.stubs(:honeypot_value).returns(nil) UsersController.any_instance.stubs(:challenge_value).returns(nil) SiteSetting.stubs(:allow_new_registrations).returns(true) @user = Fabricate.build(:user) @user.password = "strongpassword" end let(:post_user_params) do { name: @user.name, username: @user.username, password: "strongpassword", email: @user.email } end def post_user xhr :post, :create, post_user_params end context 'when creating a user' do it 'sets the user locale to I18n.locale' do SiteSetting.stubs(:default_locale).returns('en') I18n.stubs(:locale).returns(:fr) post_user expect(User.find_by(username: @user.username).locale).to eq('fr') end end context 'when creating a non active user (unconfirmed email)' do it 'returns a 500 when local logins are disabled' do SiteSetting.expects(:enable_local_logins).returns(false) post_user expect(response.status).to eq(500) end it 'returns an error when new registrations are disabled' do SiteSetting.stubs(:allow_new_registrations).returns(false) post_user json = JSON.parse(response.body) expect(json['success']).to eq(false) expect(json['message']).to be_present end it 'creates a user correctly' do Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :signup)) User.any_instance.expects(:enqueue_welcome_message).with('welcome_user').never post_user expect(JSON.parse(response.body)['active']).to be_falsey # should save user_created_message in session expect(session["user_created_message"]).to be_present end context "and 'must approve users' site setting is enabled" do before { SiteSetting.expects(:must_approve_users).returns(true) } it 'does not enqueue an email' do Jobs.expects(:enqueue).never post_user end it 'does not login the user' do post_user expect(session[:current_user_id]).to be_blank end it 'indicates the user is not active in the response' do post_user expect(JSON.parse(response.body)['active']).to be_falsey end it "shows the 'waiting approval' message" do post_user expect(JSON.parse(response.body)['message']).to eq(I18n.t 'login.wait_approval') end end end context "creating as active" do it "won't create the user as active" do xhr :post, :create, post_user_params.merge(active: true) expect(JSON.parse(response.body)['active']).to be_falsey end context "with a regular api key" do let(:user) { Fabricate(:user) } let(:api_key) { Fabricate(:api_key, user: user) } it "won't create the user as active with a regular key" do xhr :post, :create, post_user_params.merge(active: true, api_key: api_key.key) expect(JSON.parse(response.body)['active']).to be_falsey end end context "with an admin api key" do let(:user) { Fabricate(:admin) } let(:api_key) { Fabricate(:api_key, user: user) } it "creates the user as active with a regular key" do xhr :post, :create, post_user_params.merge(active: true, api_key: api_key.key) expect(JSON.parse(response.body)['active']).to be_truthy end it "won't create the developer as active" do UsernameCheckerService.expects(:is_developer?).returns(true) xhr :post, :create, post_user_params.merge(active: true, api_key: api_key.key) expect(JSON.parse(response.body)['active']).to be_falsy end end end context "creating as staged" do it "won't create the user as staged" do xhr :post, :create, post_user_params.merge(staged: true) new_user = User.where(username: post_user_params[:username]).first expect(new_user.staged?).to eq(false) end context "with a regular api key" do let(:user) { Fabricate(:user) } let(:api_key) { Fabricate(:api_key, user: user) } it "won't create the user as staged with a regular key" do xhr :post, :create, post_user_params.merge(staged: true, api_key: api_key.key) new_user = User.where(username: post_user_params[:username]).first expect(new_user.staged?).to eq(false) end end context "with an admin api key" do let(:user) { Fabricate(:admin) } let(:api_key) { Fabricate(:api_key, user: user) } it "creates the user as staged with a regular key" do xhr :post, :create, post_user_params.merge(staged: true, api_key: api_key.key) new_user = User.where(username: post_user_params[:username]).first expect(new_user.staged?).to eq(true) end it "won't create the developer as staged" do UsernameCheckerService.expects(:is_developer?).returns(true) xhr :post, :create, post_user_params.merge(staged: true, api_key: api_key.key) new_user = User.where(username: post_user_params[:username]).first expect(new_user.staged?).to eq(false) end end end context 'when creating an active user (confirmed email)' do before { User.any_instance.stubs(:active?).returns(true) } it 'enqueues a welcome email' do User.any_instance.expects(:enqueue_welcome_message).with('welcome_user') post_user # should save user_created_message in session expect(session["user_created_message"]).to be_present end it "shows the 'active' message" do User.any_instance.expects(:enqueue_welcome_message) post_user expect(JSON.parse(response.body)['message']).to eq( I18n.t 'login.active' ) end it "should be logged in" do User.any_instance.expects(:enqueue_welcome_message) post_user expect(session[:current_user_id]).to be_present end it 'indicates the user is active in the response' do User.any_instance.expects(:enqueue_welcome_message) post_user expect(JSON.parse(response.body)['active']).to be_truthy end it 'returns 500 status when new registrations are disabled' do SiteSetting.stubs(:allow_new_registrations).returns(false) post_user json = JSON.parse(response.body) expect(json['success']).to eq(false) expect(json['message']).to be_present end context 'authentication records for' do before do SiteSetting.expects(:must_approve_users).returns(true) end it 'should create twitter user info if required' do SiteSetting.stubs(:enable_twitter_logins?).returns(true) twitter_auth = { twitter_user_id: 42, twitter_screen_name: "bruce" } auth = session[:authentication] = {} auth[:authenticator_name] = 'twitter' auth[:extra_data] = twitter_auth TwitterUserInfo.expects(:create) post_user end end end context 'after success' do before { post_user } it 'should succeed' do is_expected.to respond_with(:success) end it 'has the proper JSON' do json = JSON::parse(response.body) expect(json["success"]).to eq(true) end it 'should not result in an active account' do expect(User.find_by(username: @user.username).active).to eq(false) end end shared_examples 'honeypot fails' do it 'should not create a new user' do expect { xhr :post, :create, create_params }.to_not change { User.count } end it 'should not send an email' do User.any_instance.expects(:enqueue_welcome_message).never xhr :post, :create, create_params end it 'should say it was successful' do xhr :post, :create, create_params json = JSON::parse(response.body) expect(json["success"]).to eq(true) # should not change the session expect(session["user_created_message"]).to be_blank end end context 'when honeypot value is wrong' do before do UsersController.any_instance.stubs(:honeypot_value).returns('abc') end let(:create_params) { {name: @user.name, username: @user.username, password: "strongpassword", email: @user.email, password_confirmation: 'wrong'} } include_examples 'honeypot fails' end context 'when challenge answer is wrong' do before do UsersController.any_instance.stubs(:challenge_value).returns('abc') end let(:create_params) { {name: @user.name, username: @user.username, password: "strongpassword", email: @user.email, challenge: 'abc'} } include_examples 'honeypot fails' end context "when 'invite only' setting is enabled" do before { SiteSetting.expects(:invite_only?).returns(true) } let(:create_params) {{ name: @user.name, username: @user.username, password: 'strongpassword', email: @user.email }} include_examples 'honeypot fails' end shared_examples 'failed signup' do it 'should not create a new User' do expect { xhr :post, :create, create_params }.to_not change { User.count } end it 'should report failed' do xhr :post, :create, create_params json = JSON::parse(response.body) expect(json["success"]).not_to eq(true) # should not change the session expect(session["user_created_message"]).to be_blank end end context 'when password is blank' do let(:create_params) { {name: @user.name, username: @user.username, password: "", email: @user.email} } include_examples 'failed signup' end context 'when password is too long' do let(:create_params) { {name: @user.name, username: @user.username, password: "x" * (User.max_password_length + 1), email: @user.email} } include_examples 'failed signup' end context 'when password param is missing' do let(:create_params) { {name: @user.name, username: @user.username, email: @user.email} } include_examples 'failed signup' end context 'with a reserved username' do let(:create_params) { {name: @user.name, username: 'Reserved', email: @user.email, password: "x" * 20} } before { SiteSetting.reserved_usernames = 'a|reserved|b' } after { SiteSetting.reserved_usernames = nil } include_examples 'failed signup' end context 'when an Exception is raised' do [ ActiveRecord::StatementInvalid, RestClient::Forbidden ].each do |exception| before { User.any_instance.stubs(:save).raises(exception) } let(:create_params) { { name: @user.name, username: @user.username, password: "strongpassword", email: @user.email} } include_examples 'failed signup' end end context "with custom fields" do let!(:user_field) { Fabricate(:user_field) } let!(:another_field) { Fabricate(:user_field) } let!(:optional_field) { Fabricate(:user_field, required: false) } context "without a value for the fields" do let(:create_params) { {name: @user.name, password: 'watwatwat', username: @user.username, email: @user.email} } include_examples 'failed signup' end context "with values for the fields" do let(:create_params) { { name: @user.name, password: 'watwatwatwat', username: @user.username, email: @user.email, user_fields: { user_field.id.to_s => 'value1', another_field.id.to_s => 'value2', } } } it "should succeed without the optional field" do xhr :post, :create, create_params expect(response).to be_success inserted = User.where(email: @user.email).first expect(inserted).to be_present expect(inserted.custom_fields).to be_present expect(inserted.custom_fields["user_field_#{user_field.id}"]).to eq('value1') expect(inserted.custom_fields["user_field_#{another_field.id}"]).to eq('value2') expect(inserted.custom_fields["user_field_#{optional_field.id}"]).to be_blank end it "should succeed with the optional field" do create_params[:user_fields][optional_field.id.to_s] = 'value3' xhr :post, :create, create_params.merge(create_params) expect(response).to be_success inserted = User.where(email: @user.email).first expect(inserted).to be_present expect(inserted.custom_fields).to be_present expect(inserted.custom_fields["user_field_#{user_field.id}"]).to eq('value1') expect(inserted.custom_fields["user_field_#{another_field.id}"]).to eq('value2') expect(inserted.custom_fields["user_field_#{optional_field.id}"]).to eq('value3') end it "trims excessively long fields" do create_params[:user_fields][optional_field.id.to_s] = ('x' * 3000) xhr :post, :create, create_params.merge(create_params) expect(response).to be_success inserted = User.where(email: @user.email).first val = inserted.custom_fields["user_field_#{optional_field.id}"] expect(val.length).to eq(UserField.max_length) end end end context "with only optional custom fields" do let!(:user_field) { Fabricate(:user_field, required: false) } context "without values for the fields" do let(:create_params) { { name: @user.name, password: 'watwatwatwat', username: @user.username, email: @user.email, } } it "should succeed" do xhr :post, :create, create_params expect(response).to be_success inserted = User.where(email: @user.email).first expect(inserted).to be_present expect(inserted.custom_fields).not_to be_present expect(inserted.custom_fields["user_field_#{user_field.id}"]).to be_blank end end end context "when taking over a staged account" do let!(:staged) { Fabricate(:staged, email: "staged@account.com") } it "succeeds" do xhr :post, :create, email: staged.email, username: "zogstrip", password: "P4ssw0rd$$" result = ::JSON.parse(response.body) expect(result["success"]).to eq(true) expect(User.find_by(email: staged.email).staged).to eq(false) end end end context '.username' do it 'raises an error when not logged in' do expect { xhr :put, :username, username: 'somename' }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do let(:old_username) { "OrigUsrname" } let(:new_username) { "#{old_username}1234" } let(:user) { Fabricate(:user, username: old_username) } before do user.username = old_username log_in_user(user) end it 'raises an error without a new_username param' do expect { xhr :put, :username, username: user.username }.to raise_error(ActionController::ParameterMissing) expect(user.reload.username).to eq(old_username) end it 'raises an error when you don\'t have permission to change the username' do Guardian.any_instance.expects(:can_edit_username?).with(user).returns(false) xhr :put, :username, username: user.username, new_username: new_username expect(response).to be_forbidden expect(user.reload.username).to eq(old_username) end # Bad behavior, this should give a real JSON error, not an InvalidParameters it 'raises an error when change_username fails' do User.any_instance.expects(:save).returns(false) expect { xhr :put, :username, username: user.username, new_username: new_username }.to raise_error(Discourse::InvalidParameters) expect(user.reload.username).to eq(old_username) end it 'should succeed in normal circumstances' do xhr :put, :username, username: user.username, new_username: new_username expect(response).to be_success expect(user.reload.username).to eq(new_username) end skip 'should fail if the user is old', 'ensure_can_edit_username! is not throwing' do # Older than the change period and >1 post user.created_at = Time.now - (SiteSetting.username_change_period + 1).days user.stubs(:post_count).returns(200) expect(Guardian.new(user).can_edit_username?(user)).to eq(false) xhr :put, :username, username: user.username, new_username: new_username expect(response).to be_forbidden expect(user.reload.username).to eq(old_username) end it 'should create a staff action log when a staff member changes the username' do acting_user = Fabricate(:admin) log_in_user(acting_user) xhr :put, :username, username: user.username, new_username: new_username expect(response).to be_success expect(UserHistory.where(action: UserHistory.actions[:change_username], target_user_id: user.id, acting_user_id: acting_user.id)).to be_present expect(user.reload.username).to eq(new_username) end it 'should return a JSON response with the updated username' do xhr :put, :username, username: user.username, new_username: new_username expect(::JSON.parse(response.body)['username']).to eq(new_username) end end end context '.check_username' do it 'raises an error without any parameters' do expect { xhr :get, :check_username }.to raise_error(ActionController::ParameterMissing) end shared_examples 'when username is unavailable' do it 'should return success' do expect(response).to be_success end it 'should return available as false in the JSON' do expect(::JSON.parse(response.body)['available']).to eq(false) end it 'should return a suggested username' do expect(::JSON.parse(response.body)['suggestion']).to be_present end end shared_examples 'when username is available' do it 'should return success' do expect(response).to be_success end it 'should return available in the JSON' do expect(::JSON.parse(response.body)['available']).to eq(true) end end it 'returns nothing when given an email param but no username' do xhr :get, :check_username, email: 'dood@example.com' expect(response).to be_success end context 'username is available' do before do xhr :get, :check_username, username: 'BruceWayne' end include_examples 'when username is available' end context 'username is unavailable' do let!(:user) { Fabricate(:user) } before do xhr :get, :check_username, username: user.username end include_examples 'when username is unavailable' end shared_examples 'checking an invalid username' do it 'should return success' do expect(response).to be_success end it 'should not return an available key' do expect(::JSON.parse(response.body)['available']).to eq(nil) end it 'should return an error message' do expect(::JSON.parse(response.body)['errors']).not_to be_empty end end context 'has invalid characters' do before do xhr :get, :check_username, username: 'bad username' end include_examples 'checking an invalid username' it 'should return the invalid characters message' do expect(::JSON.parse(response.body)['errors']).to include(I18n.t(:'user.username.characters')) end end context 'is too long' do before do xhr :get, :check_username, username: generate_username(User.username_length.last + 1) end include_examples 'checking an invalid username' it 'should return the "too long" message' do expect(::JSON.parse(response.body)['errors']).to include(I18n.t(:'user.username.long', max: User.username_length.end)) end end describe 'different case of existing username' do context "it's my username" do let!(:user) { Fabricate(:user, username: 'hansolo') } before do log_in_user(user) xhr :get, :check_username, username: 'HanSolo' end include_examples 'when username is available' end context "it's someone else's username" do let!(:user) { Fabricate(:user, username: 'hansolo') } before do log_in xhr :get, :check_username, username: 'HanSolo' end include_examples 'when username is unavailable' end context "an admin changing it for someone else" do let!(:user) { Fabricate(:user, username: 'hansolo') } before do log_in_user(Fabricate(:admin)) xhr :get, :check_username, username: 'HanSolo', for_user_id: user.id end include_examples 'when username is available' end end end describe '#invited' do it 'returns success' do user = Fabricate(:user) xhr :get, :invited, username: user.username expect(response).to be_success end it 'filters by email' do inviter = Fabricate(:user) invitee = Fabricate(:user) _invite = Fabricate( :invite, email: 'billybob@example.com', invited_by: inviter, user: invitee ) Fabricate( :invite, email: 'jimtom@example.com', invited_by: inviter, user: invitee ) xhr :get, :invited, username: inviter.username, search: 'billybob' invites = JSON.parse(response.body)['invites'] expect(invites.size).to eq(1) expect(invites.first).to include('email' => 'billybob@example.com') end it 'filters by username' do inviter = Fabricate(:user) invitee = Fabricate(:user, username: 'billybob') _invite = Fabricate( :invite, invited_by: inviter, email: 'billybob@example.com', user: invitee ) Fabricate( :invite, invited_by: inviter, user: Fabricate(:user, username: 'jimtom') ) xhr :get, :invited, username: inviter.username, search: 'billybob' invites = JSON.parse(response.body)['invites'] expect(invites.size).to eq(1) expect(invites.first).to include('email' => 'billybob@example.com') end context 'with guest' do context 'with pending invites' do it 'does not return invites' do inviter = Fabricate(:user) Fabricate(:invite, invited_by: inviter) xhr :get, :invited, username: inviter.username, filter: 'pending' invites = JSON.parse(response.body)['invites'] expect(invites).to be_empty end end context 'with redeemed invites' do it 'returns invites' do inviter = Fabricate(:user) invitee = Fabricate(:user) invite = Fabricate(:invite, invited_by: inviter, user: invitee) xhr :get, :invited, username: inviter.username invites = JSON.parse(response.body)['invites'] expect(invites.size).to eq(1) expect(invites.first).to include('email' => invite.email) end end end context 'with authenticated user' do context 'with pending invites' do context 'with permission to see pending invites' do it 'returns invites' do user = log_in inviter = Fabricate(:user) invite = Fabricate(:invite, invited_by: inviter) stub_guardian(user) do |guardian| guardian.stubs(:can_see_invite_details?). with(inviter).returns(true) end xhr :get, :invited, username: inviter.username, filter: 'pending' invites = JSON.parse(response.body)['invites'] expect(invites.size).to eq(1) expect(invites.first).to include("email" => invite.email) end end context 'without permission to see pending invites' do it 'does not return invites' do user = log_in inviter = Fabricate(:user) _invitee = Fabricate(:user) Fabricate(:invite, invited_by: inviter) stub_guardian(user) do |guardian| guardian.stubs(:can_see_invite_details?). with(inviter).returns(false) end xhr :get, :invited, username: inviter.username, filter: 'pending' json = JSON.parse(response.body)['invites'] expect(json).to be_empty end end end context 'with redeemed invites' do it 'returns invites' do _user = log_in inviter = Fabricate(:user) invitee = Fabricate(:user) invite = Fabricate(:invite, invited_by: inviter, user: invitee) xhr :get, :invited, username: inviter.username invites = JSON.parse(response.body)['invites'] expect(invites.size).to eq(1) expect(invites.first).to include('email' => invite.email) end end end end describe '#update' do context 'with guest' do it 'raises an error' do expect do xhr :put, :update, username: 'guest' end.to raise_error(Discourse::NotLoggedIn) end end context "as a staff user" do let!(:user) { log_in(:admin) } context "uneditable field" do let!(:user_field) { Fabricate(:user_field, editable: false) } it "allows staff to edit the field" do put :update, username: user.username, name: 'Jim Tom', user_fields: { user_field.id.to_s => 'happy' } expect(response).to be_success expect(user.user_fields[user_field.id.to_s]).to eq('happy') end end end context 'with authenticated user' do context 'with permission to update' do let!(:user) { log_in(:user) } it 'allows the update' do user2 = Fabricate(:user) user3 = Fabricate(:user) put :update, username: user.username, name: 'Jim Tom', custom_fields: {test: :it}, muted_usernames: "#{user2.username},#{user3.username}" expect(response).to be_success user.reload expect(user.name).to eq 'Jim Tom' expect(user.custom_fields['test']).to eq 'it' expect(user.muted_users.pluck(:username).sort).to eq [user2.username,user3.username].sort put :update, username: user.username, muted_usernames: "" user.reload expect(user.muted_users.pluck(:username).sort).to be_empty end context 'a locale is chosen that differs from I18n.locale' do it "updates the user's locale" do I18n.stubs(:locale).returns('fr') put :update, username: user.username, locale: :fa_IR expect(User.find_by(username: user.username).locale).to eq('fa_IR') end end context "with user fields" do context "an editable field" do let!(:user_field) { Fabricate(:user_field) } let!(:optional_field) { Fabricate(:user_field, required: false ) } it "should update the user field" do put :update, username: user.username, name: 'Jim Tom', user_fields: { user_field.id.to_s => 'happy' } expect(response).to be_success expect(user.user_fields[user_field.id.to_s]).to eq 'happy' end it "cannot be updated to blank" do put :update, username: user.username, name: 'Jim Tom', user_fields: { user_field.id.to_s => '' } expect(response).not_to be_success expect(user.user_fields[user_field.id.to_s]).not_to eq('happy') end it "trims excessively large fields" do put :update, username: user.username, name: 'Jim Tom', user_fields: { user_field.id.to_s => ('x' * 3000) } expect(user.user_fields[user_field.id.to_s].size).to eq(UserField.max_length) end end context "uneditable field" do let!(:user_field) { Fabricate(:user_field, editable: false) } it "does not update the user field" do put :update, username: user.username, name: 'Jim Tom', user_fields: { user_field.id.to_s => 'happy' } expect(response).to be_success expect(user.user_fields[user_field.id.to_s]).to be_blank end end end it 'returns user JSON' do put :update, username: user.username json = JSON.parse(response.body) expect(json['user']['id']).to eq user.id end end context 'without permission to update' do it 'does not allow the update' do user = Fabricate(:user, name: 'Billy Bob') log_in_user(user) Guardian.any_instance.expects(:can_edit?).with(user).returns(false) put :update, username: user.username, name: 'Jim Tom' expect(response).to be_forbidden expect(user.reload.name).not_to eq 'Jim Tom' end end end end describe "badge_card" do let(:user) { Fabricate(:user) } let(:badge) { Fabricate(:badge) } let(:user_badge) { BadgeGranter.grant(badge, user) } it "sets the user's card image to the badge" do log_in_user user xhr :put, :update_card_badge, user_badge_id: user_badge.id, username: user.username expect(user.user_profile.reload.card_image_badge_id).to be_blank badge.update_attributes image: "wat.com/wat.jpg" xhr :put, :update_card_badge, user_badge_id: user_badge.id, username: user.username expect(user.user_profile.reload.card_image_badge_id).to eq(badge.id) # Can set to nothing xhr :put, :update_card_badge, username: user.username expect(user.user_profile.reload.card_image_badge_id).to be_blank end end describe "badge_title" do let(:user) { Fabricate(:user) } let(:badge) { Fabricate(:badge) } let(:user_badge) { BadgeGranter.grant(badge, user) } it "sets the user's title to the badge name if it is titleable" do log_in_user user xhr :put, :badge_title, user_badge_id: user_badge.id, username: user.username expect(user.reload.title).not_to eq(badge.name) badge.update_attributes allow_title: true xhr :put, :badge_title, user_badge_id: user_badge.id, username: user.username expect(user.reload.title).to eq(badge.name) expect(user.user_profile.badge_granted_title).to eq(true) user.title = "testing" user.save user.user_profile.reload expect(user.user_profile.badge_granted_title).to eq(false) end end describe "search_users" do let(:topic) { Fabricate :topic } let(:user) { Fabricate :user, username: "joecabot", name: "Lawrence Tierney" } before do SearchIndexer.enable Fabricate :post, user: user, topic: topic end it "searches when provided the term only" do xhr :post, :search_users, term: user.name.split(" ").last expect(response).to be_success json = JSON.parse(response.body) expect(json["users"].map { |u| u["username"] }).to include(user.username) end it "searches when provided the topic only" do xhr :post, :search_users, topic_id: topic.id expect(response).to be_success json = JSON.parse(response.body) expect(json["users"].map { |u| u["username"] }).to include(user.username) end it "searches when provided the term and topic" do xhr :post, :search_users, term: user.name.split(" ").last, topic_id: topic.id expect(response).to be_success json = JSON.parse(response.body) expect(json["users"].map { |u| u["username"] }).to include(user.username) end it "searches only for users who have access to private topic" do privileged_user = Fabricate(:user, trust_level: 4, username: "joecabit", name: "Lawrence Tierney") privileged_group = Fabricate(:group) privileged_group.add(privileged_user) privileged_group.save category = Fabricate(:category) category.set_permissions(privileged_group => :readonly) category.save private_topic = Fabricate(:topic, category: category) xhr :post, :search_users, term: user.name.split(" ").last, topic_id: private_topic.id, topic_allowed_users: "true" expect(response).to be_success json = JSON.parse(response.body) expect(json["users"].map { |u| u["username"] }).to_not include(user.username) expect(json["users"].map { |u| u["username"] }).to include(privileged_user.username) end context "when `enable_names` is true" do before do SiteSetting.enable_names = true end it "returns names" do xhr :post, :search_users, term: user.name json = JSON.parse(response.body) expect(json["users"].map { |u| u["name"] }).to include(user.name) end end context "when `enable_names` is false" do before do SiteSetting.stubs(:enable_names?).returns(false) end it "returns names" do xhr :post, :search_users, term: user.name json = JSON.parse(response.body) expect(json["users"].map { |u| u["name"] }).not_to include(user.name) end end end describe 'send_activation_email' do context 'for an existing user' do let(:user) { Fabricate(:user, active: false) } context 'with a valid email_token' do it 'should send the activation email' do Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :signup)) xhr :post, :send_activation_email, username: user.username end end context 'without an existing email_token' do before do user.email_tokens.each {|t| t.destroy} user.reload end it 'should generate a new token' do expect { xhr :post, :send_activation_email, username: user.username }.to change{ user.email_tokens(true).count }.by(1) end it 'should send an email' do Jobs.expects(:enqueue).with(:critical_user_email, has_entries(type: :signup)) xhr :post, :send_activation_email, username: user.username end end end context 'when username does not exist' do it 'should not send an email' do Jobs.expects(:enqueue).never xhr :post, :send_activation_email, username: 'nopenopenopenope' end end end describe '.pick_avatar' do it 'raises an error when not logged in' do expect { xhr :put, :pick_avatar, username: 'asdf', avatar_id: 1, type: "custom" }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do let!(:user) { log_in } let(:upload) { Fabricate(:upload) } it "raises an error when you don't have permission to toggle the avatar" do another_user = Fabricate(:user) xhr :put, :pick_avatar, username: another_user.username, upload_id: upload.id, type: "custom" expect(response).to be_forbidden end it "raises an error when sso_overrides_avatar is disabled" do SiteSetting.stubs(:sso_overrides_avatar).returns(true) xhr :put, :pick_avatar, username: user.username, upload_id: upload.id, type: "custom" expect(response).to_not be_success end it "raises an error when selecting the custom/uploaded avatar and allow_uploaded_avatars is disabled" do SiteSetting.stubs(:allow_uploaded_avatars).returns(false) xhr :put, :pick_avatar, username: user.username, upload_id: upload.id, type: "custom" expect(response).to_not be_success end it 'can successfully pick the system avatar' do xhr :put, :pick_avatar, username: user.username expect(response).to be_success expect(user.reload.uploaded_avatar_id).to eq(nil) end it 'can successfully pick a gravatar' do xhr :put, :pick_avatar, username: user.username, upload_id: upload.id, type: "gravatar" expect(response).to be_success expect(user.reload.uploaded_avatar_id).to eq(upload.id) expect(user.user_avatar.reload.gravatar_upload_id).to eq(upload.id) end it 'can successfully pick a custom avatar' do xhr :put, :pick_avatar, username: user.username, upload_id: upload.id, type: "custom" expect(response).to be_success expect(user.reload.uploaded_avatar_id).to eq(upload.id) expect(user.user_avatar.reload.custom_upload_id).to eq(upload.id) end end end describe '.destroy_user_image' do it 'raises an error when not logged in' do expect { xhr :delete, :destroy_user_image, type: 'profile_background', username: 'asdf' }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do let!(:user) { log_in } it 'raises an error when you don\'t have permission to clear the profile background' do Guardian.any_instance.expects(:can_edit?).with(user).returns(false) xhr :delete, :destroy_user_image, username: user.username, type: 'profile_background' expect(response).to be_forbidden end it "requires the `type` param" do expect { xhr :delete, :destroy_user_image, username: user.username }.to raise_error(ActionController::ParameterMissing) end it "only allows certain `types`" do expect { xhr :delete, :destroy_user_image, username: user.username, type: 'wat' }.to raise_error(Discourse::InvalidParameters) end it 'can clear the profile background' do xhr :delete, :destroy_user_image, type: 'profile_background', username: user.username expect(user.reload.user_profile.profile_background).to eq("") expect(response).to be_success end end end describe '.destroy' do it 'raises an error when not logged in' do expect { xhr :delete, :destroy, username: 'nobody' }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do let!(:user) { log_in } it 'raises an error when you cannot delete your account' do Guardian.any_instance.stubs(:can_delete_user?).returns(false) UserDestroyer.any_instance.expects(:destroy).never xhr :delete, :destroy, username: user.username expect(response).to be_forbidden end it "raises an error when you try to delete someone else's account" do UserDestroyer.any_instance.expects(:destroy).never xhr :delete, :destroy, username: Fabricate(:user).username expect(response).to be_forbidden end it "deletes your account when you're allowed to" do Guardian.any_instance.stubs(:can_delete_user?).returns(true) UserDestroyer.any_instance.expects(:destroy).with(user, anything).returns(user) xhr :delete, :destroy, username: user.username expect(response).to be_success end end end describe '.my_redirect' do it "redirects if the user is not logged in" do get :my_redirect, path: "wat" expect(response).not_to be_success expect(response).to be_redirect end context "when the user is logged in" do let!(:user) { log_in } it "will not redirect to an invalid path" do get :my_redirect, path: "wat/..password.txt" expect(response).not_to be_redirect end it "will redirect to an valid path" do get :my_redirect, path: "preferences" expect(response).to be_redirect end it "permits forward slashes" do get :my_redirect, path: "activity/posts" expect(response).to be_redirect end end end describe '.check_emails' do it 'raises an error when not logged in' do expect { xhr :put, :check_emails, username: 'zogstrip' }.to raise_error(Discourse::NotLoggedIn) end context 'while logged in' do let!(:user) { log_in } it "raises an error when you aren't allowed to check emails" do Guardian.any_instance.expects(:can_check_emails?).returns(false) xhr :put, :check_emails, username: Fabricate(:user).username expect(response).to be_forbidden end it "returns both email and associated_accounts when you're allowed to see them" do Guardian.any_instance.expects(:can_check_emails?).returns(true) xhr :put, :check_emails, username: Fabricate(:user).username expect(response).to be_success json = JSON.parse(response.body) expect(json["email"]).to be_present expect(json["associated_accounts"]).to be_present end it "works on inactive users" do inactive_user = Fabricate(:user, active: false) Guardian.any_instance.expects(:can_check_emails?).returns(true) xhr :put, :check_emails, username: inactive_user.username expect(response).to be_success json = JSON.parse(response.body) expect(json["email"]).to be_present expect(json["associated_accounts"]).to be_present end end end describe ".is_local_username" do let(:user) { Fabricate(:user) } let(:group) { Fabricate(:group, name: "Discourse") } let(:topic) { Fabricate(:topic) } let(:allowed_user) { Fabricate(:user) } let(:private_topic) { Fabricate(:private_message_topic, user: allowed_user) } it "finds the user" do xhr :get, :is_local_username, username: user.username expect(response).to be_success json = JSON.parse(response.body) expect(json["valid"][0]).to eq(user.username) end it "finds the group" do xhr :get, :is_local_username, username: group.name expect(response).to be_success json = JSON.parse(response.body) expect(json["valid_groups"][0]).to eq(group.name) end it "supports multiples usernames" do xhr :get, :is_local_username, usernames: [user.username, "system"] expect(response).to be_success json = JSON.parse(response.body) expect(json["valid"].size).to eq(2) end it "never includes staged accounts" do staged = Fabricate(:user, staged: true) xhr :get, :is_local_username, usernames: [staged.username] expect(response).to be_success json = JSON.parse(response.body) expect(json["valid"].size).to eq(0) end it "returns user who cannot see topic" do Guardian.any_instance.expects(:can_see?).with(topic).returns(false) xhr :get, :is_local_username, usernames: [user.username], topic_id: topic.id expect(response).to be_success json = JSON.parse(response.body) expect(json["cannot_see"].size).to eq(1) end it "never returns a user who can see the topic" do Guardian.any_instance.expects(:can_see?).with(topic).returns(true) xhr :get, :is_local_username, usernames: [user.username], topic_id: topic.id expect(response).to be_success json = JSON.parse(response.body) expect(json["cannot_see"].size).to eq(0) end it "returns user who cannot see a private topic" do Guardian.any_instance.expects(:can_see?).with(private_topic).returns(false) xhr :get, :is_local_username, usernames: [user.username], topic_id: private_topic.id expect(response).to be_success json = JSON.parse(response.body) expect(json["cannot_see"].size).to eq(1) end it "never returns a user who can see the topic" do Guardian.any_instance.expects(:can_see?).with(private_topic).returns(true) xhr :get, :is_local_username, usernames: [allowed_user.username], topic_id: private_topic.id expect(response).to be_success json = JSON.parse(response.body) expect(json["cannot_see"].size).to eq(0) end end describe '.topic_tracking_state' do let(:user){Fabricate(:user)} context 'anon' do it "raises an error on anon for topic_tracking_state" do expect{ xhr :get, :topic_tracking_state, username: user.username, format: :json }.to raise_error(Discourse::NotLoggedIn) end end context 'logged on' do it "detects new topic" do log_in_user(user) topic = Fabricate(:topic) xhr :get, :topic_tracking_state, username: user.username, format: :json states = JSON.parse(response.body) expect(states[0]["topic_id"]).to eq(topic.id) end end end describe '.summary' do it "generates summary info" do user = Fabricate(:user) create_post(user: user) xhr :get, :summary, username: user.username_lower expect(response).to be_success json = JSON.parse(response.body) expect(json["user_summary"]["topic_count"]).to eq(1) expect(json["user_summary"]["post_count"]).to eq(1) end end end