diff --git a/.gitignore b/.gitignore index 8ac4e2e21..703cf4cd4 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ log/test.log.age target vendor/bundle public/assets +public/uploads diff --git a/app/assets/images/email/header.png b/app/assets/images/email/header.png new file mode 100644 index 000000000..d1837ce24 Binary files /dev/null and b/app/assets/images/email/header.png differ diff --git a/app/controllers/api_users_controller.rb b/app/controllers/api_users_controller.rb index f5cfd4c84..8c5f6df60 100644 --- a/app/controllers/api_users_controller.rb +++ b/app/controllers/api_users_controller.rb @@ -1,6 +1,6 @@ class ApiUsersController < ApiController - before_filter :api_signed_in_user, :except => [:create, :signup_confirm, :auth_session_create, :complete] + before_filter :api_signed_in_user, :except => [:create, :signup_confirm, :auth_session_create, :complete, :finalize_update_email] before_filter :auth_user, :only => [:session_settings_show, :session_history_index, :session_user_history_index, :update, :delete, :like_create, :like_destroy, # likes :following_create, :following_show, :following_destroy, # followings @@ -10,7 +10,7 @@ class ApiUsersController < ApiController :friend_show, :friend_destroy, # friends :notification_index, :notification_destroy, # notifications :band_invitation_index, :band_invitation_show, :band_invitation_update, # band invitations - :set_password] + :set_password, :begin_update_email] respond_to :json @@ -393,6 +393,32 @@ class ApiUsersController < ApiController end end + ###################### ACCOUNT SETTINGS ################# + def begin_update_email + # begins email update by sending an email for the user to confirm their new email + + # NOTE: if you change confirm_email_link value below, you break outstanding email changes because links in user inboxes are broken + confirm_email_link = confirm_email_url + "?token=" + + current_user.begin_update_email(params[:update_email], params[:password_validation], confirm_email_link) + + if current_user.errors.any? + response.status = :unprocessable_entity + respond_with current_user + else + respond_with current_user, responder: ApiResponder, status: 200 + end + end + + def finalize_update_email + # used when the user goes to the confirmation link in their email + @user = User.finalize_update_email(params[:token]) + + sign_in(@user) + + respond_with current_user, responder: ApiResponder, status: 200 + end + ###################### RECORDINGS ####################### # def recording_index # @recordings = User.recording_index(current_user, params[:id]) diff --git a/config/application.rb b/config/application.rb index f45ebd14b..acade1db4 100644 --- a/config/application.rb +++ b/config/application.rb @@ -38,7 +38,7 @@ if defined?(Bundler) # Activate observers that should always be running. # config.active_record.observers = :cacher, :garbage_collector, :forum_observer - config.active_record.observers = "JamRuby::InvitedUserObserver" + config.active_record.observers = "JamRuby::InvitedUserObserver", "JamRuby::UserObserver" # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. diff --git a/config/routes.rb b/config/routes.rb index 4f5c6e5ae..fa4680710 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -42,6 +42,9 @@ SampleApp::Application.routes.draw do match '/reset_password_token' => 'users#reset_password_token', :via => :get match '/reset_password_complete' => 'users#reset_password_complete', :via => :post + # email update + match '/confirm_email' => 'users#confirm_email', :via => 'confirm_email' # NOTE: if you change this, you break outstanding email changes because links in user inboxes are broken + scope '/api' do # music sessions match '/sessions/:id/participants' => 'api_music_sessions#participant_create', :via => :post @@ -136,6 +139,11 @@ SampleApp::Application.routes.draw do match '/users/:id/band_invitations/:invitation_id' => 'api_users#band_invitation_show', :via => :get, :as => 'api_user_band_invitation_detail' match '/users/:id/band_invitations/:invitation_id' => 'api_users#band_invitation_update', :via => :post + # user account settings + match '/users/:id/update_email' => 'api_users#begin_update_email', :via => :post, :as => 'begin_update_email' + match '/users/update_email/:token' => 'api_users#finalize_update_email', :via => :post, :as => 'finalize_update_email' + + # user recordings # match '/users/:id/recordings' => 'api_users#recording_index', :via => :get # match '/users/:id/recordings/:recording_id' => 'api_users#recording_show', :via => :get, :as => 'api_recording_detail' diff --git a/spec/requests/users_api_spec.rb b/spec/requests/users_api_spec.rb index 6b962011a..141d8b8f2 100644 --- a/spec/requests/users_api_spec.rb +++ b/spec/requests/users_api_spec.rb @@ -235,6 +235,19 @@ describe "User API", :type => :api do return last_response end + #################### ACCOUNT SETTINGS ########################### + def begin_update_email(authenticated_user, update_email, validation_password, login = true) + login(authenticated_user.email, authenticated_user.password, 200, true) if login + post "/api/users/#{authenticated_user.id}/update_email.json", { :update_email => update_email, :password_validation => validation_password }.to_json, "CONTENT_TYPE" => 'application/json' + return last_response + end + + def finalize_update_email(update_email_token) + post "/api/users/update_email/#{update_email_token}.json", {}.to_json, "CONTENT_TYPE" => 'application/json' + return last_response + end + + context "when accessing as unauthenticated user" do it "should allow successful login" do @@ -889,5 +902,54 @@ describe "User API", :type => :api do last_response = update_band_invitation(recipient, recipient, invitation["id"], false) end end + + ########## UPDATE EMAIL ######## + describe "update email" do + describe "begin update email" do + it "success" do + last_response = begin_update_email(user, "not_taken_test@jamkazam.com", user.password) + last_response.status.should == 200 + UserMailer.deliveries.length.should == 1 + end + + it "bad args" do + last_response = begin_update_email(user, "not_taken_test@jamkazam.com", 'wrong_password') + last_response.status.should == 422 + UserMailer.deliveries.length.should == 0 + end + + it "no session" do + last_response = begin_update_email(user, "not_taken_test@jamkazam.com", user.password, login=false) + last_response.status.should == 403 + UserMailer.deliveries.length.should == 0 + end + + it "bad user" do + login(user.email, user.password, 200, true) + post "/api/users/someone_other_uuid/update_email.json", { :update_email => "not_taken_test@jamkazam.com", :password_validation => user.password}.to_json, "CONTENT_TYPE" => 'application/json' + last_response.status.should == 403 + UserMailer.deliveries.length.should == 0 + end + end + + describe "finalize update email" do + it "success" do + begin_update_email(user, "not_taken_test@jamkazam.com", user.password) + logout + user.reload + last_response = finalize_update_email(user.update_email_token) + last_response.status.should == 200 + UserMailer.deliveries.length.should == 2 # one for begin, one for finalize + end + + it "bad token" do + last_response = finalize_update_email('junk') + last_response.status.should == 404 + UserMailer.deliveries.length.should == 00 + end + end + + + end end end