From f624b4e32ffe91b01f5947e178703d3750d5cc65 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 12 Nov 2012 06:59:43 -0600 Subject: [PATCH] * VRFS-72 complete --- Gemfile | 2 + app/controllers/api_users_controller.rb | 56 +++++++++++++++++++------ app/controllers/users_controller.rb | 56 +++++++++++++++++++++---- app/helpers/application_helper.rb | 4 ++ app/views/api_users/show.rabl | 2 +- app/views/api_users/signup_confirm.rabl | 3 ++ app/views/users/email_sent.html.erb | 8 ++++ app/views/users/new.html.erb | 11 +++++ app/views/users/signup_confirm.html.erb | 8 ++++ config/initializers/email.rb | 11 +++++ config/initializers/recaptcha.rb | 17 ++++++++ config/routes.rb | 4 ++ spec/factories.rb | 5 +++ spec/requests/search_api_spec.rb | 6 +-- spec/requests/user_pages_spec.rb | 8 ++-- spec/requests/users_api_spec.rb | 50 ++++++++++++++++++++++ spec/spec_helper.rb | 4 ++ 17 files changed, 227 insertions(+), 28 deletions(-) create mode 100644 app/views/api_users/signup_confirm.rabl create mode 100644 app/views/users/email_sent.html.erb create mode 100644 app/views/users/signup_confirm.html.erb create mode 100644 config/initializers/email.rb create mode 100644 config/initializers/recaptcha.rb create mode 100644 spec/requests/users_api_spec.rb diff --git a/Gemfile b/Gemfile index f5a1d3c16..23f73e8bd 100644 --- a/Gemfile +++ b/Gemfile @@ -25,6 +25,8 @@ gem 'eventmachine' gem 'amqp' gem 'logging-rails', :require => 'logging/rails' gem 'tire' +gem 'sendgrid' +gem 'recaptcha' group :development, :test do gem 'rspec-rails', '2.11.0' diff --git a/app/controllers/api_users_controller.rb b/app/controllers/api_users_controller.rb index 2a3ef1ebe..3fba6553a 100644 --- a/app/controllers/api_users_controller.rb +++ b/app/controllers/api_users_controller.rb @@ -1,6 +1,6 @@ -class ApiUsersController < ApplicationController +class ApiUsersController < ApiController - before_filter :signed_in_user, only: [:index, :edit, :update, :delete, + before_filter :signed_in_user, only: [:index, :show, :edit, :update, :delete, :friend_request_index, :friend_request_show, :friend_request_create, :friend_request_update, :friend_index, :friend_destroy] @@ -8,24 +8,54 @@ class ApiUsersController < ApplicationController respond_to :json def index - @users = User.paginate(page: params[:page]) + # don't return users that aren't yet confirmed + @users = User.where('email_confirmed=TRUE').paginate(page: params[:page]) end def show - @user = User.find(params[:id]) + # don't return users that aren't yet confirmed + @user = User.where('email_confirmed=TRUE').find(params[:id]) end def create - @user = User.save(params) - - # check for errors - if @user.errors.nil? || @user.errors.size == 0 - respond_with @user, responder: ApiResponder, :location => api_user_detail_url(@user) + UserManager.active_record_transaction do |user_manager| + # sends email to email account for confirmation + @user = user_manager.signup(params[:name], + params[:email], + params[:password], + params[:password_confirmation], + params[:city], + params[:state], + params[:country], + params[:instruments], + ApplicationHelper.base_uri(request) + "/confirm") - else - raise ActiveRecord::Rollback - response.status = :unprocessable_entity - respond_with @user + # check recaptcha; if any errors seen, contribute it to the model + verify_recaptcha(:model => @user, :message => "recaptcha") + + # check for errors + unless @user.errors.any? + render :json => {}, :status => :ok # an empty response, but 200 OK + else + raise ActiveRecord::Rollback + response.status = :unprocessable_entity + respond_with @user, responder: ApiResponder + end + end + end + + def signup_confirm + UserManager.active_record_transaction do |user_manager| + + @user = user_manager.signup_confirm(params[:signup_token]) + + unless @user.errors.any? + respond_with @user, responder: ApiResponder, :location => api_user_detail_url(@user) + else + raise ActiveRecord::Rollback + response.status = :unprocessable_entity + respond_with @user, responder: ApiResponder + end end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 46988a7f2..d6bea549a 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,9 +1,10 @@ class UsersController < ApplicationController - before_filter :signed_in_user, + before_filter :signed_in_user, only: [:index, :edit, :update, :destroy] before_filter :correct_user, only: [:edit, :update] before_filter :admin_user, only: :destroy + def index @users = User.paginate(page: params[:page]) end @@ -17,16 +18,57 @@ class UsersController < ApplicationController end def create - @user = User.new(params[:jam_ruby_user]) - if @user.save - sign_in @user - flash[:success] = "Welcome to Jamkazam!" - redirect_to @user - else + + @user = User.new + + # check recaptcha; if any errors seen, contribute it to the model + unless verify_recaptcha(:model => @user, :message => "recaptcha") + # let the template render errors on the user model render 'new' + else + # sends email to email account for confirmation + @user = UserManager.new.signup(params[:jam_ruby_user][:name], + params[:jam_ruby_user][:email], + params[:jam_ruby_user][:password], + params[:jam_ruby_user][:password_confirmation], + params[:jam_ruby_user][:city], + params[:jam_ruby_user][:state], + params[:jam_ruby_user][:country], + params[:jam_ruby_user][:instruments], + ApplicationHelper.base_uri(request) + "/confirm") + + + # check for errors + if @user.errors.any? + # render any @user.errors on error + render 'new' + else + # if success, redirect to 'email_sent' page + flash[:success] = "Please check your email and confirm your signup" + redirect_to :email_sent + end end end + def email_sent + + end + + def signup_confirm + begin + @user = UserManager.new.signup_confirm(params[:signup_token]) + rescue ActiveRecord::RecordNotFound + @user = nil + end + + unless @user.nil? || @user.errors.any? + sign_in @user + redirect_to :client + end + + # let errors fall through to signup_confirm.html.erb + end + def edit end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 9d88e495a..88fe36dae 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -9,4 +9,8 @@ module ApplicationHelper "#{base_title} | #{page_title}" end end + + def self.base_uri(request) + (request.ssl? ? "https://" : "http://") + request.host_with_port + end end diff --git a/app/views/api_users/show.rabl b/app/views/api_users/show.rabl index a4589cac9..e4bfc855f 100644 --- a/app/views/api_users/show.rabl +++ b/app/views/api_users/show.rabl @@ -1,6 +1,6 @@ object @user -attributes :id, :name, :city, :state, :country, :email, :online, :photo_url, :friend_count, :follower_count, :following_count +attributes :id, :name, :city, :state, :country, :online, :photo_url, :friend_count, :follower_count, :following_count unless @user.friends.nil? || @user.friends.size == 0 child :friends => :friends do diff --git a/app/views/api_users/signup_confirm.rabl b/app/views/api_users/signup_confirm.rabl new file mode 100644 index 000000000..e7df79f18 --- /dev/null +++ b/app/views/api_users/signup_confirm.rabl @@ -0,0 +1,3 @@ +object @user + +extends "api_users/show" \ No newline at end of file diff --git a/app/views/users/email_sent.html.erb b/app/views/users/email_sent.html.erb new file mode 100644 index 000000000..83d199210 --- /dev/null +++ b/app/views/users/email_sent.html.erb @@ -0,0 +1,8 @@ +<% provide(:title, 'Confirmation Email Sent') %> +

Confirm Email Next

+ +
+
+ Do it. +
+
\ No newline at end of file diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb index 30f15fb1a..aa648c7b1 100644 --- a/app/views/users/new.html.erb +++ b/app/views/users/new.html.erb @@ -11,12 +11,23 @@ <%= f.label :email %> <%= f.text_field :email %> + <%= f.label :city %> + <%= f.text_field :city %> + + <%= f.label :state %> + <%= f.text_field :state %> + + <%= f.label :country %> + <%= f.text_field :country %> + <%= f.label :password %> <%= f.password_field :password %> <%= f.label :password_confirmation, "Confirmation" %> <%= f.password_field :password_confirmation %> + <%= recaptcha_tags %> + <%= f.submit "Create my account", class: "btn btn-large btn-primary" %> <% end %> diff --git a/app/views/users/signup_confirm.html.erb b/app/views/users/signup_confirm.html.erb new file mode 100644 index 000000000..d36e92765 --- /dev/null +++ b/app/views/users/signup_confirm.html.erb @@ -0,0 +1,8 @@ +<% provide(:title, 'Signup Confirmation') %> +

Signup Confirmation Failure

+ +
+
+ Unable to confirm registration email +
+
\ No newline at end of file diff --git a/config/initializers/email.rb b/config/initializers/email.rb new file mode 100644 index 000000000..818dc6e67 --- /dev/null +++ b/config/initializers/email.rb @@ -0,0 +1,11 @@ +ActionMailer::Base.raise_delivery_errors = true +ActionMailer::Base.delivery_method = Rails.env == "test" ? :test : :smtp +ActionMailer::Base.smtp_settings = { + :address => "smtp.sendgrid.net", + :port => 587, + :domain => "www.jamkazam.com", + :authentication => :plain, + :user_name => "jamkazam", + :password => "jamjamblueberryjam", + :enable_starttls_auto => true +} \ No newline at end of file diff --git a/config/initializers/recaptcha.rb b/config/initializers/recaptcha.rb new file mode 100644 index 000000000..86767ff86 --- /dev/null +++ b/config/initializers/recaptcha.rb @@ -0,0 +1,17 @@ +# this gem turns recaptcha verification off during tests by default. +# The public key/private keys shown below valid for all jamkazam.com domains +# note that all recaptcha keys work on localhost/127.0.0.1 +# the keys are created at https://www.google.com/recaptcha/admin/create +Recaptcha.configure do |config| + # created using seth@jamkazam.com; can't see way to delegate + config.public_key = '6Let8dgSAAAAAFheKGWrs6iaq_hIlPOZ2f3Bb56B' + config.private_key = '6Let8dgSAAAAAJzFxL9w2QR5auxjk0ol1_xAtOGO' + + # other config options available with this gem: + #nonssl_api_server_url, + #ssl_api_server_url, + #verify_url, + #skip_verify_env, + #proxy, + #handle_timeouts_gracefully +end \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index c861a0482..52318f41a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -14,6 +14,7 @@ SampleApp::Application.routes.draw do root to: 'static_pages#home' match '/signup', to: 'users#new' + match '/email_sent', to: 'users#email_sent' match '/signin', to: 'sessions#new' match '/signout', to: 'sessions#destroy', via: :delete @@ -23,6 +24,8 @@ SampleApp::Application.routes.draw do match '/client', to: 'clients#index' + match '/confirm/:signup_token', to: 'users#signup_confirm' + scope '/api' do # music sessions match '/sessions/:id/participants' => 'api_music_sessions#participant_create', :via => :post @@ -42,6 +45,7 @@ SampleApp::Application.routes.draw do match '/users' => 'api_users#create', :via => :post match '/users/:id' => 'api_users#update', :via => :post match '/users/:id' => 'api_users#destroy', :via => :delete + match '/users/confirm/:signup_token' => 'api_users#signup_confirm', :via => :post, :as => 'api_signup_confirmation' # friend requests match '/users/:id/friend_requests' => 'api_users#friend_request_index', :via => :get diff --git a/spec/factories.rb b/spec/factories.rb index e93d3c3e7..dc5552132 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -4,6 +4,7 @@ FactoryGirl.define do sequence(:email) { |n| "person_#{n}@example.com"} password "foobar" password_confirmation "foobar" + email_confirmed true factory :admin do admin true @@ -26,4 +27,8 @@ FactoryGirl.define do factory :invitation, :class => JamRuby::Invitation do end + + factory :band, :class => JamRuby::Band do + + end end diff --git a/spec/requests/search_api_spec.rb b/spec/requests/search_api_spec.rb index 8ad5cc24c..9dfc98f89 100644 --- a/spec/requests/search_api_spec.rb +++ b/spec/requests/search_api_spec.rb @@ -28,10 +28,8 @@ describe "Search API ", :type => :api do it "simple search" do User.delete_search_index # so that the user created before the test and logged in doesn't show up User.create_search_index - @musician = User.save(name: "Peach", email: "user@example.com", - password: "foobar", password_confirmation: "foobar", musician: true) - @fan = User.save(name: "Peach Peach", email: "fan@example.com", - password: "foobar", password_confirmation: "foobar", musician: false) + @musician = FactoryGirl.create(:user, name: "Peach", email: "user@example.com", musician: true) + @fan = FactoryGirl.create(:user, name: "Peach Peach", email: "fan@example.com", musician: false) @band = Band.save(name: "Peach pit", website: "www.bands.com", biography: "zomg we rock") @band2 = Band.save(name: "Peach", website: "www.bands2.com", biography: "zomg we rock") User.search_index.refresh diff --git a/spec/requests/user_pages_spec.rb b/spec/requests/user_pages_spec.rb index eb1f7f461..c9d8d122b 100644 --- a/spec/requests/user_pages_spec.rb +++ b/spec/requests/user_pages_spec.rb @@ -114,6 +114,9 @@ describe "User pages" do before do fill_in "Name", with: "Example User" fill_in "Email", with: "user@example.com" + fill_in "City", with: "Austin" + fill_in "State", with: "TX" + fill_in "Country", with: "USA" fill_in "Password", with: "foobar" fill_in "Confirmation", with: "foobar" end @@ -127,9 +130,8 @@ describe "User pages" do let(:user) { User.find_by_email('user@example.com') } - it { should have_selector('title', text: user.name) } - it { should have_selector('div.alert.alert-success', text: 'Welcome') } - it { should have_link('Sign out') } + it { should have_selector('title', text: "Confirmation Email Sent") } + it { should have_selector('div.alert.alert-success', text: 'check your email') } end end end diff --git a/spec/requests/users_api_spec.rb b/spec/requests/users_api_spec.rb new file mode 100644 index 000000000..a854936cc --- /dev/null +++ b/spec/requests/users_api_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe "User API ", :type => :api do + + include Rack::Test::Methods + + subject { page } + + def login(user) + post '/sessions', "session[email]" => user.email, "session[password]" => user.password + rack_mock_session.cookie_jar["remember_token"].should == user.remember_token + end + + describe "profile page" do + let(:user) { FactoryGirl.create(:user) } + + before(:each) do + UserMailer.deliveries.clear + end + + it "successful signup" do + post '/api/users.json', { :name => "user1", :email => "user1@jamkazam.com", :password => "jam123", :password_confirmation => "jam123", + :city => "Austin", :state => "TX", :country => "United States" }.to_json, "CONTENT_TYPE" => 'application/json' + last_response.status.should == 200 + last_response.body.should == "{}" + + created_user = User.find_by_email("user1@jamkazam.com") + + # login as another user, and verify that this user can't be seen yet because email_confired=false + login(user) + get "/api/users/#{created_user.id}.json" + last_response.status.should == 404 + + # we should see one email created as a result of creating the user + UserMailer.deliveries.length.should == 1 + email = UserMailer.deliveries[0] + + # check that the signup url is in the email + email.html_part.body.include?(created_user.signup_token).should be_true + + post "/api/users/confirm/#{created_user.signup_token}.json", "{}" + last_response.status.should == 201 + + get last_response.headers["Location"] + ".json" + last_response.status.should == 200 + found_user = JSON.parse(last_response.body) + found_user["id"].should == created_user.id + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0232d68a7..664966e7a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,11 +4,15 @@ require 'spork' #require 'spork/ext/ruby-debug' require 'active_record' +require 'action_mailer' require 'jam_db' require 'spec_db' include JamRuby +# put ActionMailer into test mode +ActionMailer::Base.delivery_method = :test + # recreate test database and migrate it db_config = YAML::load(File.open('config/database.yml'))["test"] SpecDb::recreate_database(db_config)