From fbc6559ad0564909f97dd291a47cf10cc0083eb3 Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Tue, 8 Dec 2015 22:35:51 +0300 Subject: [PATCH 01/34] Authentication via social networks - copy common part from template --- .ruby-version | 2 +- Gemfile | 4 +- Gemfile.lock | 278 ++++++++++-------- .../omniauth_callbacks_controller.rb | 2 + app/controllers/social_profiles_controller.rb | 21 ++ app/helpers/omniauth_helper.rb | 5 + app/interactors/connect_account_organizer.rb | 39 +++ app/interactors/connect_social_profile.rb | 22 ++ app/models/social_profile.rb | 12 + app/models/user.rb | 11 +- app/policies/auth_verification_policy.rb | 32 ++ .../application/_navigation_user.html.slim | 6 + app/views/social_profiles/_list.html.slim | 14 + app/views/users/registrations/edit.html.slim | 2 + config/initializers/devise.rb | 2 + config/locales/models/social_profile.en.yml | 7 + config/locales/oauth.en.yml | 5 + config/routes.rb | 3 +- db/schema.rb | 22 +- spec/factories/social_profiles.rb | 7 + spec/models/social_profiles_spec.rb | 11 + spec/models/user_spec.rb | 1 + .../policies/auth_verification_policy_spec.rb | 43 +++ 23 files changed, 414 insertions(+), 137 deletions(-) create mode 100644 app/controllers/omniauth_callbacks_controller.rb create mode 100644 app/controllers/social_profiles_controller.rb create mode 100644 app/helpers/omniauth_helper.rb create mode 100644 app/interactors/connect_account_organizer.rb create mode 100644 app/interactors/connect_social_profile.rb create mode 100644 app/models/social_profile.rb create mode 100644 app/policies/auth_verification_policy.rb create mode 100644 app/views/social_profiles/_list.html.slim create mode 100644 config/locales/models/social_profile.en.yml create mode 100644 config/locales/oauth.en.yml create mode 100644 spec/factories/social_profiles.rb create mode 100644 spec/models/social_profiles_spec.rb create mode 100644 spec/policies/auth_verification_policy_spec.rb diff --git a/.ruby-version b/.ruby-version index b1b25a5f..58594069 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.2.2 +2.2.3 diff --git a/Gemfile b/Gemfile index f5443231..5aee83b0 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source "https://rubygems.org" -ruby "2.2.2" +ruby "2.2.3" gem "rails", "4.2.3" gem "pg" @@ -28,6 +28,8 @@ gem "devise" gem "google-analytics-rails" gem "interactor" gem "kaminari" +gem "omniauth-facebook" +gem "omniauth-google-oauth2" gem "responders" gem "rollbar", "~> 0.10.3" gem "seedbank" diff --git a/Gemfile.lock b/Gemfile.lock index 3fd62acb..d6d948f1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -20,7 +20,7 @@ GEM erubis (~> 2.7.0) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - active_link_to (1.0.2) + active_link_to (1.0.3) actionpack activejob (4.2.3) activesupport (= 4.2.3) @@ -38,73 +38,71 @@ GEM minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - addressable (2.3.6) + addressable (2.3.8) arel (6.0.3) - ast (2.0.0) - astrolabe (1.3.0) - parser (>= 2.2.0.pre.3, < 3.0) - autoprefixer-rails (4.0.2.2) + ast (2.1.0) + astrolabe (1.3.1) + parser (~> 2.2) + autoprefixer-rails (6.0.3) execjs + json awesome_print (1.6.1) bcrypt (3.1.10) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) - brakeman (3.0.3) + brakeman (3.1.1) erubis (~> 2.6) fastercsv (~> 1.5) haml (>= 3.0, < 5.0) - highline (~> 1.6.20) + highline (~> 1.6) multi_json (~> 1.2) - ruby2ruby (~> 2.1.1) - ruby_parser (~> 3.6.2) + ruby2ruby (>= 2.1.1, < 2.3.0) + ruby_parser (~> 3.7.0) sass (~> 3.0) + slim (>= 1.3.6, < 4.0) terminal-table (~> 1.4) builder (3.2.2) - bullet (4.14.7) + bullet (4.14.9) activesupport (>= 3.0.0) uniform_notifier (~> 1.9.0) - bundler-audit (0.3.1) + bundler-audit (0.4.0) bundler (~> 1.2) thor (~> 0.18) - byebug (3.1.2) - columnize (~> 0.8) - debugger-linecache (~> 1.2) - capybara (2.4.4) + byebug (6.0.2) + capybara (2.5.0) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - capybara-webkit (1.3.1) - capybara (>= 2.0.2, < 2.5.0) + capybara-webkit (1.7.1) + capybara (>= 2.3.0, < 2.6.0) json choice (0.2.0) code_analyzer (0.4.5) sexp_processor - codeclimate-test-reporter (0.4.7) + codeclimate-test-reporter (0.4.8) simplecov (>= 0.7.1, < 1.0.0) coderay (1.1.0) - coffee-rails (4.0.1) + coffee-rails (4.1.0) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.0) - coffee-script (2.2.0) + coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.7.0) - coffeelint (1.9.1) + coffee-script-source (1.9.1.1) + coffeelint (1.11.0) coffee-script execjs json colored (1.2) - columnize (0.8.9) crack (0.4.2) safe_yaml (~> 1.0.0) - daemons (1.1.9) - database_cleaner (1.3.0) + daemons (1.2.3) + database_cleaner (1.5.0) debug_inspector (0.0.2) - debugger-linecache (1.2.0) - decent_exposure (2.3.1) - devise (3.4.1) + decent_exposure (2.3.2) + devise (3.5.2) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 3.2.6, < 5) @@ -113,15 +111,16 @@ GEM warden (~> 1.2.3) diff-lcs (1.2.5) docile (1.1.5) - dotenv (0.9.0) - dotenv-rails (0.9.0) - dotenv (= 0.9.0) + dotenv (2.0.2) + dotenv-rails (2.0.2) + dotenv (= 2.0.2) + railties (~> 4.0) email_spec (1.6.0) launchy (~> 2.1) mail (~> 2.2) erubis (2.7.0) - eventmachine (1.0.7) - execjs (2.2.2) + eventmachine (1.0.8) + execjs (2.6.0) factory_girl (4.5.0) activesupport (>= 3.0.0) factory_girl_rails (4.5.0) @@ -129,51 +128,54 @@ GEM railties (>= 3.0.0) faker (1.5.0) i18n (~> 0.5) + faraday (0.9.2) + multipart-post (>= 1.2, < 3) fastercsv (1.5.5) - foreman (0.63.0) - dotenv (>= 0.7) - thor (>= 0.13.6) - formulaic (0.2.0) + foreman (0.78.0) + thor (~> 0.19.1) + formulaic (0.3.0) activesupport capybara i18n foundation-icons-sass-rails (3.0.0) railties (>= 3.1.1) sass-rails (>= 3.1.1) - foundation-rails (5.4.5.0) + foundation-rails (5.5.3.2) railties (>= 3.1.0) - sass (>= 3.2.0) - fuubar (2.0.0.rc1) - rspec (~> 3.0.rc1) + sass (>= 3.3.0, < 3.5) + fuubar (2.0.0) + rspec (~> 3.0) ruby-progressbar (~> 1.4) globalid (0.3.6) activesupport (>= 4.1.0) google-analytics-rails (0.0.6) - haml (4.0.6) + haml (4.0.7) tilt - highline (1.6.21) + hashie (3.4.2) + highline (1.7.8) i18n (0.7.0) interactor (3.1.0) - jasmine (2.3.0) + jasmine (2.3.1) jasmine-core (~> 2.3) phantomjs rack (>= 1.2.1) rake - jasmine-core (2.3.0) + jasmine-core (2.3.4) jasmine-jquery-rails (2.0.3) - jquery-rails (4.0.4) + jquery-rails (4.0.5) rails-dom-testing (~> 1.0) railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (1.8.3) + jwt (1.5.1) kaminari (0.16.3) actionpack (>= 3.0.0) activesupport (>= 3.0.0) - launchy (2.4.2) + launchy (2.4.3) addressable (~> 2.3) - letter_opener (1.2.0) + letter_opener (1.4.1) launchy (~> 2.2) - libv8 (3.16.14.7) + libv8 (3.16.14.11) loofah (2.0.3) nokogiri (>= 1.5.9) mail (2.6.3) @@ -181,27 +183,49 @@ GEM metamagic (3.1.7) rails (>= 3.0.0) method_source (0.8.2) - mime-types (2.6.1) + mime-types (2.6.2) mini_portile (0.6.2) - minitest (5.8.0) - multi_json (1.11.0) + minitest (5.8.1) + multi_json (1.11.2) + multi_xml (0.5.5) + multipart-post (2.0.0) nokogiri (1.6.6.2) mini_portile (~> 0.6.0) + oauth2 (1.0.0) + faraday (>= 0.8, < 0.10) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (~> 1.2) + omniauth (1.2.2) + hashie (>= 1.2, < 4) + rack (~> 1.0) + omniauth-facebook (2.0.1) + omniauth-oauth2 (~> 1.2) + omniauth-google-oauth2 (0.2.8) + addressable (~> 2.3) + jwt (~> 1.0) + multi_json (~> 1.3) + omniauth (>= 1.1.1) + omniauth-oauth2 (>= 1.1.1) + omniauth-oauth2 (1.3.1) + oauth2 (~> 1.0) + omniauth (~> 1.2) orm_adapter (0.5.0) - parser (2.2.2.3) + parser (2.2.3.0) ast (>= 1.1, < 3.0) - pg (0.17.1) + pg (0.18.3) phantomjs (1.9.8.0) powerpack (0.1.1) - pry (0.9.12.6) - coderay (~> 1.0) - method_source (~> 0.8) + pry (0.10.2) + coderay (~> 1.1.0) + method_source (~> 0.8.1) slop (~> 3.4) - pry-rails (0.3.2) + pry-rails (0.3.4) pry (>= 0.9.10) - pundit (1.0.0) + pundit (1.0.1) activesupport (>= 3.0.0) - quiet_assets (1.0.2) + quiet_assets (1.1.0) railties (>= 3.1, < 5.0) rack (1.6.4) rack-canonical-host (0.1.0) @@ -226,14 +250,14 @@ GEM activesupport (>= 4.2.0.beta, < 5.0) nokogiri (~> 1.6.0) rails-deprecated_sanitizer (>= 1.0.1) - rails-erd (1.4.1) + rails-erd (1.4.3) activerecord (>= 3.2) activesupport (>= 3.2) choice (~> 0.2.0) ruby-graphviz (~> 1.2) rails-html-sanitizer (1.0.2) loofah (~> 2.0) - rails_12factor (0.0.2) + rails_12factor (0.0.3) rails_serve_static_assets rails_stdout_logging rails_best_practices (1.15.7) @@ -245,8 +269,8 @@ GEM json require_all ruby-progressbar - rails_serve_static_assets (0.0.2) - rails_stdout_logging (0.0.3) + rails_serve_static_assets (0.0.4) + rails_stdout_logging (0.0.4) railties (4.2.3) actionpack (= 4.2.3) activesupport (= 4.2.3) @@ -254,61 +278,62 @@ GEM thor (>= 0.18.1, < 2.0) rainbow (2.0.0) rake (10.4.2) - ref (1.0.5) + ref (2.0.0) require_all (1.3.2) responders (2.1.0) railties (>= 4.2.0, < 5) rollbar (0.10.14) multi_json (~> 1.5) - rspec (3.0.0) - rspec-core (~> 3.0.0) - rspec-expectations (~> 3.0.0) - rspec-mocks (~> 3.0.0) - rspec-core (3.0.2) - rspec-support (~> 3.0.0) - rspec-expectations (3.0.2) + rspec (3.3.0) + rspec-core (~> 3.3.0) + rspec-expectations (~> 3.3.0) + rspec-mocks (~> 3.3.0) + rspec-core (3.3.2) + rspec-support (~> 3.3.0) + rspec-expectations (3.3.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.0.0) - rspec-mocks (3.0.2) - rspec-support (~> 3.0.0) - rspec-rails (3.0.1) - actionpack (>= 3.0) - activesupport (>= 3.0) - railties (>= 3.0) - rspec-core (~> 3.0.0) - rspec-expectations (~> 3.0.0) - rspec-mocks (~> 3.0.0) - rspec-support (~> 3.0.0) - rspec-support (3.0.2) - rubocop (0.31.0) + rspec-support (~> 3.3.0) + rspec-mocks (3.3.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.3.0) + rspec-rails (3.3.3) + actionpack (>= 3.0, < 4.3) + activesupport (>= 3.0, < 4.3) + railties (>= 3.0, < 4.3) + rspec-core (~> 3.3.0) + rspec-expectations (~> 3.3.0) + rspec-mocks (~> 3.3.0) + rspec-support (~> 3.3.0) + rspec-support (3.3.0) + rubocop (0.34.2) astrolabe (~> 1.3) - parser (>= 2.2.2.1, < 3.0) + parser (>= 2.2.2.5, < 3.0) powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.4) ruby-graphviz (1.2.2) ruby-progressbar (1.7.5) - ruby2ruby (2.1.4) + ruby2ruby (2.2.0) ruby_parser (~> 3.1) sexp_processor (~> 4.0) - ruby_parser (3.6.6) + ruby_parser (3.7.1) sexp_processor (~> 4.1) - safe_yaml (1.0.1) - sass (3.4.13) - sass-rails (5.0.3) + safe_yaml (1.0.4) + sass (3.4.19) + sass-rails (5.0.4) railties (>= 4.0.0, < 5.0) sass (~> 3.1) sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) - tilt (~> 1.1) - scss_lint (0.38.0) + tilt (>= 1.1, < 3) + scss_lint (0.42.2) rainbow (~> 2.0) - sass (~> 3.4.1) + sass (~> 3.4.15) seedbank (0.3.0) - sexp_processor (4.5.1) - shoulda-matchers (2.8.0) - activesupport (>= 3.0.0) - simple_form (3.1.0) + sexp_processor (4.6.0) + shoulda-matchers (3.0.0) + activesupport (>= 4.0.0) + simple_form (3.2.0) actionpack (~> 4.0) activemodel (~> 4.0) simplecov (0.10.0) @@ -316,53 +341,57 @@ GEM json (~> 1.8) simplecov-html (~> 0.10.0) simplecov-html (0.10.0) - skim (0.9.3) + skim (0.10.0) coffee-script coffee-script-source (>= 1.2.0) - slim (~> 2.0.0) - sprockets - slim (2.0.3) - temple (~> 0.6.6) + slim (>= 3.0) + sprockets (>= 2, < 4) + slim (3.0.6) + temple (~> 0.7.3) tilt (>= 1.3.3, < 2.1) - slim-rails (0.2.1) - slim (>= 0.9.2) + slim-rails (3.0.1) + actionmailer (>= 3.1, < 5.0) + actionpack (>= 3.1, < 5.0) + activesupport (>= 3.1, < 5.0) + railties (>= 3.1, < 5.0) + slim (~> 3.0) slop (3.6.0) - spring (1.3.6) + spring (1.4.0) spring-commands-rspec (1.0.4) spring (>= 0.9.1) - sprockets (3.3.1) - rack (~> 1.0) - sprockets-rails (2.3.2) + sprockets (3.4.0) + rack (> 1, < 3) + sprockets-rails (2.3.3) actionpack (>= 3.0) activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) - temple (0.6.10) - terminal-table (1.4.5) - therubyracer (0.12.1) + temple (0.7.6) + terminal-table (1.5.2) + therubyracer (0.12.2) libv8 (~> 3.16.14.0) ref - thin (1.6.1) - daemons (>= 1.0.9) - eventmachine (>= 1.0.0) - rack (>= 1.0.0) + thin (1.6.4) + daemons (~> 1.0, >= 1.0.9) + eventmachine (~> 1.0, >= 1.0.4) + rack (~> 1.0) thor (0.19.1) thread_safe (0.3.5) - tilt (1.4.1) + tilt (2.0.1) tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (2.4.0) + uglifier (2.7.2) execjs (>= 0.3.0) json (>= 1.8.0) uniform_notifier (1.9.0) warden (1.2.3) rack (>= 1.0) - web-console (2.1.3) + web-console (2.2.1) activemodel (>= 4.0) binding_of_caller (>= 0.7.2) railties (>= 4.0) sprockets-rails (>= 2.0, < 4.0) - webmock (1.17.3) - addressable (>= 2.2.7) + webmock (1.21.0) + addressable (>= 2.3.6) crack (>= 0.3.2) xpath (2.0.0) nokogiri (~> 1.3) @@ -404,6 +433,8 @@ DEPENDENCIES launchy letter_opener metamagic + omniauth-facebook + omniauth-google-oauth2 pg pry-rails pundit @@ -432,3 +463,6 @@ DEPENDENCIES uglifier (>= 1.3.0) web-console (~> 2.0) webmock + +BUNDLED WITH + 1.10.6 diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb new file mode 100644 index 00000000..965043f0 --- /dev/null +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -0,0 +1,2 @@ +class OmniauthCallbacksController < Devise::OmniauthCallbacksController +end diff --git a/app/controllers/social_profiles_controller.rb b/app/controllers/social_profiles_controller.rb new file mode 100644 index 00000000..eac940b2 --- /dev/null +++ b/app/controllers/social_profiles_controller.rb @@ -0,0 +1,21 @@ +class SocialProfilesController < ApplicationController + before_action :authenticate_user! + + expose(:social_profiles) { current_user.social_profiles } + expose(:social_profile) + + def destroy + if social_profile.destroy + flash[:notice] = t "flash.actions.destroy.notice", resource_name: resource_name + else + flash[:alert] = t "flash.actions.destroy.alert", resource_name: resource_name + end + redirect_to edit_user_registration_url + end + + private + + def resource_name + SocialProfile.model_name.human + end +end diff --git a/app/helpers/omniauth_helper.rb b/app/helpers/omniauth_helper.rb new file mode 100644 index 00000000..88471a14 --- /dev/null +++ b/app/helpers/omniauth_helper.rb @@ -0,0 +1,5 @@ +module OmniauthHelper + def provider_name(provider) + t "active_record.attributes.social_profile.provider_name.#{provider}" + end +end diff --git a/app/interactors/connect_account_organizer.rb b/app/interactors/connect_account_organizer.rb new file mode 100644 index 00000000..5d250176 --- /dev/null +++ b/app/interactors/connect_account_organizer.rb @@ -0,0 +1,39 @@ +class ConnectAccountOrganizer + attr_reader :auth, :user + private :auth, :user + + def initialize(auth, user) + @auth, @user = auth, user + end + + def call + fail_oauth if !auth_verified? && !user.confirmed? + + connect_social_profile + process_user_confirmation unless user.confirmed? + end + + private + + def fail_oauth + fail "Please confirm your account before connecting your #{auth.provider} account." + end + + def auth_verified? + AuthVerificationPolicy.new(auth).verified? + end + + def connect_social_profile + ConnectSocialProfile.new(user, auth).call + end + + def process_user_confirmation + user.reset_password(new_password, new_password) + user.confirm! + user.send_reset_password_instructions + end + + def new_password + @new_password ||= Devise.friendly_token.first(8) + end +end diff --git a/app/interactors/connect_social_profile.rb b/app/interactors/connect_social_profile.rb new file mode 100644 index 00000000..120c29cc --- /dev/null +++ b/app/interactors/connect_social_profile.rb @@ -0,0 +1,22 @@ +class ConnectSocialProfile + attr_reader :user, :auth + private :user, :auth + + def initialize(user, auth) + @user, @auth = user, auth + end + + def call + if social_profile + social_profile.update_attribute(:user, user) + else + user.apply_omniauth(auth) && user.save! + end + end + + private + + def social_profile + @social_profile ||= SocialProfile.from_omniauth(auth) + end +end diff --git a/app/models/social_profile.rb b/app/models/social_profile.rb new file mode 100644 index 00000000..87e1087c --- /dev/null +++ b/app/models/social_profile.rb @@ -0,0 +1,12 @@ +class SocialProfile < ActiveRecord::Base + PROVIDERS = %i(facebook google_oauth2) + + belongs_to :user + + validates :user, :provider, :uid, presence: true + validates :uid, uniqueness: { scope: :provider } + + def self.from_omniauth(auth) + find_by(provider: auth.provider, uid: auth.uid) + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 521dad51..e865d2a6 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,9 +1,12 @@ class User < ActiveRecord::Base devise :database_authenticatable, :registerable, :confirmable, - :recoverable, :rememberable, :trackable, :validatable + :recoverable, :rememberable, :trackable, :validatable, + :omniauthable, omniauth_providers: SocialProfile::PROVIDERS validates :full_name, presence: true + has_many :social_profiles, dependent: :destroy + def to_s full_name end @@ -11,4 +14,10 @@ def to_s def full_name_with_email "#{self[:full_name]} (#{email})" end + + def apply_omniauth(auth) + self.email = auth["info"]["email"] if email.blank? + self.full_name = auth["info"]["name"] if full_name.blank? + social_profiles.build(provider: auth["provider"], uid: auth["uid"]) + end end diff --git a/app/policies/auth_verification_policy.rb b/app/policies/auth_verification_policy.rb new file mode 100644 index 00000000..a113c04e --- /dev/null +++ b/app/policies/auth_verification_policy.rb @@ -0,0 +1,32 @@ +class AuthVerificationPolicy + attr_reader :auth + private :auth + + def initialize(auth) + @auth = auth + end + + def verified? + request_verification_for + rescue NoMethodError + fail_with_error + end + + private + + def request_verification_for + send(auth.provider) + end + + def fail_with_error + fail ArgumentError, I18n.t("omniauth.verification.not_implemented", kind: auth.provider) + end + + def facebook + auth.info.verified? || auth.extra.raw_info.verified? + end + + def google_oauth2 + auth.extra.raw_info.email_verified? + end +end diff --git a/app/views/application/_navigation_user.html.slim b/app/views/application/_navigation_user.html.slim index f3fe4746..57edb9f1 100644 --- a/app/views/application/_navigation_user.html.slim +++ b/app/views/application/_navigation_user.html.slim @@ -13,3 +13,9 @@ ul.right - else = active_link_to 'Sign in', new_user_session_path, active: :exclusive, wrap_tag: :li = active_link_to 'Sign up', new_user_registration_path, active: :exclusive, wrap_tag: :li + + - SocialProfile::PROVIDERS.each do |provider| + = active_link_to "Sign in with #{provider_name(provider)}", + user_omniauth_authorize_path(provider), + active: :exclusive, + wrap_tag: :li \ No newline at end of file diff --git a/app/views/social_profiles/_list.html.slim b/app/views/social_profiles/_list.html.slim new file mode 100644 index 00000000..69571677 --- /dev/null +++ b/app/views/social_profiles/_list.html.slim @@ -0,0 +1,14 @@ +- if social_profiles.any? + b Successfully authorized via: + ul.js-social-profiles + - social_profiles.each do |social_profile| + li = link_to "#{provider_name(social_profile.provider)} (#{social_profile.uid.truncate(9)}). Unauthorize?", + social_profile, + data: { confirm: "Are you sure you want to remove this social profile?" }, + method: :delete, + class: "js-unauthorize" + +b Add service to sign in with: +ul + - SocialProfile::PROVIDERS.each do |provider| + li = link_to provider_name(provider), user_omniauth_authorize_path(provider) diff --git a/app/views/users/registrations/edit.html.slim b/app/views/users/registrations/edit.html.slim index 0decc952..dcd25388 100644 --- a/app/views/users/registrations/edit.html.slim +++ b/app/views/users/registrations/edit.html.slim @@ -24,6 +24,8 @@ = f.button :submit, 'Update' .medium-6.columns.end + = render "social_profiles/list", social_profiles: current_user.social_profiles + h6 b Cancel my account p diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index e5b9fb2c..0f57869c 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -230,6 +230,8 @@ # Add a new OmniAuth provider. Check the wiki for more information on setting # up on your models and hooks. # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' + config.omniauth :google_oauth2, ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"] + config.omniauth :facebook, ENV["FACEBOOK_APP_ID"], ENV["FACEBOOK_APP_SECRET"], info_fields: "email, name, verified" # ==> Warden configuration # If you want to use other strategies, that are not supported by Devise, or diff --git a/config/locales/models/social_profile.en.yml b/config/locales/models/social_profile.en.yml new file mode 100644 index 00000000..9a464125 --- /dev/null +++ b/config/locales/models/social_profile.en.yml @@ -0,0 +1,7 @@ +en: + active_record: + attributes: + social_profile: + provider_name: + google_oauth2: Google + facebook: Facebook diff --git a/config/locales/oauth.en.yml b/config/locales/oauth.en.yml new file mode 100644 index 00000000..9f69b348 --- /dev/null +++ b/config/locales/oauth.en.yml @@ -0,0 +1,5 @@ +en: + omniauth: + verification: + failure: Your %{kind} account can't be used to sign in. Please verify it via profile page. + not_implemented: Verification checking is not implemented for %{kind}. diff --git a/config/routes.rb b/config/routes.rb index 2053e363..7cedb57e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,8 @@ Rails.application.routes.draw do - devise_for :users + devise_for :users, controllers: { omniauth_callbacks: "omniauth_callbacks" } resource :feedback, only: %i(new create) + resources :social_profiles, only: :destroy with_options controller: :pages do get :about diff --git a/db/schema.rb b/db/schema.rb index 423f9aa7..3b83b322 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -17,23 +17,23 @@ enable_extension "plpgsql" create_table "users", force: :cascade do |t| - t.string "email", limit: 255, default: "", null: false - t.string "encrypted_password", limit: 255, default: "", null: false - t.string "reset_password_token", limit: 255 + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.string "confirmation_token", limit: 255 + t.string "confirmation_token" t.datetime "confirmed_at" t.datetime "confirmation_sent_at" - t.string "unconfirmed_email", limit: 255 - t.integer "sign_in_count", default: 0 + t.string "unconfirmed_email" + t.integer "sign_in_count", default: 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" - t.string "current_sign_in_ip", limit: 255 - t.string "last_sign_in_ip", limit: 255 - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "full_name", limit: 255 + t.string "current_sign_in_ip" + t.string "last_sign_in_ip" + t.datetime "created_at" + t.datetime "updated_at" + t.string "full_name" end add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree diff --git a/spec/factories/social_profiles.rb b/spec/factories/social_profiles.rb new file mode 100644 index 00000000..45755b4d --- /dev/null +++ b/spec/factories/social_profiles.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :social_profile do + user + provider "facebook" + uid "12345" + end +end diff --git a/spec/models/social_profiles_spec.rb b/spec/models/social_profiles_spec.rb new file mode 100644 index 00000000..79c30cf3 --- /dev/null +++ b/spec/models/social_profiles_spec.rb @@ -0,0 +1,11 @@ +require "rails_helper" + +describe SocialProfile do + subject { create :social_profile } + + it { should belong_to :user } + it { is_expected.to validate_presence_of :user } + it { is_expected.to validate_presence_of :uid } + it { is_expected.to validate_presence_of :provider } + it { is_expected.to validate_uniqueness_of(:uid).scoped_to(:provider) } +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 0d1f5ef0..5ca43167 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -2,4 +2,5 @@ describe User do it { is_expected.to validate_presence_of :full_name } + it { is_expected.to have_many(:social_profiles).dependent(:destroy) } end diff --git a/spec/policies/auth_verification_policy_spec.rb b/spec/policies/auth_verification_policy_spec.rb new file mode 100644 index 00000000..fb4f6fdf --- /dev/null +++ b/spec/policies/auth_verification_policy_spec.rb @@ -0,0 +1,43 @@ +require "rails_helper" + +describe AuthVerificationPolicy do + let(:auth) { double(:omniauth, provider: provider) } + + describe ".verified?" do + subject { described_class.new(auth).verified? } + + context "when provider is Facebook" do + let(:provider) { "facebook" } + + before do + allow(auth).to receive_message_chain(:info, :verified?).and_return(true) + allow(auth).to receive_message_chain(:extra, :raw_info, :verified?).and_return(true) + end + + it "returns corresponding value" do + expect(subject).to eq(true) + end + end + + context "when provider is Google" do + let(:provider) { "google_oauth2" } + + before do + allow(auth).to receive_message_chain(:extra, :raw_info, :email_verified?).and_return(true) + end + + it "returns corresponding value" do + expect(subject).to eq(true) + end + end + + context "when provider is not in the case statement" do + let(:provider) { "another" } + + it "raises Exception" do + expect { subject } + .to raise_error(ArgumentError, I18n.t("omniauth.verification.not_implemented", kind: provider)) + end + end + end +end From 4abaf4f72428ab99536f8e1973ca57259f55512e Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Tue, 8 Dec 2015 23:08:42 +0300 Subject: [PATCH 02/34] add account from profile page: logic implemented --- .../omniauth_callbacks_controller.rb | 27 +++++++++++++++++++ app/interactors/connect_account_organizer.rb | 5 +++- .../20151208195800_create_social_profiles.rb | 9 +++++++ db/schema.rb | 12 ++++++++- 4 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20151208195800_create_social_profiles.rb diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 965043f0..7fc58ce2 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -1,2 +1,29 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController + SocialProfile::PROVIDERS.each do |provider| + define_method(provider.to_s) do + begin + handle_user + rescue OauthOrganizer::OauthError + handle_error + end + end + end + + private + + def handle_user + fail unless current_user # temporarily handling only connecting account from profile page + + begin + ConnectAccountOrganizer.new(auth, current_user).call + rescue ConnectAccountOrganizer::OauthError => e + redirect_to root_url, error: e + end + + redirect_to edit_user_registration_path + end + + def auth + request.env["omniauth.auth"] + end end diff --git a/app/interactors/connect_account_organizer.rb b/app/interactors/connect_account_organizer.rb index 5d250176..bff74b37 100644 --- a/app/interactors/connect_account_organizer.rb +++ b/app/interactors/connect_account_organizer.rb @@ -1,4 +1,7 @@ class ConnectAccountOrganizer + class OauthError < StandardError + end + attr_reader :auth, :user private :auth, :user @@ -16,7 +19,7 @@ def call private def fail_oauth - fail "Please confirm your account before connecting your #{auth.provider} account." + fail OauthError, "Please confirm your account before connecting your #{auth.provider} account." end def auth_verified? diff --git a/db/migrate/20151208195800_create_social_profiles.rb b/db/migrate/20151208195800_create_social_profiles.rb new file mode 100644 index 00000000..1278f4d8 --- /dev/null +++ b/db/migrate/20151208195800_create_social_profiles.rb @@ -0,0 +1,9 @@ +class CreateSocialProfiles < ActiveRecord::Migration + def change + create_table :social_profiles do |t| + t.references :user, index: true + t.string :provider, index: true, null: false, default: "" + t.string :uid, index: true, null: false, default: "" + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 3b83b322..a979f0b0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,11 +11,21 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20100713113845) do +ActiveRecord::Schema.define(version: 20151208195800) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "social_profiles", force: :cascade do |t| + t.integer "user_id" + t.string "provider", default: "", null: false + t.string "uid", default: "", null: false + end + + add_index "social_profiles", ["provider"], name: "index_social_profiles_on_provider", using: :btree + add_index "social_profiles", ["uid"], name: "index_social_profiles_on_uid", using: :btree + add_index "social_profiles", ["user_id"], name: "index_social_profiles_on_user_id", using: :btree + create_table "users", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false From 33154dfeb7348d3a64bd0b45e308eb5db7ffa6fb Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Thu, 10 Dec 2015 22:30:04 +0300 Subject: [PATCH 03/34] rename classes, minor refactoring --- app/controllers/omniauth_callbacks_controller.rb | 15 +++++++-------- ...nt_organizer.rb => oauth_connect_organizer.rb} | 2 +- app/interactors/oauth_signup_organizer.rb | 12 ++++++++++++ 3 files changed, 20 insertions(+), 9 deletions(-) rename app/interactors/{connect_account_organizer.rb => oauth_connect_organizer.rb} (96%) create mode 100644 app/interactors/oauth_signup_organizer.rb diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 7fc58ce2..6dd378bc 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -3,8 +3,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController define_method(provider.to_s) do begin handle_user - rescue OauthOrganizer::OauthError - handle_error + rescue OauthConnectOrganizer::OauthError => e + handle_error(e) end end end @@ -14,15 +14,14 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController def handle_user fail unless current_user # temporarily handling only connecting account from profile page - begin - ConnectAccountOrganizer.new(auth, current_user).call - rescue ConnectAccountOrganizer::OauthError => e - redirect_to root_url, error: e - end - + OauthConnectOrganizer.new(auth, current_user).call redirect_to edit_user_registration_path end + def handle_error(message) + redirect_to root_url, error: message + end + def auth request.env["omniauth.auth"] end diff --git a/app/interactors/connect_account_organizer.rb b/app/interactors/oauth_connect_organizer.rb similarity index 96% rename from app/interactors/connect_account_organizer.rb rename to app/interactors/oauth_connect_organizer.rb index bff74b37..d640a1a1 100644 --- a/app/interactors/connect_account_organizer.rb +++ b/app/interactors/oauth_connect_organizer.rb @@ -1,4 +1,4 @@ -class ConnectAccountOrganizer +class OauthConnectOrganizer class OauthError < StandardError end diff --git a/app/interactors/oauth_signup_organizer.rb b/app/interactors/oauth_signup_organizer.rb new file mode 100644 index 00000000..d3d3a65a --- /dev/null +++ b/app/interactors/oauth_signup_organizer.rb @@ -0,0 +1,12 @@ +class OauthSignupOrganizer + attr_reader :auth + private :auth + + def initialize(auth) + @auth = auth + end + + def call + + end +end From b6db77fbad6a295148750f807c89bd32af188802 Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Thu, 10 Dec 2015 22:52:03 +0300 Subject: [PATCH 04/34] sign_up flow draft --- app/interactors/oauth_signup_organizer.rb | 77 +++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/app/interactors/oauth_signup_organizer.rb b/app/interactors/oauth_signup_organizer.rb index d3d3a65a..d1551b29 100644 --- a/app/interactors/oauth_signup_organizer.rb +++ b/app/interactors/oauth_signup_organizer.rb @@ -7,6 +7,83 @@ def initialize(auth) end def call + if auth_verified? + if user_found_by_uid.present? + if user_found_by_uid.confirmed? + user_found_by_uid.sign_in # pseudocode + else + user_found_by_uid.sign_in + user_found_by_uid.reset_password(new_password, new_password) + user_found_by_uid.confirm! + user_found_by_uid.send_reset_password_instructions + end + else + if user_found_by_email.present? + if user_found_by_email.confirmed? + user_found_by_email.sign_in + create_social_profile(user_found_by_email) + else + user_found_by_email.sign_in + create_social_profile(user_found_by_email) + user_found_by_email.reset_password(new_password, new_password) + user_found_by_email.confirm! + redirect_to finish_signup_page_path # pseudocode + end + else + create_confirmed_user + end + end + else + if user_found_by_uid.present? + user_found_by_uid.sign_in + user_found_by_uid.send_confirmation_instructions unless user_found_by_uid.confirmed? + else + if user_found_by_email.present? + "Alert: Please, connect your account from profile page" + else + create_new_user + user_found_by_uid.send_confirmation_instructions + end + end + end + end + + private + + def auth_verified? + AuthVerificationPolicy.new(auth).verified? + end + + def user_found_by_uid + @user_found_by_uid |= SocialProfile.from_omniauth(auth).try(:user) + end + + def user_found_by_email + @user_found_by_email ||= User.find_by(email: auth["info"]["email"]) + end + + def create_social_profile(user) + user.apply_omniauth(auth) && user.save! + end + + def create_confirmed_user + user = User.new + user.email = auth["info"]["email"] + user.full_name = auth["info"]["name"] + user.reset_password(new_password, new_password) + user.confirm! + redirect_to finish_signup_page_path # pseudocode + end + + def create_new_user + user = User.new + user.email = auth["info"]["email"] + user.full_name = auth["info"]["name"] + user.reset_password(new_password, new_password) + redirect_to finish_signup_page_path # pseudocode + end + def new_password + @new_password ||= Devise.friendly_token.first(8) end end From 3d53b9abbebb7c95056e789b797746a2d8bef4a7 Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Tue, 15 Dec 2015 23:46:23 +0300 Subject: [PATCH 05/34] add organizers and services --- .../omniauth_callbacks_controller.rb | 16 ++++++++-- app/interactors/oauth_connect_organizer.rb | 2 +- app/interactors/oauth_sign_up_organizer.rb | 22 +++++++++++++ ...nizer.rb => oauth_signup_organizer_old.rb} | 10 +++--- app/interactors/unverified_auth_organizer.rb | 32 +++++++++++++++++++ app/interactors/user_found_by_email.rb | 24 ++++++++++++++ app/interactors/user_found_by_uid.rb | 17 ++++++++++ app/interactors/user_from_omniauth.rb | 24 ++++++++++++++ app/interactors/verified_auth_organizer.rb | 26 +++++++++++++++ 9 files changed, 164 insertions(+), 9 deletions(-) create mode 100644 app/interactors/oauth_sign_up_organizer.rb rename app/interactors/{oauth_signup_organizer.rb => oauth_signup_organizer_old.rb} (93%) create mode 100644 app/interactors/unverified_auth_organizer.rb create mode 100644 app/interactors/user_found_by_email.rb create mode 100644 app/interactors/user_found_by_uid.rb create mode 100644 app/interactors/user_from_omniauth.rb create mode 100644 app/interactors/verified_auth_organizer.rb diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 6dd378bc..b3df0c7b 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -12,10 +12,20 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController private def handle_user - fail unless current_user # temporarily handling only connecting account from profile page + if current_user + OauthConnectOrganizer.new(auth, current_user).call + redirect_to edit_user_registration_path + else + user = OauthSignUpOrganizer.new(auth).user - OauthConnectOrganizer.new(auth, current_user).call - redirect_to edit_user_registration_path + unless user.confirmed? + user.reset_password(new_password, new_password) + user.confirm! + # fail "Need to finish sign-up!" + redirect_to finish_signup_path + end + + end end def handle_error(message) diff --git a/app/interactors/oauth_connect_organizer.rb b/app/interactors/oauth_connect_organizer.rb index d640a1a1..81eb1bcb 100644 --- a/app/interactors/oauth_connect_organizer.rb +++ b/app/interactors/oauth_connect_organizer.rb @@ -32,7 +32,7 @@ def connect_social_profile def process_user_confirmation user.reset_password(new_password, new_password) - user.confirm! + user.confirm user.send_reset_password_instructions end diff --git a/app/interactors/oauth_sign_up_organizer.rb b/app/interactors/oauth_sign_up_organizer.rb new file mode 100644 index 00000000..cb54cf12 --- /dev/null +++ b/app/interactors/oauth_sign_up_organizer.rb @@ -0,0 +1,22 @@ +class OauthSignUpOrganizer + attr_reader :auth + private :auth + + def initialize(auth) + @auth = auth + end + + def user + if auth_verified? + VerifiedAuthOrganizer.new(auth).user + else + UnverifiedAuthOrganizer.new(auth).user + end + end + + private + + def auth_verified? + AuthVerificationPolicy.new(auth).verified? + end +end diff --git a/app/interactors/oauth_signup_organizer.rb b/app/interactors/oauth_signup_organizer_old.rb similarity index 93% rename from app/interactors/oauth_signup_organizer.rb rename to app/interactors/oauth_signup_organizer_old.rb index d1551b29..a9f47a3b 100644 --- a/app/interactors/oauth_signup_organizer.rb +++ b/app/interactors/oauth_signup_organizer_old.rb @@ -1,4 +1,4 @@ -class OauthSignupOrganizer +class OauthSignupOrganizerOld attr_reader :auth private :auth @@ -12,16 +12,16 @@ def call if user_found_by_uid.confirmed? user_found_by_uid.sign_in # pseudocode else - user_found_by_uid.sign_in user_found_by_uid.reset_password(new_password, new_password) user_found_by_uid.confirm! - user_found_by_uid.send_reset_password_instructions + user_found_by_uid.sign_in + redirect_to finish_signup_page_path # pseudocode end else if user_found_by_email.present? if user_found_by_email.confirmed? - user_found_by_email.sign_in create_social_profile(user_found_by_email) + user_found_by_email.sign_in else user_found_by_email.sign_in create_social_profile(user_found_by_email) @@ -55,7 +55,7 @@ def auth_verified? end def user_found_by_uid - @user_found_by_uid |= SocialProfile.from_omniauth(auth).try(:user) + @user_found_by_uid ||= SocialProfile.from_omniauth(auth).try(:user) end def user_found_by_email diff --git a/app/interactors/unverified_auth_organizer.rb b/app/interactors/unverified_auth_organizer.rb new file mode 100644 index 00000000..6cf6fc3d --- /dev/null +++ b/app/interactors/unverified_auth_organizer.rb @@ -0,0 +1,32 @@ +class UnverifiedAuthOrganizer + attr_reader :auth + private :auth + + def initialize(auth) + @auth = auth + end + + def user + if user_found_by_uid.present? + user_found_by_uid.send_confirmation_instructions unless user_found_by_uid.confirmed? + elsif user_found_by_email.present? + fail "Alert: Please, connect your account from profile page" + else + user_from_omniauth.send_confirmation_instructions + end + end + + private + + def user_found_by_uid + @user_found_by_uid ||= SocialProfile.from_omniauth(auth).try(:user) + end + + def user_found_by_email + @user_found_by_email ||= User.find_by(email: auth["info"]["email"]) + end + + def user_from_omniauth + @user_from_omniauth ||= UserFromOmniauth.new(auth).call + end +end diff --git a/app/interactors/user_found_by_email.rb b/app/interactors/user_found_by_email.rb new file mode 100644 index 00000000..1bca39b1 --- /dev/null +++ b/app/interactors/user_found_by_email.rb @@ -0,0 +1,24 @@ +class UserFoundByEmail + attr_reader :auth + private :auth + + def initialize(auth) + @auth = auth + end + + def call + return unless user + create_social_profile + user + end + + private + + def user + @user ||= User.find_by(email: auth["info"]["email"]) + end + + def create_social_profile + user.social_profiles.create!(provider: auth["provider"], uid: auth["uid"]) + end +end diff --git a/app/interactors/user_found_by_uid.rb b/app/interactors/user_found_by_uid.rb new file mode 100644 index 00000000..850f8169 --- /dev/null +++ b/app/interactors/user_found_by_uid.rb @@ -0,0 +1,17 @@ +class UserFoundByUid + attr_reader :auth + private :auth + + def initialize(auth) + @auth = auth + end + + def call + SocialProfile.from_omniauth(auth).try(:user) + end +end + +# user.reset_password(new_password, new_password) +# user.confirm! +# fail "Need to finish sign-up!" +# TODO: handle password_reset, confirmation and redirection in controller \ No newline at end of file diff --git a/app/interactors/user_from_omniauth.rb b/app/interactors/user_from_omniauth.rb new file mode 100644 index 00000000..c5a57de7 --- /dev/null +++ b/app/interactors/user_from_omniauth.rb @@ -0,0 +1,24 @@ +class UserFromOmniauth + attr_reader :auth + private :auth + + def initialize(auth) + @auth = auth + end + + def call + user = User.new + user.email = auth["info"]["email"] + user.full_name = auth["info"]["name"] + user.reset_password(new_password, new_password) + user + # user.confirm + # fail "Need to finish signup" + end + + private + + def new_password + @new_password ||= Devise.friendly_token.first(8) + end +end \ No newline at end of file diff --git a/app/interactors/verified_auth_organizer.rb b/app/interactors/verified_auth_organizer.rb new file mode 100644 index 00000000..38838006 --- /dev/null +++ b/app/interactors/verified_auth_organizer.rb @@ -0,0 +1,26 @@ +class VerifiedAuthOrganizer + attr_reader :auth + private :auth + + def initialize(auth) + @auth = auth + end + + def user + user_found_by_uid || user_found_by_email || user_from_omniauth + end + + private + + def user_found_by_uid + @user_found_by_uid ||= SocialProfile.from_omniauth(auth).try(:user) + end + + def user_found_by_email + @user_found_by_email ||= UserFoundByEmail.new(auth).call + end + + def user_from_omniauth + @user_from_omniauth ||= UserFromOmniauth.new(auth).call + end +end From f98b87ed401bb76b2b226eca534d49dd97da8218 Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Thu, 17 Dec 2015 22:53:00 +0300 Subject: [PATCH 06/34] add users_controller --- app/controllers/users_controller.rb | 0 app/interactors/user_found_by_uid.rb | 17 ----------------- 2 files changed, 17 deletions(-) create mode 100644 app/controllers/users_controller.rb delete mode 100644 app/interactors/user_found_by_uid.rb diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 00000000..e69de29b diff --git a/app/interactors/user_found_by_uid.rb b/app/interactors/user_found_by_uid.rb deleted file mode 100644 index 850f8169..00000000 --- a/app/interactors/user_found_by_uid.rb +++ /dev/null @@ -1,17 +0,0 @@ -class UserFoundByUid - attr_reader :auth - private :auth - - def initialize(auth) - @auth = auth - end - - def call - SocialProfile.from_omniauth(auth).try(:user) - end -end - -# user.reset_password(new_password, new_password) -# user.confirm! -# fail "Need to finish sign-up!" -# TODO: handle password_reset, confirmation and redirection in controller \ No newline at end of file From ab24a299a61f15570cfe38cd5490ae868c9f49ee Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Thu, 17 Dec 2015 22:53:09 +0300 Subject: [PATCH 07/34] add users_controller --- .../omniauth_callbacks_controller.rb | 41 ++++++++++++------- app/controllers/users_controller.rb | 40 ++++++++++++++++++ app/interactors/unverified_auth_organizer.rb | 25 ++++++----- app/interactors/user_from_omniauth.rb | 15 +++---- app/interactors/verified_auth_organizer.rb | 6 +-- 5 files changed, 91 insertions(+), 36 deletions(-) diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index b3df0c7b..c3bb7194 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -2,7 +2,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController SocialProfile::PROVIDERS.each do |provider| define_method(provider.to_s) do begin - handle_user + if current_user + OauthConnectOrganizer.new(auth, current_user).call + redirect_to edit_user_registration_path + else + handle_user + end rescue OauthConnectOrganizer::OauthError => e handle_error(e) end @@ -11,28 +16,34 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController private + def auth + request.env["omniauth.auth"] + end + def handle_user - if current_user - OauthConnectOrganizer.new(auth, current_user).call - redirect_to edit_user_registration_path + if auth_verified? + user = VerifiedAuthOrganizer.new(auth).user + user.reset_password(new_password, new_password) unless user.confirmed? else - user = OauthSignUpOrganizer.new(auth).user - - unless user.confirmed? - user.reset_password(new_password, new_password) - user.confirm! - # fail "Need to finish sign-up!" - redirect_to finish_signup_path - end - + user = UnverifiedAuthOrganizer.new(auth).call + session[:auth_not_verified] = true end + sign_in_and_redirect user, event: :authentication end def handle_error(message) redirect_to root_url, error: message end - def auth - request.env["omniauth.auth"] + def auth_verified? + AuthVerificationPolicy.new(auth).verified? + end + + def after_sign_in_path_for(resource) + if resource.confirmed? + super resource + else + finish_signup_path(resource) + end end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index e69de29b..8e1662f6 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -0,0 +1,40 @@ +class UsersController < ApplicationController + expose(:user, attributes: :user_params) + + before_action :authenticate_user!, only: :home + + def home + end + + def finish_signup + return false unless request.patch? + + user.update_attributes(user_params) ? sign_in_user : render_errors + end + + private + + def user_params + params.require(:user).permit(%i(full_name email password)) + end + + def sign_in_user + user.skip_reconfirmation! + confirm_user + sign_in(user, bypass: true) + redirect_to root_path, notice: "Welcome!" + end + + def render_errors + render :finish_signup + end + + def confirm_user + if session[:auth_not_verified] + user.send_confirmation_instructions + reset_session + else + user.update_attribute(:confirmed_at, Time.zone.now) + end + end +end diff --git a/app/interactors/unverified_auth_organizer.rb b/app/interactors/unverified_auth_organizer.rb index 6cf6fc3d..02d31c01 100644 --- a/app/interactors/unverified_auth_organizer.rb +++ b/app/interactors/unverified_auth_organizer.rb @@ -6,27 +6,30 @@ def initialize(auth) @auth = auth end - def user - if user_found_by_uid.present? - user_found_by_uid.send_confirmation_instructions unless user_found_by_uid.confirmed? - elsif user_found_by_email.present? + def call + if user_found_by_email.present? fail "Alert: Please, connect your account from profile page" else - user_from_omniauth.send_confirmation_instructions + user.send_confirmation_instructions unless user.confirmed? + user end end private - def user_found_by_uid - @user_found_by_uid ||= SocialProfile.from_omniauth(auth).try(:user) - end - def user_found_by_email @user_found_by_email ||= User.find_by(email: auth["info"]["email"]) end - def user_from_omniauth - @user_from_omniauth ||= UserFromOmniauth.new(auth).call + def user + user_found_by_uid || new_user + end + + def user_found_by_uid + @user_found_by_uid ||= SocialProfile.from_omniauth(auth).try(:user) + end + + def new_user + @new_user ||= UserFromOmniauth.new(auth).call end end diff --git a/app/interactors/user_from_omniauth.rb b/app/interactors/user_from_omniauth.rb index c5a57de7..70be0aa5 100644 --- a/app/interactors/user_from_omniauth.rb +++ b/app/interactors/user_from_omniauth.rb @@ -7,13 +7,14 @@ def initialize(auth) end def call - user = User.new - user.email = auth["info"]["email"] - user.full_name = auth["info"]["name"] - user.reset_password(new_password, new_password) + user = User.new( + email: auth["info"]["email"], + full_name: auth["info"]["name"], + password: new_password, + password_confirmation: new_password + ) + user.skip_confirmation_notification! && user.save! user - # user.confirm - # fail "Need to finish signup" end private @@ -21,4 +22,4 @@ def call def new_password @new_password ||= Devise.friendly_token.first(8) end -end \ No newline at end of file +end diff --git a/app/interactors/verified_auth_organizer.rb b/app/interactors/verified_auth_organizer.rb index 38838006..fa92b4c5 100644 --- a/app/interactors/verified_auth_organizer.rb +++ b/app/interactors/verified_auth_organizer.rb @@ -7,7 +7,7 @@ def initialize(auth) end def user - user_found_by_uid || user_found_by_email || user_from_omniauth + user_found_by_uid || user_found_by_email || new_user end private @@ -20,7 +20,7 @@ def user_found_by_email @user_found_by_email ||= UserFoundByEmail.new(auth).call end - def user_from_omniauth - @user_from_omniauth ||= UserFromOmniauth.new(auth).call + def new_user + @new_user ||= UserFromOmniauth.new(auth).call end end From 273af976a47397bc5c6288548826b614fd46f6c5 Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Thu, 17 Dec 2015 23:12:58 +0300 Subject: [PATCH 08/34] move OauthError into policy object --- .../omniauth_callbacks_controller.rb | 17 ++-- app/controllers/users_controller.rb | 7 +- app/interactors/oauth_connect_organizer.rb | 5 +- app/interactors/oauth_signup_organizer_old.rb | 89 ------------------- app/interactors/unverified_auth_organizer.rb | 2 +- app/policies/auth_verification_policy.rb | 3 + 6 files changed, 14 insertions(+), 109 deletions(-) delete mode 100644 app/interactors/oauth_signup_organizer_old.rb diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index c3bb7194..fbcce0c1 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -2,13 +2,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController SocialProfile::PROVIDERS.each do |provider| define_method(provider.to_s) do begin - if current_user - OauthConnectOrganizer.new(auth, current_user).call - redirect_to edit_user_registration_path - else - handle_user - end - rescue OauthConnectOrganizer::OauthError => e + current_user ? handle_connection : handle_sign_up + rescue AuthVerificationPolicy::OauthError => e handle_error(e) end end @@ -16,17 +11,21 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController private + def handle_connection + OauthConnectOrganizer.new(auth, current_user).call + redirect_to edit_user_registration_path + end + def auth request.env["omniauth.auth"] end - def handle_user + def handle_sign_up if auth_verified? user = VerifiedAuthOrganizer.new(auth).user user.reset_password(new_password, new_password) unless user.confirmed? else user = UnverifiedAuthOrganizer.new(auth).call - session[:auth_not_verified] = true end sign_in_and_redirect user, event: :authentication end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 8e1662f6..102e461b 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -30,11 +30,6 @@ def render_errors end def confirm_user - if session[:auth_not_verified] - user.send_confirmation_instructions - reset_session - else - user.update_attribute(:confirmed_at, Time.zone.now) - end + user.update_attribute(:confirmed_at, Time.zone.now) end end diff --git a/app/interactors/oauth_connect_organizer.rb b/app/interactors/oauth_connect_organizer.rb index 81eb1bcb..7d2a17e9 100644 --- a/app/interactors/oauth_connect_organizer.rb +++ b/app/interactors/oauth_connect_organizer.rb @@ -1,7 +1,4 @@ class OauthConnectOrganizer - class OauthError < StandardError - end - attr_reader :auth, :user private :auth, :user @@ -19,7 +16,7 @@ def call private def fail_oauth - fail OauthError, "Please confirm your account before connecting your #{auth.provider} account." + fail AuthVerificationPolicy::OauthError, "Please confirm your account before connecting your #{auth.provider} account." end def auth_verified? diff --git a/app/interactors/oauth_signup_organizer_old.rb b/app/interactors/oauth_signup_organizer_old.rb deleted file mode 100644 index a9f47a3b..00000000 --- a/app/interactors/oauth_signup_organizer_old.rb +++ /dev/null @@ -1,89 +0,0 @@ -class OauthSignupOrganizerOld - attr_reader :auth - private :auth - - def initialize(auth) - @auth = auth - end - - def call - if auth_verified? - if user_found_by_uid.present? - if user_found_by_uid.confirmed? - user_found_by_uid.sign_in # pseudocode - else - user_found_by_uid.reset_password(new_password, new_password) - user_found_by_uid.confirm! - user_found_by_uid.sign_in - redirect_to finish_signup_page_path # pseudocode - end - else - if user_found_by_email.present? - if user_found_by_email.confirmed? - create_social_profile(user_found_by_email) - user_found_by_email.sign_in - else - user_found_by_email.sign_in - create_social_profile(user_found_by_email) - user_found_by_email.reset_password(new_password, new_password) - user_found_by_email.confirm! - redirect_to finish_signup_page_path # pseudocode - end - else - create_confirmed_user - end - end - else - if user_found_by_uid.present? - user_found_by_uid.sign_in - user_found_by_uid.send_confirmation_instructions unless user_found_by_uid.confirmed? - else - if user_found_by_email.present? - "Alert: Please, connect your account from profile page" - else - create_new_user - user_found_by_uid.send_confirmation_instructions - end - end - end - end - - private - - def auth_verified? - AuthVerificationPolicy.new(auth).verified? - end - - def user_found_by_uid - @user_found_by_uid ||= SocialProfile.from_omniauth(auth).try(:user) - end - - def user_found_by_email - @user_found_by_email ||= User.find_by(email: auth["info"]["email"]) - end - - def create_social_profile(user) - user.apply_omniauth(auth) && user.save! - end - - def create_confirmed_user - user = User.new - user.email = auth["info"]["email"] - user.full_name = auth["info"]["name"] - user.reset_password(new_password, new_password) - user.confirm! - redirect_to finish_signup_page_path # pseudocode - end - - def create_new_user - user = User.new - user.email = auth["info"]["email"] - user.full_name = auth["info"]["name"] - user.reset_password(new_password, new_password) - redirect_to finish_signup_page_path # pseudocode - end - - def new_password - @new_password ||= Devise.friendly_token.first(8) - end -end diff --git a/app/interactors/unverified_auth_organizer.rb b/app/interactors/unverified_auth_organizer.rb index 02d31c01..541d669a 100644 --- a/app/interactors/unverified_auth_organizer.rb +++ b/app/interactors/unverified_auth_organizer.rb @@ -8,7 +8,7 @@ def initialize(auth) def call if user_found_by_email.present? - fail "Alert: Please, connect your account from profile page" + fail AuthVerificationPolicy::OauthError, "Alert: Please, connect your account from profile page." else user.send_confirmation_instructions unless user.confirmed? user diff --git a/app/policies/auth_verification_policy.rb b/app/policies/auth_verification_policy.rb index a113c04e..158152e7 100644 --- a/app/policies/auth_verification_policy.rb +++ b/app/policies/auth_verification_policy.rb @@ -1,4 +1,7 @@ class AuthVerificationPolicy + class OauthError < StandardError + end + attr_reader :auth private :auth From 255a6887fbe93fbef1899419d08f66ec848d7f6c Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Thu, 17 Dec 2015 23:21:10 +0300 Subject: [PATCH 09/34] minor improvements --- app/controllers/users_controller.rb | 6 +++--- app/interactors/unverified_auth_organizer.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 102e461b..9e698d1a 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,8 +1,8 @@ class UsersController < ApplicationController - expose(:user, attributes: :user_params) - before_action :authenticate_user!, only: :home + expose(:user, attributes: :user_params) + def home end @@ -19,7 +19,7 @@ def user_params end def sign_in_user - user.skip_reconfirmation! + # user.skip_reconfirmation! confirm_user sign_in(user, bypass: true) redirect_to root_path, notice: "Welcome!" diff --git a/app/interactors/unverified_auth_organizer.rb b/app/interactors/unverified_auth_organizer.rb index 541d669a..ed3830e6 100644 --- a/app/interactors/unverified_auth_organizer.rb +++ b/app/interactors/unverified_auth_organizer.rb @@ -8,7 +8,7 @@ def initialize(auth) def call if user_found_by_email.present? - fail AuthVerificationPolicy::OauthError, "Alert: Please, connect your account from profile page." + fail AuthVerificationPolicy::OauthError, "Please, connect your account from profile page." else user.send_confirmation_instructions unless user.confirmed? user From 9d39a2526e32955d4616e87c48a8562ada28420d Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Fri, 18 Dec 2015 23:06:12 +0300 Subject: [PATCH 10/34] refactor controller, extend finish sign-up page logic --- .../omniauth_callbacks_controller.rb | 15 +++++-------- app/controllers/users_controller.rb | 8 +++++-- app/interactors/connect_social_profile.rb | 6 ++++- ...m_omniauth.rb => create_user_from_auth.rb} | 2 +- app/interactors/oauth_sign_up_organizer.rb | 22 ------------------- app/interactors/unverified_auth_organizer.rb | 10 ++++----- app/interactors/verified_auth_organizer.rb | 2 +- 7 files changed, 24 insertions(+), 41 deletions(-) rename app/interactors/{user_from_omniauth.rb => create_user_from_auth.rb} (94%) delete mode 100644 app/interactors/oauth_sign_up_organizer.rb diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index fbcce0c1..ed5353a6 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -4,7 +4,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController begin current_user ? handle_connection : handle_sign_up rescue AuthVerificationPolicy::OauthError => e - handle_error(e) + redirect_to root_url, error: e.message end end end @@ -21,19 +21,14 @@ def auth end def handle_sign_up - if auth_verified? - user = VerifiedAuthOrganizer.new(auth).user - user.reset_password(new_password, new_password) unless user.confirmed? + user = if auth_verified? + VerifiedAuthOrganizer.new(auth).user else - user = UnverifiedAuthOrganizer.new(auth).call + UnverifiedAuthOrganizer.new(auth).user end sign_in_and_redirect user, event: :authentication end - def handle_error(message) - redirect_to root_url, error: message - end - def auth_verified? AuthVerificationPolicy.new(auth).verified? end @@ -42,6 +37,8 @@ def after_sign_in_path_for(resource) if resource.confirmed? super resource else + session[:auth_verified?] = auth_verified? + resource.reset_password(new_password, new_password) finish_signup_path(resource) end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 9e698d1a..d379d43a 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -19,8 +19,8 @@ def user_params end def sign_in_user - # user.skip_reconfirmation! confirm_user + reset_session sign_in(user, bypass: true) redirect_to root_path, notice: "Welcome!" end @@ -30,6 +30,10 @@ def render_errors end def confirm_user - user.update_attribute(:confirmed_at, Time.zone.now) + if session[:auth_confirmed?] + user.update_attribute(:confirmed_at, Time.zone.now) + else + user.send_confirmation_instructions + end end end diff --git a/app/interactors/connect_social_profile.rb b/app/interactors/connect_social_profile.rb index 120c29cc..7efbaea3 100644 --- a/app/interactors/connect_social_profile.rb +++ b/app/interactors/connect_social_profile.rb @@ -10,7 +10,7 @@ def call if social_profile social_profile.update_attribute(:user, user) else - user.apply_omniauth(auth) && user.save! + create_social_profile! end end @@ -19,4 +19,8 @@ def call def social_profile @social_profile ||= SocialProfile.from_omniauth(auth) end + + def create_social_profile! + user.social_profiles.create!(provider: auth["provider"], uid: auth["uid"]) + end end diff --git a/app/interactors/user_from_omniauth.rb b/app/interactors/create_user_from_auth.rb similarity index 94% rename from app/interactors/user_from_omniauth.rb rename to app/interactors/create_user_from_auth.rb index 70be0aa5..f2fd6586 100644 --- a/app/interactors/user_from_omniauth.rb +++ b/app/interactors/create_user_from_auth.rb @@ -1,4 +1,4 @@ -class UserFromOmniauth +class CreateUserFromAuth attr_reader :auth private :auth diff --git a/app/interactors/oauth_sign_up_organizer.rb b/app/interactors/oauth_sign_up_organizer.rb deleted file mode 100644 index cb54cf12..00000000 --- a/app/interactors/oauth_sign_up_organizer.rb +++ /dev/null @@ -1,22 +0,0 @@ -class OauthSignUpOrganizer - attr_reader :auth - private :auth - - def initialize(auth) - @auth = auth - end - - def user - if auth_verified? - VerifiedAuthOrganizer.new(auth).user - else - UnverifiedAuthOrganizer.new(auth).user - end - end - - private - - def auth_verified? - AuthVerificationPolicy.new(auth).verified? - end -end diff --git a/app/interactors/unverified_auth_organizer.rb b/app/interactors/unverified_auth_organizer.rb index ed3830e6..39b8d923 100644 --- a/app/interactors/unverified_auth_organizer.rb +++ b/app/interactors/unverified_auth_organizer.rb @@ -6,12 +6,12 @@ def initialize(auth) @auth = auth end - def call + def user if user_found_by_email.present? fail AuthVerificationPolicy::OauthError, "Please, connect your account from profile page." else - user.send_confirmation_instructions unless user.confirmed? - user + found_user.send_confirmation_instructions unless found_user.confirmed? + found_user end end @@ -21,7 +21,7 @@ def user_found_by_email @user_found_by_email ||= User.find_by(email: auth["info"]["email"]) end - def user + def found_user user_found_by_uid || new_user end @@ -30,6 +30,6 @@ def user_found_by_uid end def new_user - @new_user ||= UserFromOmniauth.new(auth).call + @new_user ||= CreateUserFromAuth.new(auth).call end end diff --git a/app/interactors/verified_auth_organizer.rb b/app/interactors/verified_auth_organizer.rb index fa92b4c5..70c22313 100644 --- a/app/interactors/verified_auth_organizer.rb +++ b/app/interactors/verified_auth_organizer.rb @@ -21,6 +21,6 @@ def user_found_by_email end def new_user - @new_user ||= UserFromOmniauth.new(auth).call + @new_user ||= CreateUserFromAuth.new(auth).call end end From d5529cd9a903d51c820dbc648f350ffa1f28881a Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Sat, 19 Dec 2015 14:52:43 +0300 Subject: [PATCH 11/34] add routes and view for finish_signup, minor improvements --- app/controllers/omniauth_callbacks_controller.rb | 4 ++++ app/controllers/users_controller.rb | 2 +- app/views/users/finish_signup.html.slim | 16 ++++++++++++++++ config/application.rb | 2 +- config/routes.rb | 1 + 5 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 app/views/users/finish_signup.html.slim diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index ed5353a6..936bf961 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -42,4 +42,8 @@ def after_sign_in_path_for(resource) finish_signup_path(resource) end end + + def new_password + @new_password ||= Devise.friendly_token.first(8) + end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index d379d43a..915bfda1 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -30,7 +30,7 @@ def render_errors end def confirm_user - if session[:auth_confirmed?] + if session[:auth_verified?] user.update_attribute(:confirmed_at, Time.zone.now) else user.send_confirmation_instructions diff --git a/app/views/users/finish_signup.html.slim b/app/views/users/finish_signup.html.slim new file mode 100644 index 00000000..7757819b --- /dev/null +++ b/app/views/users/finish_signup.html.slim @@ -0,0 +1,16 @@ +.row + h3 Finish Signup + + .medium-6.columns + = simple_form_for user, + as: 'user', + url: finish_signup_path(user), + html: { method: :patch } do |f| + + .form-inputs + = f.input :email, required: true, autofocus: true, placeholder: "Your email here" + + = f.input :password, required: true, hint: '', placeholder: "Your password here" + + .form-actions + = f.button :submit, "Finish Signup" diff --git a/config/application.rb b/config/application.rb index 2c5297cc..254d45ee 100644 --- a/config/application.rb +++ b/config/application.rb @@ -32,7 +32,7 @@ class Application < Rails::Application config.noreply = "noreply@fs-rails-base.herokuapp.com" # Default host for action mailer, initializers/mailer.rb - config.host = "localhost:5000" + config.host = "localhost:3000" # We send all feedback email to this address config.feedback_email = ENV.fetch("FEEDBACK_EMAIL", "feedback@example.com") diff --git a/config/routes.rb b/config/routes.rb index 7cedb57e..cc204395 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,6 +3,7 @@ resource :feedback, only: %i(new create) resources :social_profiles, only: :destroy + match "/users/:id/finish_signup" => "users#finish_signup", via: %i(get patch), as: :finish_signup with_options controller: :pages do get :about From 5ac827be8cbe6a88bbf5dc656edb8373f87d7c33 Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Sat, 19 Dec 2015 16:04:14 +0300 Subject: [PATCH 12/34] add base specs, refactor code --- .../omniauth_callbacks_controller.rb | 6 +- app/interactors/oauth_connect_organizer.rb | 2 +- app/models/user.rb | 6 -- .../application/_navigation_user.html.slim | 6 -- config/application.rb | 2 +- spec/factories/users.rb | 4 + spec/features/user/connect_social_spec.rb | 0 .../visitor/sign_in_with_social_spec.rb | 47 ++++++++++ .../visitor/sign_up_with_social_spec.rb | 30 +++++++ spec/rails_helper.rb | 1 + spec/support/shared_examples/omniauth_stub.rb | 89 +++++++++++++++++++ 11 files changed, 176 insertions(+), 17 deletions(-) create mode 100644 spec/features/user/connect_social_spec.rb create mode 100644 spec/features/visitor/sign_in_with_social_spec.rb create mode 100644 spec/features/visitor/sign_up_with_social_spec.rb create mode 100644 spec/support/shared_examples/omniauth_stub.rb diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 936bf961..d2852d25 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -2,7 +2,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController SocialProfile::PROVIDERS.each do |provider| define_method(provider.to_s) do begin - current_user ? handle_connection : handle_sign_up + current_user ? connect_social_profile : handle_sign_in rescue AuthVerificationPolicy::OauthError => e redirect_to root_url, error: e.message end @@ -11,7 +11,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController private - def handle_connection + def connect_social_profile OauthConnectOrganizer.new(auth, current_user).call redirect_to edit_user_registration_path end @@ -20,7 +20,7 @@ def auth request.env["omniauth.auth"] end - def handle_sign_up + def handle_sign_in user = if auth_verified? VerifiedAuthOrganizer.new(auth).user else diff --git a/app/interactors/oauth_connect_organizer.rb b/app/interactors/oauth_connect_organizer.rb index 7d2a17e9..84eab24e 100644 --- a/app/interactors/oauth_connect_organizer.rb +++ b/app/interactors/oauth_connect_organizer.rb @@ -7,7 +7,7 @@ def initialize(auth, user) end def call - fail_oauth if !auth_verified? && !user.confirmed? + fail_oauth unless auth_verified? && user.confirmed? connect_social_profile process_user_confirmation unless user.confirmed? diff --git a/app/models/user.rb b/app/models/user.rb index e865d2a6..7b84a228 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -14,10 +14,4 @@ def to_s def full_name_with_email "#{self[:full_name]} (#{email})" end - - def apply_omniauth(auth) - self.email = auth["info"]["email"] if email.blank? - self.full_name = auth["info"]["name"] if full_name.blank? - social_profiles.build(provider: auth["provider"], uid: auth["uid"]) - end end diff --git a/app/views/application/_navigation_user.html.slim b/app/views/application/_navigation_user.html.slim index 57edb9f1..f3fe4746 100644 --- a/app/views/application/_navigation_user.html.slim +++ b/app/views/application/_navigation_user.html.slim @@ -13,9 +13,3 @@ ul.right - else = active_link_to 'Sign in', new_user_session_path, active: :exclusive, wrap_tag: :li = active_link_to 'Sign up', new_user_registration_path, active: :exclusive, wrap_tag: :li - - - SocialProfile::PROVIDERS.each do |provider| - = active_link_to "Sign in with #{provider_name(provider)}", - user_omniauth_authorize_path(provider), - active: :exclusive, - wrap_tag: :li \ No newline at end of file diff --git a/config/application.rb b/config/application.rb index 254d45ee..2c5297cc 100644 --- a/config/application.rb +++ b/config/application.rb @@ -32,7 +32,7 @@ class Application < Rails::Application config.noreply = "noreply@fs-rails-base.herokuapp.com" # Default host for action mailer, initializers/mailer.rb - config.host = "localhost:3000" + config.host = "localhost:5000" # We send all feedback email to this address config.feedback_email = ENV.fetch("FEEDBACK_EMAIL", "feedback@example.com") diff --git a/spec/factories/users.rb b/spec/factories/users.rb index b98724b1..fb58dd2a 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -17,4 +17,8 @@ user.update(confirmation_sent_at: 3.days.ago) end end + + trait :from_auth_hashie do + email "joe@bloggs.com" + end end diff --git a/spec/features/user/connect_social_spec.rb b/spec/features/user/connect_social_spec.rb new file mode 100644 index 00000000..e69de29b diff --git a/spec/features/visitor/sign_in_with_social_spec.rb b/spec/features/visitor/sign_in_with_social_spec.rb new file mode 100644 index 00000000..799a7867 --- /dev/null +++ b/spec/features/visitor/sign_in_with_social_spec.rb @@ -0,0 +1,47 @@ +require "rails_helper" + +feature "Sign In with social account" do + include_context :stub_omniauth + + let!(:user) { create :user, :confirmed, :from_auth_hashie } + + def sign_in_with_facebook + visit new_user_session_path + + click_link "Sign in with Facebook" + end + + scenario "user signs in directly to main page" do + sign_in_with_facebook + + expect(page).to have_content("Home") + expect(page).to have_content(user.full_name) + expect(page).to have_content(user.email) + end + + context "when social network account is not verified" do + include_context :stub_not_verified_omniauth + + # scenario "is notified with notice" do + # sign_in_with_facebook + # + # expect(page).to have_content("Your Facebook account can't be used to sign in. Please verify it via profile page.") + # expect(page).not_to have_content(user.full_name) + # expect(page).not_to have_content(user.email) + # end + end + + context "when user account is not confirmed" do + let!(:user) { create :user, :not_confirmed, :from_auth_hashie } + + scenario "user is offered to create new account", :js do + sign_in_with_facebook + save_and_open_screenshot + + expect(page).to have_content("You have to confirm your email address before continuing") + expect(page).to have_field("Enter your email address") + expect(page).to have_field("Password") + expect(page).to have_button("Sign in") + end + end +end diff --git a/spec/features/visitor/sign_up_with_social_spec.rb b/spec/features/visitor/sign_up_with_social_spec.rb new file mode 100644 index 00000000..217bd890 --- /dev/null +++ b/spec/features/visitor/sign_up_with_social_spec.rb @@ -0,0 +1,30 @@ +require "rails_helper" + +feature "Sign Up with social account" do + include_context :stub_omniauth + + let(:name_from_auth_hash) { "Joe Bloggs" } + + scenario "Visitor signs up" do + visit new_user_session_path + + click_link "Sign in with Facebook" + + expect(page).to have_content("Finish Signup") + + fill_in :user_email, with: "mailer@mail.com" + fill_in :user_password, with: "123456qwe" + click_button "Finish Signup" + + open_email("mailer@mail.com") + + expect(current_email).to have_subject("Confirmation instructions") + expect(current_email).to have_body_text(name_from_auth_hash) + + visit_in_email("Confirm my account") + + expect(page).to have_content("Your email address has been successfully confirmed") + expect(page).to have_text(name_from_auth_hash) + expect(page).to have_text("mailer@mail.com") + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 7177eac9..0a0460d2 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -6,6 +6,7 @@ require "shoulda/matchers" Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } +OmniAuth.config.test_mode = true RSpec.configure do |config| config.use_transactional_fixtures = false diff --git a/spec/support/shared_examples/omniauth_stub.rb b/spec/support/shared_examples/omniauth_stub.rb new file mode 100644 index 00000000..9ab8052f --- /dev/null +++ b/spec/support/shared_examples/omniauth_stub.rb @@ -0,0 +1,89 @@ +require "rails_helper" + +shared_context :auth_hashie do + let(:auth_hashie) do + OmniAuth::AuthHash.new( + provider: "facebook", + uid: "123545", + info: { + email: "joe@bloggs.com", + name: "Joe Bloggs", + verified: true + }, + extra: { + raw_info: { + email: "joe@bloggs.com", + name: "Joe Bloggs", + verified: true, + email_verified: true + } + } + ) + end +end + +shared_context :invalid_auth_hashie do + let(:auth_hashie) do + OmniAuth::AuthHash.new( + provider: "facebook", + uid: "123545", + info: { + email: "joe@bloggs.com", + name: "Joe Bloggs", + verified: false + }, + extra: { + raw_info: { + email: "joe@bloggs.com", + name: "Joe Bloggs", + verified: false, + email_verified: false + } + } + ) + end +end + +shared_context :stub_omniauth do + background do + OmniAuth.config.mock_auth[:facebook] = OmniAuth::AuthHash.new( + provider: "facebook", + uid: "123545", + info: { + email: "joe@bloggs.com", + name: "Joe Bloggs", + verified: true + }, + extra: { + raw_info: { + email: "joe@bloggs.com", + name: "Joe Bloggs", + verified: true, + email_verified: true + } + } + ) + end +end + +shared_context :stub_not_verified_omniauth do + background do + OmniAuth.config.mock_auth[:facebook] = OmniAuth::AuthHash.new( + provider: "facebook", + uid: "123545", + info: { + email: "joe@bloggs.com", + name: "Joe Bloggs", + verified: false + }, + extra: { + raw_info: { + email: "joe@bloggs.com", + name: "Joe Bloggs", + verified: false, + email_verified: false + } + } + ) + end +end From 8a64096c9835c232b4414c0be591719443a46d45 Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Sat, 19 Dec 2015 23:43:19 +0300 Subject: [PATCH 13/34] add services specs: wip --- app/interactors/oauth_connect_organizer.rb | 13 ++---- app/interactors/process_user_confirmation.rb | 20 +++++++++ .../connect_social_profile_spec.rb | 27 +++++++++++ .../interactors/create_user_from_auth_spec.rb | 16 +++++++ .../oauth_connect_organizer_spec.rb | 45 +++++++++++++++++++ 5 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 app/interactors/process_user_confirmation.rb create mode 100644 spec/interactors/connect_social_profile_spec.rb create mode 100644 spec/interactors/create_user_from_auth_spec.rb create mode 100644 spec/interactors/oauth_connect_organizer_spec.rb diff --git a/app/interactors/oauth_connect_organizer.rb b/app/interactors/oauth_connect_organizer.rb index 84eab24e..c7d81728 100644 --- a/app/interactors/oauth_connect_organizer.rb +++ b/app/interactors/oauth_connect_organizer.rb @@ -7,10 +7,11 @@ def initialize(auth, user) end def call - fail_oauth unless auth_verified? && user.confirmed? + unless user.confirmed? + auth_verified? ? process_user_confirmation : fail_oauth + end connect_social_profile - process_user_confirmation unless user.confirmed? end private @@ -28,12 +29,6 @@ def connect_social_profile end def process_user_confirmation - user.reset_password(new_password, new_password) - user.confirm - user.send_reset_password_instructions - end - - def new_password - @new_password ||= Devise.friendly_token.first(8) + ProcessUserConfirmation.new(user).call end end diff --git a/app/interactors/process_user_confirmation.rb b/app/interactors/process_user_confirmation.rb new file mode 100644 index 00000000..893bea9d --- /dev/null +++ b/app/interactors/process_user_confirmation.rb @@ -0,0 +1,20 @@ +class ProcessUserConfirmation + attr_reader :user + private :user + + def initialize(user) + @user = user + end + + def call + user.reset_password(new_password, new_password) + user.confirm + user.send_reset_password_instructions + end + + private + + def new_password + @new_password ||= Devise.friendly_token.first(8) + end +end diff --git a/spec/interactors/connect_social_profile_spec.rb b/spec/interactors/connect_social_profile_spec.rb new file mode 100644 index 00000000..de5d56f9 --- /dev/null +++ b/spec/interactors/connect_social_profile_spec.rb @@ -0,0 +1,27 @@ +require "rails_helper" + +describe ConnectSocialProfile do + include_context :auth_hashie + + let(:user_1) { create(:user) } + let(:user_2) { create(:user) } + let(:service) { described_class.new(user_2, auth_hashie) } + + subject { service.call } + + context "when social_profile exists" do + let!(:social_profile) do + create(:social_profile, uid: auth_hashie.uid, provider: auth_hashie.provider, user: user_1) + end + + it "updates user record" do + expect { subject }.to change { social_profile.reload.user }.from(user_1).to(user_2) + end + end + + context "when social profile not exists" do + it "creates related social profile" do + expect { subject }.to change { user_2.social_profiles.count }.by(1) + end + end +end diff --git a/spec/interactors/create_user_from_auth_spec.rb b/spec/interactors/create_user_from_auth_spec.rb new file mode 100644 index 00000000..ca3082a1 --- /dev/null +++ b/spec/interactors/create_user_from_auth_spec.rb @@ -0,0 +1,16 @@ +require "rails_helper" + +describe CreateUserFromAuth do + include_context :auth_hashie + + let(:user) { User.last } + let(:service) { described_class.new(auth_hashie) } + let(:sent_emails) { ActionMailer::Base.deliveries.count } + + subject { service.call } + + it "creates new user from auth hash" do + expect { subject }.to change { User.count }.by(1) + expect(sent_emails).to eq(0) + end +end diff --git a/spec/interactors/oauth_connect_organizer_spec.rb b/spec/interactors/oauth_connect_organizer_spec.rb new file mode 100644 index 00000000..48976fca --- /dev/null +++ b/spec/interactors/oauth_connect_organizer_spec.rb @@ -0,0 +1,45 @@ +require "rails_helper" + +describe OauthConnectOrganizer do + include_context :auth_hashie + + let(:service) { described_class.new(auth_hashie, user) } + + subject { service.call } + + context "when user confirmed" do + let(:user) { create(:user, :confirmed) } + + before do + allow(ConnectSocialProfile).to receive_message_chain(:new, :call) + service.call + end + + it "creates social profile" do + expect(ConnectSocialProfile).to have_received(:new).with(user, auth_hashie) + end + end + + context "when user not confirmed" do + let(:user) { create(:user) } + + context "when auth not verified" do + include_context :invalid_auth_hashie + + it "raises error" do + expect { subject }.to raise_error(AuthVerificationPolicy::OauthError) + end + end + + context "when auth verified" do + before do + allow(ProcessUserConfirmation).to receive_message_chain(:new, :call) + service.call + end + + it "invokes ProcessUserConfirmation" do + expect(ProcessUserConfirmation).to have_received(:new).with(user) + end + end + end +end From cd23b1b7e847c94810b2df838bc6f5811887098f Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Mon, 21 Dec 2015 23:39:37 +0300 Subject: [PATCH 14/34] extend specs --- app/controllers/users_controller.rb | 2 +- app/interactors/verified_auth_organizer.rb | 6 +-- .../interactors/create_user_from_auth_spec.rb | 2 + spec/interactors/unverified_auth_organizer.rb | 48 +++++++++++++++++++ spec/interactors/user_found_by_email_spec.rb | 22 +++++++++ .../verified_auth_organizer_spec.rb | 20 ++++++++ 6 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 spec/interactors/unverified_auth_organizer.rb create mode 100644 spec/interactors/user_found_by_email_spec.rb create mode 100644 spec/interactors/verified_auth_organizer_spec.rb diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 915bfda1..14a7adbc 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -20,7 +20,6 @@ def user_params def sign_in_user confirm_user - reset_session sign_in(user, bypass: true) redirect_to root_path, notice: "Welcome!" end @@ -31,6 +30,7 @@ def render_errors def confirm_user if session[:auth_verified?] + session[:auth_verified?] = nil user.update_attribute(:confirmed_at, Time.zone.now) else user.send_confirmation_instructions diff --git a/app/interactors/verified_auth_organizer.rb b/app/interactors/verified_auth_organizer.rb index 70c22313..e01ff60b 100644 --- a/app/interactors/verified_auth_organizer.rb +++ b/app/interactors/verified_auth_organizer.rb @@ -13,14 +13,14 @@ def user private def user_found_by_uid - @user_found_by_uid ||= SocialProfile.from_omniauth(auth).try(:user) + SocialProfile.from_omniauth(auth).try(:user) end def user_found_by_email - @user_found_by_email ||= UserFoundByEmail.new(auth).call + UserFoundByEmail.new(auth).call end def new_user - @new_user ||= CreateUserFromAuth.new(auth).call + CreateUserFromAuth.new(auth).call end end diff --git a/spec/interactors/create_user_from_auth_spec.rb b/spec/interactors/create_user_from_auth_spec.rb index ca3082a1..2ad97d3f 100644 --- a/spec/interactors/create_user_from_auth_spec.rb +++ b/spec/interactors/create_user_from_auth_spec.rb @@ -12,5 +12,7 @@ it "creates new user from auth hash" do expect { subject }.to change { User.count }.by(1) expect(sent_emails).to eq(0) + expect(user.email).to eq(auth_hashie.info.email) + expect(user.full_name).to eq(auth_hashie.info.name) end end diff --git a/spec/interactors/unverified_auth_organizer.rb b/spec/interactors/unverified_auth_organizer.rb new file mode 100644 index 00000000..a1827a5b --- /dev/null +++ b/spec/interactors/unverified_auth_organizer.rb @@ -0,0 +1,48 @@ +require "rails_helper" + +describe UnverifiedAuthOrganizer do + include_context :auth_hashie + + let(:service) { described_class.new(auth_hashie) } + + subject { service.user } + + context "when user found by email" do + before { create(:user, :from_auth_hashie) } + + it "raises error" do + expect { subject }.to raise_error(AuthVerificationPolicy::OauthError) + end + end + + context "when user found by uid" do + let!(:social_profile) { create(:social_profile, provider: auth_hashie.provider, uid: auth_hashie.uid, user: user) } + + let(:email) { ActionMailer::Base.deliveries.last } + + context "when user confirmed" do + let!(:user) { create(:user, :confirmed, :from_auth_hashie) } + + it "not sends confirmation notification" do + expect(email).to be_nil + end + end + + context "when user not confirmed" do + let!(:user) { create(:user, :from_auth_hashie) } + + it "sends confirmation notification" do + expect(email).to be_truthy + end + end + end + + context "when user not found" do + let(:user) { User.last } + + it "creates new user from auth_hashie" do + expect { subject }.to change { User.count }.by(1) + expect(subject).to eq(user) + end + end +end diff --git a/spec/interactors/user_found_by_email_spec.rb b/spec/interactors/user_found_by_email_spec.rb new file mode 100644 index 00000000..ba61eaec --- /dev/null +++ b/spec/interactors/user_found_by_email_spec.rb @@ -0,0 +1,22 @@ +require "rails_helper" + +describe UserFoundByEmail do + include_context :auth_hashie + + let(:service) { described_class.new(auth_hashie) } + + subject { service.call } + + context "when user not exists" do + it { is_expected.to be_nil } + end + + context "when user exists" do + let!(:user) { create(:user, :from_auth_hashie) } + + it "creates new social_profile" do + expect { subject }.to change { user.social_profiles.count }.by(1) + expect(subject).to eq(user) + end + end +end diff --git a/spec/interactors/verified_auth_organizer_spec.rb b/spec/interactors/verified_auth_organizer_spec.rb new file mode 100644 index 00000000..e87367f8 --- /dev/null +++ b/spec/interactors/verified_auth_organizer_spec.rb @@ -0,0 +1,20 @@ +require "rails_helper" + +describe VerifiedAuthOrganizer do + include_context :auth_hashie + + let(:service) { described_class.new(auth_hashie) } + + before do + allow(SocialProfile).to receive_message_chain(:from_omniauth, :user) + allow(UserFoundByEmail).to receive_message_chain(:new, :call) + allow(CreateUserFromAuth).to receive_message_chain(:new, :call) + service.user + end + + it "fetches existing user or create new one" do + expect(SocialProfile).to have_received(:from_omniauth) + expect(UserFoundByEmail).to have_received(:new) + expect(CreateUserFromAuth).to have_received(:new) + end +end From 4dac3e426533069ee2092d53a04041bdf3eaeabc Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Tue, 22 Dec 2015 19:47:22 +0300 Subject: [PATCH 15/34] extend specs --- .../process_user_confirmation_spec.rb | 19 +++++++++++++ ...r.rb => unverified_auth_organizer_spec.rb} | 0 spec/models/social_profile_spec.rb | 27 +++++++++++++++++++ spec/models/social_profiles_spec.rb | 11 -------- 4 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 spec/interactors/process_user_confirmation_spec.rb rename spec/interactors/{unverified_auth_organizer.rb => unverified_auth_organizer_spec.rb} (100%) create mode 100644 spec/models/social_profile_spec.rb delete mode 100644 spec/models/social_profiles_spec.rb diff --git a/spec/interactors/process_user_confirmation_spec.rb b/spec/interactors/process_user_confirmation_spec.rb new file mode 100644 index 00000000..3d25bb72 --- /dev/null +++ b/spec/interactors/process_user_confirmation_spec.rb @@ -0,0 +1,19 @@ +require "rails_helper" + +describe ProcessUserConfirmation do + let(:user) { create(:user) } + let(:service) { described_class.new(user) } + + before do + allow(user).to receive(:reset_password) + allow(user).to receive(:confirm) + allow(user).to receive(:send_reset_password_instructions) + service.call + end + + it "invokes user confirmation methods" do + expect(user).to have_received(:reset_password) + expect(user).to have_received(:confirm) + expect(user).to have_received(:send_reset_password_instructions) + end +end diff --git a/spec/interactors/unverified_auth_organizer.rb b/spec/interactors/unverified_auth_organizer_spec.rb similarity index 100% rename from spec/interactors/unverified_auth_organizer.rb rename to spec/interactors/unverified_auth_organizer_spec.rb diff --git a/spec/models/social_profile_spec.rb b/spec/models/social_profile_spec.rb new file mode 100644 index 00000000..b66aab41 --- /dev/null +++ b/spec/models/social_profile_spec.rb @@ -0,0 +1,27 @@ +require "rails_helper" + +describe SocialProfile do + subject { create :social_profile } + + it { should belong_to :user } + it { is_expected.to validate_presence_of :user } + it { is_expected.to validate_presence_of :uid } + it { is_expected.to validate_presence_of :provider } + it { is_expected.to validate_uniqueness_of(:uid).scoped_to(:provider) } + + describe ".from_omniauth" do + include_context :auth_hashie + + subject { described_class.from_omniauth(auth_hashie) } + + context "when record exists" do + let!(:social_profile) { create(:social_profile, uid: auth_hashie.uid, provider: auth_hashie.provider) } + + it { is_expected.to eq(social_profile) } + end + + context "when record not exists" do + it { is_expected.to be_nil } + end + end +end diff --git a/spec/models/social_profiles_spec.rb b/spec/models/social_profiles_spec.rb deleted file mode 100644 index 79c30cf3..00000000 --- a/spec/models/social_profiles_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -require "rails_helper" - -describe SocialProfile do - subject { create :social_profile } - - it { should belong_to :user } - it { is_expected.to validate_presence_of :user } - it { is_expected.to validate_presence_of :uid } - it { is_expected.to validate_presence_of :provider } - it { is_expected.to validate_uniqueness_of(:uid).scoped_to(:provider) } -end From f72241e0dc318f4644ab3a21fe86df2749261d21 Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Tue, 22 Dec 2015 23:12:44 +0300 Subject: [PATCH 16/34] start feature specs --- .../user/connect_social_account_spec.rb | 19 ++++++ spec/features/user/connect_social_spec.rb | 0 .../visitor/sign_in_with_social_spec.rb | 58 ++++++++----------- .../sign_in_with_social_spec_example.rb | 46 +++++++++++++++ ...rb => sign_up_with_social_spec_example.rb} | 0 5 files changed, 88 insertions(+), 35 deletions(-) create mode 100644 spec/features/user/connect_social_account_spec.rb delete mode 100644 spec/features/user/connect_social_spec.rb create mode 100644 spec/features/visitor/sign_in_with_social_spec_example.rb rename spec/features/visitor/{sign_up_with_social_spec.rb => sign_up_with_social_spec_example.rb} (100%) diff --git a/spec/features/user/connect_social_account_spec.rb b/spec/features/user/connect_social_account_spec.rb new file mode 100644 index 00000000..e4a95dd2 --- /dev/null +++ b/spec/features/user/connect_social_account_spec.rb @@ -0,0 +1,19 @@ +require "rails_helper" + +feature "Connect social account" do + context "oauth confirmed" do + context "user confirmed" do + end + + context "user not confirmed" do + end + end + + context "oauth not confirmed" do + context "user confirmed" do + end + + context "user not confirmed" do + end + end +end diff --git a/spec/features/user/connect_social_spec.rb b/spec/features/user/connect_social_spec.rb deleted file mode 100644 index e69de29b..00000000 diff --git a/spec/features/visitor/sign_in_with_social_spec.rb b/spec/features/visitor/sign_in_with_social_spec.rb index 799a7867..7e5d31af 100644 --- a/spec/features/visitor/sign_in_with_social_spec.rb +++ b/spec/features/visitor/sign_in_with_social_spec.rb @@ -1,47 +1,35 @@ require "rails_helper" -feature "Sign In with social account" do - include_context :stub_omniauth - - let!(:user) { create :user, :confirmed, :from_auth_hashie } - - def sign_in_with_facebook - visit new_user_session_path - - click_link "Sign in with Facebook" - end +feature "Sign in with social account" do + context "when oauth confirmed" do + context "when user found by uid" do + context "when user confirmed" do + end + + context "when user not confirmed" do + end + end - scenario "user signs in directly to main page" do - sign_in_with_facebook + context "when user found by email" do + context "when user confirmed" do + end - expect(page).to have_content("Home") - expect(page).to have_content(user.full_name) - expect(page).to have_content(user.email) - end - - context "when social network account is not verified" do - include_context :stub_not_verified_omniauth + context "when user not confirmed" do + end + end - # scenario "is notified with notice" do - # sign_in_with_facebook - # - # expect(page).to have_content("Your Facebook account can't be used to sign in. Please verify it via profile page.") - # expect(page).not_to have_content(user.full_name) - # expect(page).not_to have_content(user.email) - # end + context "when user not found" do + end end - context "when user account is not confirmed" do - let!(:user) { create :user, :not_confirmed, :from_auth_hashie } + context "when oauth not confirmed" do + context "when user found by uid" do + end - scenario "user is offered to create new account", :js do - sign_in_with_facebook - save_and_open_screenshot + context "when user found by email" do + end - expect(page).to have_content("You have to confirm your email address before continuing") - expect(page).to have_field("Enter your email address") - expect(page).to have_field("Password") - expect(page).to have_button("Sign in") + context "when user not found" do end end end diff --git a/spec/features/visitor/sign_in_with_social_spec_example.rb b/spec/features/visitor/sign_in_with_social_spec_example.rb new file mode 100644 index 00000000..ca08c923 --- /dev/null +++ b/spec/features/visitor/sign_in_with_social_spec_example.rb @@ -0,0 +1,46 @@ +require "rails_helper" + +feature "Sign In with social account" do + include_context :stub_omniauth + + let!(:user) { create :user, :confirmed, :from_auth_hashie } + + def sign_in_with_facebook + visit new_user_session_path + + click_link "Sign in with Facebook" + end + + scenario "user signs in directly to main page" do + sign_in_with_facebook + + expect(page).to have_content("Home") + expect(page).to have_content(user.full_name) + expect(page).to have_content(user.email) + end + + context "when social network account is not verified" do + include_context :stub_not_verified_omniauth + + # scenario "is notified with notice" do + # sign_in_with_facebook + # + # expect(page).to have_content("Your Facebook account can't be used to sign in. Please verify it via profile page.") + # expect(page).not_to have_content(user.full_name) + # expect(page).not_to have_content(user.email) + # end + end + + context "when user account is not confirmed" do + let!(:user) { create :user, :not_confirmed, :from_auth_hashie } + + scenario "user is offered to create new account", :js do + sign_in_with_facebook + + expect(page).to have_content("You have to confirm your email address before continuing") + expect(page).to have_field("Enter your email address") + expect(page).to have_field("Password") + expect(page).to have_button("Sign in") + end + end +end diff --git a/spec/features/visitor/sign_up_with_social_spec.rb b/spec/features/visitor/sign_up_with_social_spec_example.rb similarity index 100% rename from spec/features/visitor/sign_up_with_social_spec.rb rename to spec/features/visitor/sign_up_with_social_spec_example.rb From cb2c7727f156b0e5bb9c8d30d5cecd3fc3c0d3f3 Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Wed, 23 Dec 2015 22:50:41 +0300 Subject: [PATCH 17/34] connect social profile specs: wip --- app/interactors/process_user_confirmation.rb | 2 +- .../user/connect_social_account_spec.rb | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/app/interactors/process_user_confirmation.rb b/app/interactors/process_user_confirmation.rb index 893bea9d..477e58be 100644 --- a/app/interactors/process_user_confirmation.rb +++ b/app/interactors/process_user_confirmation.rb @@ -7,8 +7,8 @@ def initialize(user) end def call - user.reset_password(new_password, new_password) user.confirm + # user.update(password: new_password, password_confirmation: new_password) user.send_reset_password_instructions end diff --git a/spec/features/user/connect_social_account_spec.rb b/spec/features/user/connect_social_account_spec.rb index e4a95dd2..7447cb87 100644 --- a/spec/features/user/connect_social_account_spec.rb +++ b/spec/features/user/connect_social_account_spec.rb @@ -1,16 +1,54 @@ require "rails_helper" feature "Connect social account" do + before do + login_as(user, scope: :user) + visit edit_user_registration_path(user) + click_link "Facebook" + end + context "oauth confirmed" do + include_context :stub_omniauth + context "user confirmed" do + let!(:user) { create(:user, :confirmed, :from_auth_hashie) } + + scenario "User connects social account" do + within ".js-social-profiles" do + expect(page).to have_content("Facebook") + end + end end context "user not confirmed" do + let!(:user) { create(:user, :from_auth_hashie) } + + scenario "User connects social account" do + within ".js-social-profiles" do + expect(page).to have_content("Facebook") + end + + open_email(user.email) + + expect(current_email).to have_subject("Confirmation instructions") + expect(current_email).to have_body_text(user.full_name) + end end end context "oauth not confirmed" do + include_context :stub_not_verified_omniauth + context "user confirmed" do + let!(:user) { create(:user, :confirmed, :from_auth_hashie) } + + scenario "User connects social account", :js do + save_and_open_screenshot + + # within ".js-social-profiles" do + # expect(page).to have_content("Facebook") + # end + end end context "user not confirmed" do From b34c9ed5cf7678cc8780925c127c0fdda8766cab Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Fri, 25 Dec 2015 23:11:14 +0300 Subject: [PATCH 18/34] connect social profiles spec done --- .../omniauth_callbacks_controller.rb | 2 +- .../user/connect_social_account_spec.rb | 39 ++++++++++++------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index d2852d25..4d357986 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -4,7 +4,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController begin current_user ? connect_social_profile : handle_sign_in rescue AuthVerificationPolicy::OauthError => e - redirect_to root_url, error: e.message + redirect_to edit_user_registration_path, flash: { notice: e.to_s } end end end diff --git a/spec/features/user/connect_social_account_spec.rb b/spec/features/user/connect_social_account_spec.rb index 7447cb87..f3d2725a 100644 --- a/spec/features/user/connect_social_account_spec.rb +++ b/spec/features/user/connect_social_account_spec.rb @@ -1,20 +1,22 @@ require "rails_helper" feature "Connect social account" do - before do - login_as(user, scope: :user) - visit edit_user_registration_path(user) - click_link "Facebook" - end + let(:social_profiles_section) { ".js-social-profiles" } context "oauth confirmed" do include_context :stub_omniauth + before do + login_as(user, scope: :user) + visit edit_user_registration_path(user) + click_link "Facebook" + end + context "user confirmed" do let!(:user) { create(:user, :confirmed, :from_auth_hashie) } scenario "User connects social account" do - within ".js-social-profiles" do + within social_profiles_section do expect(page).to have_content("Facebook") end end @@ -24,7 +26,7 @@ let!(:user) { create(:user, :from_auth_hashie) } scenario "User connects social account" do - within ".js-social-profiles" do + within social_profiles_section do expect(page).to have_content("Facebook") end @@ -39,19 +41,30 @@ context "oauth not confirmed" do include_context :stub_not_verified_omniauth + before do + login_as(user, scope: :user) + visit edit_user_registration_path(user) + click_link "Facebook" + end + context "user confirmed" do let!(:user) { create(:user, :confirmed, :from_auth_hashie) } - scenario "User connects social account", :js do - save_and_open_screenshot - - # within ".js-social-profiles" do - # expect(page).to have_content("Facebook") - # end + scenario "User connects social account" do + within social_profiles_section do + expect(page).to have_content("Facebook") + end end end context "user not confirmed" do + let!(:user) { create(:user, :from_auth_hashie) } + + scenario "User sees alert" do + expect(page).not_to have_css(social_profiles_section) + expect(page).to have_text("Please confirm your account before connecting your facebook account.") + expect(current_path).to eq(edit_user_registration_path) + end end end end From 16aca482a0371fc549900017b34f2b2006eafa06 Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Fri, 25 Dec 2015 23:27:07 +0300 Subject: [PATCH 19/34] improve specs --- .../features/user/connect_social_account_spec.rb | 16 ++++------------ spec/support/matchers/have_connected_account.rb | 7 +++++++ 2 files changed, 11 insertions(+), 12 deletions(-) create mode 100644 spec/support/matchers/have_connected_account.rb diff --git a/spec/features/user/connect_social_account_spec.rb b/spec/features/user/connect_social_account_spec.rb index f3d2725a..a9347f66 100644 --- a/spec/features/user/connect_social_account_spec.rb +++ b/spec/features/user/connect_social_account_spec.rb @@ -1,8 +1,6 @@ require "rails_helper" feature "Connect social account" do - let(:social_profiles_section) { ".js-social-profiles" } - context "oauth confirmed" do include_context :stub_omniauth @@ -16,9 +14,7 @@ let!(:user) { create(:user, :confirmed, :from_auth_hashie) } scenario "User connects social account" do - within social_profiles_section do - expect(page).to have_content("Facebook") - end + expect(page).to have_connected_account("Facebook") end end @@ -26,9 +22,7 @@ let!(:user) { create(:user, :from_auth_hashie) } scenario "User connects social account" do - within social_profiles_section do - expect(page).to have_content("Facebook") - end + expect(page).to have_connected_account("Facebook") open_email(user.email) @@ -51,9 +45,7 @@ let!(:user) { create(:user, :confirmed, :from_auth_hashie) } scenario "User connects social account" do - within social_profiles_section do - expect(page).to have_content("Facebook") - end + expect(page).to have_connected_account("Facebook") end end @@ -61,7 +53,7 @@ let!(:user) { create(:user, :from_auth_hashie) } scenario "User sees alert" do - expect(page).not_to have_css(social_profiles_section) + expect(page).not_to have_css(".js-social-profiles") expect(page).to have_text("Please confirm your account before connecting your facebook account.") expect(current_path).to eq(edit_user_registration_path) end diff --git a/spec/support/matchers/have_connected_account.rb b/spec/support/matchers/have_connected_account.rb new file mode 100644 index 00000000..301f8685 --- /dev/null +++ b/spec/support/matchers/have_connected_account.rb @@ -0,0 +1,7 @@ +RSpec::Matchers.define :have_connected_account do |social_profile| + match do + within ".js-social-profiles" do + have_text(social_profile) + end + end +end From f1d0127a0fce7d2f2d719672d7c67b8664060c19 Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Sat, 26 Dec 2015 00:25:54 +0300 Subject: [PATCH 20/34] extend specs --- .../omniauth_callbacks_controller.rb | 2 +- .../visitor/sign_in_with_social_spec.rb | 81 +++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 4d357986..d7f5504e 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -4,7 +4,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController begin current_user ? connect_social_profile : handle_sign_in rescue AuthVerificationPolicy::OauthError => e - redirect_to edit_user_registration_path, flash: { notice: e.to_s } + redirect_to root_path, flash: { notice: e.to_s } end end end diff --git a/spec/features/visitor/sign_in_with_social_spec.rb b/spec/features/visitor/sign_in_with_social_spec.rb index 7e5d31af..cd3c44b3 100644 --- a/spec/features/visitor/sign_in_with_social_spec.rb +++ b/spec/features/visitor/sign_in_with_social_spec.rb @@ -2,34 +2,115 @@ feature "Sign in with social account" do context "when oauth confirmed" do + include_context :stub_omniauth + context "when user found by uid" do + let!(:social_profile) { create(:social_profile, user: user) } + + before do + visit new_user_session_path + click_link "Sign in with Facebook" + end + context "when user confirmed" do + let!(:user) { create(:user, :confirmed, :from_auth_hashie) } + + scenario "User signs up" do + expect(page).to have_text(user.full_name) + expect(current_path).to eq(root_path) + end end context "when user not confirmed" do + let!(:user) { create(:user, :from_auth_hashie) } + + scenario "Users signs up through Finish page" do + expect(page).to have_text("Finish Signup") + # TODO: finish signup + end end end context "when user found by email" do context "when user confirmed" do + let!(:user) { create(:user, :confirmed, :from_auth_hashie) } + + before do + visit new_user_session_path + click_link "Sign in with Facebook" + end + + scenario "Users connects social profile" do + expect(page).to have_text(user.full_name) + expect(current_path).to eq(root_path) + end end context "when user not confirmed" do + let!(:user) { create(:user, :from_auth_hashie) } + + before do + visit new_user_session_path + click_link "Sign in with Facebook" + end + + scenario "Users connects social profile" do + expect(page).to have_text("Finish Signup") + end end end context "when user not found" do + before do + visit new_user_session_path + click_link "Sign in with Facebook" + end + + scenario "Users connects social profile" do + expect(page).to have_text("Finish Signup") + end end end context "when oauth not confirmed" do + include_context :stub_not_verified_omniauth + context "when user found by uid" do + let!(:social_profile) { create(:social_profile, user: user) } + let!(:user) { create(:user, :confirmed) } + + before do + visit new_user_session_path + click_link "Sign in with Facebook" + end + + scenario "Users connects social profile" do + expect(page).to have_text("Finish Signup") + end end context "when user found by email" do + let!(:user) { create(:user, :confirmed, :from_auth_hashie) } + + before do + visit new_user_session_path + click_link "Sign in with Facebook" + end + + scenario "Users connects social profile" do + expect(page).to have_text("Please, connect your account from profile page.") + end end context "when user not found" do + before do + visit new_user_session_path + click_link "Sign in with Facebook" + end + + scenario "Users connects social profile" do + expect(page).to have_text("Finish Signup") + end end end end From aa1776cd96be12e7bcb4da39fb9ca850eb972ffd Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Sat, 26 Dec 2015 23:08:01 +0300 Subject: [PATCH 21/34] extend sign_in_with_social_spec --- spec/factories/social_profiles.rb | 2 +- .../visitor/sign_in_with_social_spec.rb | 68 +++++++++++++++++-- 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/spec/factories/social_profiles.rb b/spec/factories/social_profiles.rb index 45755b4d..483521b7 100644 --- a/spec/factories/social_profiles.rb +++ b/spec/factories/social_profiles.rb @@ -2,6 +2,6 @@ factory :social_profile do user provider "facebook" - uid "12345" + uid "123545" end end diff --git a/spec/features/visitor/sign_in_with_social_spec.rb b/spec/features/visitor/sign_in_with_social_spec.rb index cd3c44b3..385d549f 100644 --- a/spec/features/visitor/sign_in_with_social_spec.rb +++ b/spec/features/visitor/sign_in_with_social_spec.rb @@ -24,9 +24,21 @@ context "when user not confirmed" do let!(:user) { create(:user, :from_auth_hashie) } - scenario "Users signs up through Finish page" do + scenario "Users should finish registration" do expect(page).to have_text("Finish Signup") - # TODO: finish signup + + fill_in :user_email, with: "mailer@mail.com" + fill_in :user_password, with: "123456qwe" + click_button "Finish Signup" + + open_email("mailer@mail.com") + expect(current_email).to have_subject("Confirmation instructions") + expect(current_email).to have_body_text(user.full_name) + + visit_in_email("Confirm my account") + expect(page).to have_content("Your email address has been successfully confirmed") + expect(page).to have_text(user.full_name) + expect(page).to have_text("mailer@mail.com") end end end @@ -54,20 +66,48 @@ click_link "Sign in with Facebook" end - scenario "Users connects social profile" do + scenario "Users should finish registration" do expect(page).to have_text("Finish Signup") + + fill_in :user_email, with: "mailer@mail.com" + fill_in :user_password, with: "123456qwe" + click_button "Finish Signup" + + open_email("mailer@mail.com") + expect(current_email).to have_subject("Confirmation instructions") + expect(current_email).to have_body_text(user.full_name) + + visit_in_email("Confirm my account") + expect(page).to have_content("Your email address has been successfully confirmed") + expect(page).to have_text(user.full_name) + expect(page).to have_text("mailer@mail.com") end end end context "when user not found" do + let(:name_from_auth_hash) { "Joe Bloggs" } + before do visit new_user_session_path click_link "Sign in with Facebook" end - scenario "Users connects social profile" do + scenario "Users should finish registration" do expect(page).to have_text("Finish Signup") + + fill_in :user_email, with: "mailer@mail.com" + fill_in :user_password, with: "123456qwe" + click_button "Finish Signup" + + open_email("mailer@mail.com") + expect(current_email).to have_subject("Confirmation instructions") + expect(current_email).to have_body_text(name_from_auth_hash) + + visit_in_email("Confirm my account") + expect(page).to have_content("Your email address has been successfully confirmed") + expect(page).to have_text(name_from_auth_hash) + expect(page).to have_text("mailer@mail.com") end end end @@ -84,8 +124,8 @@ click_link "Sign in with Facebook" end - scenario "Users connects social profile" do - expect(page).to have_text("Finish Signup") + scenario "User signs in" do + expect(page).to have_text(user.full_name) end end @@ -99,10 +139,13 @@ scenario "Users connects social profile" do expect(page).to have_text("Please, connect your account from profile page.") + expect(current_path).to eq(root_path) end end context "when user not found" do + let(:name_from_auth_hash) { "Joe Bloggs" } + before do visit new_user_session_path click_link "Sign in with Facebook" @@ -110,6 +153,19 @@ scenario "Users connects social profile" do expect(page).to have_text("Finish Signup") + + fill_in :user_email, with: "mailer@mail.com" + fill_in :user_password, with: "123456qwe" + click_button "Finish Signup" + + open_email("mailer@mail.com") + expect(current_email).to have_subject("Confirmation instructions") + expect(current_email).to have_body_text(name_from_auth_hash) + + visit_in_email("Confirm my account") + expect(page).to have_content("Your email address has been successfully confirmed") + expect(page).to have_text(name_from_auth_hash) + expect(page).to have_text("mailer@mail.com") end end end From b6d918240fc91f9a740e6a40abf6b6f5e7ff7adc Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Sat, 26 Dec 2015 23:40:16 +0300 Subject: [PATCH 22/34] use shared_examples --- .../visitor/sign_in_with_social_spec.rb | 95 ++++--------------- .../shared_examples/finishing_sign_up.rb | 23 +++++ 2 files changed, 39 insertions(+), 79 deletions(-) create mode 100644 spec/support/shared_examples/finishing_sign_up.rb diff --git a/spec/features/visitor/sign_in_with_social_spec.rb b/spec/features/visitor/sign_in_with_social_spec.rb index 385d549f..53852159 100644 --- a/spec/features/visitor/sign_in_with_social_spec.rb +++ b/spec/features/visitor/sign_in_with_social_spec.rb @@ -24,21 +24,10 @@ context "when user not confirmed" do let!(:user) { create(:user, :from_auth_hashie) } - scenario "Users should finish registration" do - expect(page).to have_text("Finish Signup") - - fill_in :user_email, with: "mailer@mail.com" - fill_in :user_password, with: "123456qwe" - click_button "Finish Signup" - - open_email("mailer@mail.com") - expect(current_email).to have_subject("Confirmation instructions") - expect(current_email).to have_body_text(user.full_name) - - visit_in_email("Confirm my account") - expect(page).to have_content("Your email address has been successfully confirmed") - expect(page).to have_text(user.full_name) - expect(page).to have_text("mailer@mail.com") + include_examples "finishing_sign_up" do + let(:name) { user.full_name } + let(:email) { "mailer@mail.com" } + let(:password) { "123456qwe" } end end end @@ -61,53 +50,19 @@ context "when user not confirmed" do let!(:user) { create(:user, :from_auth_hashie) } - before do - visit new_user_session_path - click_link "Sign in with Facebook" - end - - scenario "Users should finish registration" do - expect(page).to have_text("Finish Signup") - - fill_in :user_email, with: "mailer@mail.com" - fill_in :user_password, with: "123456qwe" - click_button "Finish Signup" - - open_email("mailer@mail.com") - expect(current_email).to have_subject("Confirmation instructions") - expect(current_email).to have_body_text(user.full_name) - - visit_in_email("Confirm my account") - expect(page).to have_content("Your email address has been successfully confirmed") - expect(page).to have_text(user.full_name) - expect(page).to have_text("mailer@mail.com") + include_examples "finishing_sign_up" do + let(:name) { user.full_name } + let(:email) { "mailer@mail.com" } + let(:password) { "123456qwe" } end end end context "when user not found" do - let(:name_from_auth_hash) { "Joe Bloggs" } - - before do - visit new_user_session_path - click_link "Sign in with Facebook" - end - - scenario "Users should finish registration" do - expect(page).to have_text("Finish Signup") - - fill_in :user_email, with: "mailer@mail.com" - fill_in :user_password, with: "123456qwe" - click_button "Finish Signup" - - open_email("mailer@mail.com") - expect(current_email).to have_subject("Confirmation instructions") - expect(current_email).to have_body_text(name_from_auth_hash) - - visit_in_email("Confirm my account") - expect(page).to have_content("Your email address has been successfully confirmed") - expect(page).to have_text(name_from_auth_hash) - expect(page).to have_text("mailer@mail.com") + include_examples "finishing_sign_up" do + let(:name) { "Joe Bloggs" } + let(:email) { "mailer@mail.com" } + let(:password) { "123456qwe" } end end end @@ -144,28 +99,10 @@ end context "when user not found" do - let(:name_from_auth_hash) { "Joe Bloggs" } - - before do - visit new_user_session_path - click_link "Sign in with Facebook" - end - - scenario "Users connects social profile" do - expect(page).to have_text("Finish Signup") - - fill_in :user_email, with: "mailer@mail.com" - fill_in :user_password, with: "123456qwe" - click_button "Finish Signup" - - open_email("mailer@mail.com") - expect(current_email).to have_subject("Confirmation instructions") - expect(current_email).to have_body_text(name_from_auth_hash) - - visit_in_email("Confirm my account") - expect(page).to have_content("Your email address has been successfully confirmed") - expect(page).to have_text(name_from_auth_hash) - expect(page).to have_text("mailer@mail.com") + include_examples "finishing_sign_up" do + let(:name) { "Joe Bloggs" } + let(:email) { "mailer@mail.com" } + let(:password) { "123456qwe" } end end end diff --git a/spec/support/shared_examples/finishing_sign_up.rb b/spec/support/shared_examples/finishing_sign_up.rb new file mode 100644 index 00000000..921120c8 --- /dev/null +++ b/spec/support/shared_examples/finishing_sign_up.rb @@ -0,0 +1,23 @@ +RSpec.shared_examples "finishing_sign_up" do + before do + visit new_user_session_path + click_link "Sign in with Facebook" + end + + scenario "User finishing registration" do + expect(page).to have_text("Finish Signup") + + fill_in :user_email, with: email + fill_in :user_password, with: password + click_button "Finish Signup" + + open_email("mailer@mail.com") + expect(current_email).to have_subject("Confirmation instructions") + expect(current_email).to have_body_text(name) + + visit_in_email("Confirm my account") + expect(page).to have_content("Your email address has been successfully confirmed") + expect(page).to have_text(name) + expect(page).to have_text("mailer@mail.com") + end +end From 4970525a85a46acdbec81dc961e1c82b8633521b Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Mon, 28 Dec 2015 23:14:35 +0300 Subject: [PATCH 23/34] use more clean shared context methods --- .../visitor/sign_in_with_social_spec.rb | 49 +++++++------------ .../shared_examples/finishing_sign_up.rb | 2 +- .../shared_examples/success sign in.rb | 6 +++ 3 files changed, 24 insertions(+), 33 deletions(-) create mode 100644 spec/support/shared_examples/success sign in.rb diff --git a/spec/features/visitor/sign_in_with_social_spec.rb b/spec/features/visitor/sign_in_with_social_spec.rb index 53852159..4209fc09 100644 --- a/spec/features/visitor/sign_in_with_social_spec.rb +++ b/spec/features/visitor/sign_in_with_social_spec.rb @@ -7,24 +7,18 @@ context "when user found by uid" do let!(:social_profile) { create(:social_profile, user: user) } - before do - visit new_user_session_path - click_link "Sign in with Facebook" - end + before { click_sign_in_with_fb } context "when user confirmed" do let!(:user) { create(:user, :confirmed, :from_auth_hashie) } - scenario "User signs up" do - expect(page).to have_text(user.full_name) - expect(current_path).to eq(root_path) - end + it_behaves_like "success sign in" end context "when user not confirmed" do let!(:user) { create(:user, :from_auth_hashie) } - include_examples "finishing_sign_up" do + it_behaves_like "finishing sign up" do let(:name) { user.full_name } let(:email) { "mailer@mail.com" } let(:password) { "123456qwe" } @@ -36,21 +30,15 @@ context "when user confirmed" do let!(:user) { create(:user, :confirmed, :from_auth_hashie) } - before do - visit new_user_session_path - click_link "Sign in with Facebook" - end + before { click_sign_in_with_fb } - scenario "Users connects social profile" do - expect(page).to have_text(user.full_name) - expect(current_path).to eq(root_path) - end + it_behaves_like "success sign in" end context "when user not confirmed" do let!(:user) { create(:user, :from_auth_hashie) } - include_examples "finishing_sign_up" do + it_behaves_like "finishing sign up" do let(:name) { user.full_name } let(:email) { "mailer@mail.com" } let(:password) { "123456qwe" } @@ -59,7 +47,7 @@ end context "when user not found" do - include_examples "finishing_sign_up" do + it_behaves_like "finishing sign up" do let(:name) { "Joe Bloggs" } let(:email) { "mailer@mail.com" } let(:password) { "123456qwe" } @@ -74,36 +62,33 @@ let!(:social_profile) { create(:social_profile, user: user) } let!(:user) { create(:user, :confirmed) } - before do - visit new_user_session_path - click_link "Sign in with Facebook" - end + before { click_sign_in_with_fb } - scenario "User signs in" do - expect(page).to have_text(user.full_name) - end + it_behaves_like "success sign in" end context "when user found by email" do let!(:user) { create(:user, :confirmed, :from_auth_hashie) } - before do - visit new_user_session_path - click_link "Sign in with Facebook" - end + before { click_sign_in_with_fb } - scenario "Users connects social profile" do + scenario "User sees alert message" do expect(page).to have_text("Please, connect your account from profile page.") expect(current_path).to eq(root_path) end end context "when user not found" do - include_examples "finishing_sign_up" do + it_behaves_like "finishing sign up" do let(:name) { "Joe Bloggs" } let(:email) { "mailer@mail.com" } let(:password) { "123456qwe" } end end end + + def click_sign_in_with_fb + visit new_user_session_path + click_link "Sign in with Facebook" + end end diff --git a/spec/support/shared_examples/finishing_sign_up.rb b/spec/support/shared_examples/finishing_sign_up.rb index 921120c8..c68e8989 100644 --- a/spec/support/shared_examples/finishing_sign_up.rb +++ b/spec/support/shared_examples/finishing_sign_up.rb @@ -1,4 +1,4 @@ -RSpec.shared_examples "finishing_sign_up" do +shared_examples_for "finishing sign up" do before do visit new_user_session_path click_link "Sign in with Facebook" diff --git a/spec/support/shared_examples/success sign in.rb b/spec/support/shared_examples/success sign in.rb new file mode 100644 index 00000000..2f972498 --- /dev/null +++ b/spec/support/shared_examples/success sign in.rb @@ -0,0 +1,6 @@ +shared_examples_for "success sign in" do + scenario "User signs in" do + expect(page).to have_text(user.full_name) + expect(current_path).to eq(root_path) + end +end From 4010bb893fa159d4624dc775a5e2390fdcab09ff Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Wed, 30 Dec 2015 22:15:31 +0300 Subject: [PATCH 24/34] refactor specs --- .../omniauth_callbacks_controller.rb | 2 +- .../user/connect_social_account_spec.rb | 22 ++++----- .../sign_in_with_social_spec_example.rb | 46 ------------------- .../sign_up_with_social_spec_example.rb | 30 ------------ .../process_user_confirmation_spec.rb | 1 - spec/models/social_profile_spec.rb | 2 +- ...{success sign in.rb => success_sign_in.rb} | 3 +- 7 files changed, 14 insertions(+), 92 deletions(-) delete mode 100644 spec/features/visitor/sign_in_with_social_spec_example.rb delete mode 100644 spec/features/visitor/sign_up_with_social_spec_example.rb rename spec/support/shared_examples/{success sign in.rb => success_sign_in.rb} (52%) diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index d7f5504e..29a9b7d2 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -35,7 +35,7 @@ def auth_verified? def after_sign_in_path_for(resource) if resource.confirmed? - super resource + edit_user_registration_path else session[:auth_verified?] = auth_verified? resource.reset_password(new_password, new_password) diff --git a/spec/features/user/connect_social_account_spec.rb b/spec/features/user/connect_social_account_spec.rb index a9347f66..eba326ef 100644 --- a/spec/features/user/connect_social_account_spec.rb +++ b/spec/features/user/connect_social_account_spec.rb @@ -4,11 +4,7 @@ context "oauth confirmed" do include_context :stub_omniauth - before do - login_as(user, scope: :user) - visit edit_user_registration_path(user) - click_link "Facebook" - end + before { click_connect_fb } context "user confirmed" do let!(:user) { create(:user, :confirmed, :from_auth_hashie) } @@ -21,7 +17,7 @@ context "user not confirmed" do let!(:user) { create(:user, :from_auth_hashie) } - scenario "User connects social account" do + scenario "User have to confirm own account" do expect(page).to have_connected_account("Facebook") open_email(user.email) @@ -35,11 +31,7 @@ context "oauth not confirmed" do include_context :stub_not_verified_omniauth - before do - login_as(user, scope: :user) - visit edit_user_registration_path(user) - click_link "Facebook" - end + before { click_connect_fb } context "user confirmed" do let!(:user) { create(:user, :confirmed, :from_auth_hashie) } @@ -55,8 +47,14 @@ scenario "User sees alert" do expect(page).not_to have_css(".js-social-profiles") expect(page).to have_text("Please confirm your account before connecting your facebook account.") - expect(current_path).to eq(edit_user_registration_path) + expect(current_path).to eq(root_path) end end end + + def click_connect_fb + login_as(user, scope: :user) + visit edit_user_registration_path(user) + click_link "Facebook" + end end diff --git a/spec/features/visitor/sign_in_with_social_spec_example.rb b/spec/features/visitor/sign_in_with_social_spec_example.rb deleted file mode 100644 index ca08c923..00000000 --- a/spec/features/visitor/sign_in_with_social_spec_example.rb +++ /dev/null @@ -1,46 +0,0 @@ -require "rails_helper" - -feature "Sign In with social account" do - include_context :stub_omniauth - - let!(:user) { create :user, :confirmed, :from_auth_hashie } - - def sign_in_with_facebook - visit new_user_session_path - - click_link "Sign in with Facebook" - end - - scenario "user signs in directly to main page" do - sign_in_with_facebook - - expect(page).to have_content("Home") - expect(page).to have_content(user.full_name) - expect(page).to have_content(user.email) - end - - context "when social network account is not verified" do - include_context :stub_not_verified_omniauth - - # scenario "is notified with notice" do - # sign_in_with_facebook - # - # expect(page).to have_content("Your Facebook account can't be used to sign in. Please verify it via profile page.") - # expect(page).not_to have_content(user.full_name) - # expect(page).not_to have_content(user.email) - # end - end - - context "when user account is not confirmed" do - let!(:user) { create :user, :not_confirmed, :from_auth_hashie } - - scenario "user is offered to create new account", :js do - sign_in_with_facebook - - expect(page).to have_content("You have to confirm your email address before continuing") - expect(page).to have_field("Enter your email address") - expect(page).to have_field("Password") - expect(page).to have_button("Sign in") - end - end -end diff --git a/spec/features/visitor/sign_up_with_social_spec_example.rb b/spec/features/visitor/sign_up_with_social_spec_example.rb deleted file mode 100644 index 217bd890..00000000 --- a/spec/features/visitor/sign_up_with_social_spec_example.rb +++ /dev/null @@ -1,30 +0,0 @@ -require "rails_helper" - -feature "Sign Up with social account" do - include_context :stub_omniauth - - let(:name_from_auth_hash) { "Joe Bloggs" } - - scenario "Visitor signs up" do - visit new_user_session_path - - click_link "Sign in with Facebook" - - expect(page).to have_content("Finish Signup") - - fill_in :user_email, with: "mailer@mail.com" - fill_in :user_password, with: "123456qwe" - click_button "Finish Signup" - - open_email("mailer@mail.com") - - expect(current_email).to have_subject("Confirmation instructions") - expect(current_email).to have_body_text(name_from_auth_hash) - - visit_in_email("Confirm my account") - - expect(page).to have_content("Your email address has been successfully confirmed") - expect(page).to have_text(name_from_auth_hash) - expect(page).to have_text("mailer@mail.com") - end -end diff --git a/spec/interactors/process_user_confirmation_spec.rb b/spec/interactors/process_user_confirmation_spec.rb index 3d25bb72..f3790d4a 100644 --- a/spec/interactors/process_user_confirmation_spec.rb +++ b/spec/interactors/process_user_confirmation_spec.rb @@ -12,7 +12,6 @@ end it "invokes user confirmation methods" do - expect(user).to have_received(:reset_password) expect(user).to have_received(:confirm) expect(user).to have_received(:send_reset_password_instructions) end diff --git a/spec/models/social_profile_spec.rb b/spec/models/social_profile_spec.rb index b66aab41..a640a9d8 100644 --- a/spec/models/social_profile_spec.rb +++ b/spec/models/social_profile_spec.rb @@ -3,7 +3,7 @@ describe SocialProfile do subject { create :social_profile } - it { should belong_to :user } + it { is_expected.to belong_to :user } it { is_expected.to validate_presence_of :user } it { is_expected.to validate_presence_of :uid } it { is_expected.to validate_presence_of :provider } diff --git a/spec/support/shared_examples/success sign in.rb b/spec/support/shared_examples/success_sign_in.rb similarity index 52% rename from spec/support/shared_examples/success sign in.rb rename to spec/support/shared_examples/success_sign_in.rb index 2f972498..a51f6609 100644 --- a/spec/support/shared_examples/success sign in.rb +++ b/spec/support/shared_examples/success_sign_in.rb @@ -1,6 +1,7 @@ shared_examples_for "success sign in" do scenario "User signs in" do expect(page).to have_text(user.full_name) - expect(current_path).to eq(root_path) + expect(current_path).to eq(edit_user_registration_path) + expect(page).to have_connected_account("Facebook") end end From 00e28ef89b85bd0738e432e461133e40f9d452dd Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Wed, 30 Dec 2015 22:52:28 +0300 Subject: [PATCH 25/34] refactor specs --- .../process_user_confirmation_spec.rb | 1 - .../unverified_auth_organizer_spec.rb | 8 +++-- .../verified_auth_organizer_spec.rb | 30 +++++++++++++------ .../shared_examples/finishing_sign_up.rb | 4 +-- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/spec/interactors/process_user_confirmation_spec.rb b/spec/interactors/process_user_confirmation_spec.rb index f3790d4a..fa8dbade 100644 --- a/spec/interactors/process_user_confirmation_spec.rb +++ b/spec/interactors/process_user_confirmation_spec.rb @@ -5,7 +5,6 @@ let(:service) { described_class.new(user) } before do - allow(user).to receive(:reset_password) allow(user).to receive(:confirm) allow(user).to receive(:send_reset_password_instructions) service.call diff --git a/spec/interactors/unverified_auth_organizer_spec.rb b/spec/interactors/unverified_auth_organizer_spec.rb index a1827a5b..b3b86d21 100644 --- a/spec/interactors/unverified_auth_organizer_spec.rb +++ b/spec/interactors/unverified_auth_organizer_spec.rb @@ -18,21 +18,23 @@ context "when user found by uid" do let!(:social_profile) { create(:social_profile, provider: auth_hashie.provider, uid: auth_hashie.uid, user: user) } - let(:email) { ActionMailer::Base.deliveries.last } + let(:emails) { ActionMailer::Base.deliveries } context "when user confirmed" do let!(:user) { create(:user, :confirmed, :from_auth_hashie) } it "not sends confirmation notification" do - expect(email).to be_nil + expect(emails).to be_empty end end context "when user not confirmed" do let!(:user) { create(:user, :from_auth_hashie) } + let(:email) { ActionMailer::Base.deliveries.last } it "sends confirmation notification" do - expect(email).to be_truthy + expect(email.subject).to eq("Confirmation instructions") + expect(email.to).to eq([user.email]) end end end diff --git a/spec/interactors/verified_auth_organizer_spec.rb b/spec/interactors/verified_auth_organizer_spec.rb index e87367f8..e498edc2 100644 --- a/spec/interactors/verified_auth_organizer_spec.rb +++ b/spec/interactors/verified_auth_organizer_spec.rb @@ -5,16 +5,28 @@ let(:service) { described_class.new(auth_hashie) } - before do - allow(SocialProfile).to receive_message_chain(:from_omniauth, :user) - allow(UserFoundByEmail).to receive_message_chain(:new, :call) - allow(CreateUserFromAuth).to receive_message_chain(:new, :call) - service.user + subject { service.user } + + context "when social profile exists" do + let!(:social_profile) { create(:social_profile, uid: auth_hashie.uid, provider: auth_hashie.provider) } + + it { is_expected.to eq(social_profile.user) } end - it "fetches existing user or create new one" do - expect(SocialProfile).to have_received(:from_omniauth) - expect(UserFoundByEmail).to have_received(:new) - expect(CreateUserFromAuth).to have_received(:new) + context "when social profile not exists" do + context "when user exists" do + let!(:user) { create(:user, :from_auth_hashie) } + + it "creates related social profile" do + expect { subject }.to change { user.social_profiles.count }.by(1) + expect(subject).to eq(user) + end + end + + context "when user not exists" do + it "creates new one" do + expect { subject }.to change { User.count }.by(1) + end + end end end diff --git a/spec/support/shared_examples/finishing_sign_up.rb b/spec/support/shared_examples/finishing_sign_up.rb index c68e8989..368c3d98 100644 --- a/spec/support/shared_examples/finishing_sign_up.rb +++ b/spec/support/shared_examples/finishing_sign_up.rb @@ -11,13 +11,13 @@ fill_in :user_password, with: password click_button "Finish Signup" - open_email("mailer@mail.com") + open_email(email) expect(current_email).to have_subject("Confirmation instructions") expect(current_email).to have_body_text(name) visit_in_email("Confirm my account") expect(page).to have_content("Your email address has been successfully confirmed") expect(page).to have_text(name) - expect(page).to have_text("mailer@mail.com") + expect(page).to have_text(email) end end From 0aaa1aebfd7192b6fde9333befba12d8b89f9af6 Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Mon, 4 Jan 2016 22:46:59 +0300 Subject: [PATCH 26/34] apply review comments: refactor interactors and specs --- app/controllers/omniauth_callbacks_controller.rb | 10 +++++----- app/interactors/process_user_confirmation.rb | 7 ------- app/interactors/unverified_auth_organizer.rb | 15 +++++++-------- spec/features/user/connect_social_account_spec.rb | 1 - 4 files changed, 12 insertions(+), 21 deletions(-) diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 29a9b7d2..bee67a14 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -21,14 +21,14 @@ def auth end def handle_sign_in - user = if auth_verified? - VerifiedAuthOrganizer.new(auth).user - else - UnverifiedAuthOrganizer.new(auth).user - end + user = auth_organizer.new(auth).user sign_in_and_redirect user, event: :authentication end + def auth_organizer + auth_verified? ? VerifiedAuthOrganizer : UnverifiedAuthOrganizer + end + def auth_verified? AuthVerificationPolicy.new(auth).verified? end diff --git a/app/interactors/process_user_confirmation.rb b/app/interactors/process_user_confirmation.rb index 477e58be..773e5069 100644 --- a/app/interactors/process_user_confirmation.rb +++ b/app/interactors/process_user_confirmation.rb @@ -8,13 +8,6 @@ def initialize(user) def call user.confirm - # user.update(password: new_password, password_confirmation: new_password) user.send_reset_password_instructions end - - private - - def new_password - @new_password ||= Devise.friendly_token.first(8) - end end diff --git a/app/interactors/unverified_auth_organizer.rb b/app/interactors/unverified_auth_organizer.rb index 39b8d923..fa95aa15 100644 --- a/app/interactors/unverified_auth_organizer.rb +++ b/app/interactors/unverified_auth_organizer.rb @@ -7,18 +7,17 @@ def initialize(auth) end def user - if user_found_by_email.present? - fail AuthVerificationPolicy::OauthError, "Please, connect your account from profile page." - else - found_user.send_confirmation_instructions unless found_user.confirmed? - found_user - end + check_user_with_email! + + found_user.send_confirmation_instructions unless found_user.confirmed? + found_user end private - def user_found_by_email - @user_found_by_email ||= User.find_by(email: auth["info"]["email"]) + def check_user_with_email! + user_with_email = User.find_by(email: auth["info"]["email"]) + fail AuthVerificationPolicy::OauthError, "Please, connect your account from profile page." if user_with_email end def found_user diff --git a/spec/features/user/connect_social_account_spec.rb b/spec/features/user/connect_social_account_spec.rb index eba326ef..23181359 100644 --- a/spec/features/user/connect_social_account_spec.rb +++ b/spec/features/user/connect_social_account_spec.rb @@ -45,7 +45,6 @@ let!(:user) { create(:user, :from_auth_hashie) } scenario "User sees alert" do - expect(page).not_to have_css(".js-social-profiles") expect(page).to have_text("Please confirm your account before connecting your facebook account.") expect(current_path).to eq(root_path) end From c555cde04de795698cb59313bcf7cf27a1c3385b Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Mon, 4 Jan 2016 23:03:03 +0300 Subject: [PATCH 27/34] refactoring in specs and interactors --- app/interactors/create_user_from_auth.rb | 4 ++-- ...er_found_by_email.rb => find_user_by_email_service.rb} | 6 +++--- app/interactors/unverified_auth_organizer.rb | 8 ++++---- app/interactors/verified_auth_organizer.rb | 2 +- spec/interactors/user_found_by_email_spec.rb | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) rename app/interactors/{user_found_by_email.rb => find_user_by_email_service.rb} (58%) diff --git a/app/interactors/create_user_from_auth.rb b/app/interactors/create_user_from_auth.rb index f2fd6586..eae04f7c 100644 --- a/app/interactors/create_user_from_auth.rb +++ b/app/interactors/create_user_from_auth.rb @@ -8,8 +8,8 @@ def initialize(auth) def call user = User.new( - email: auth["info"]["email"], - full_name: auth["info"]["name"], + email: auth.info.email, + full_name: auth.info.name, password: new_password, password_confirmation: new_password ) diff --git a/app/interactors/user_found_by_email.rb b/app/interactors/find_user_by_email_service.rb similarity index 58% rename from app/interactors/user_found_by_email.rb rename to app/interactors/find_user_by_email_service.rb index 1bca39b1..641155df 100644 --- a/app/interactors/user_found_by_email.rb +++ b/app/interactors/find_user_by_email_service.rb @@ -1,4 +1,4 @@ -class UserFoundByEmail +class FindUserByEmailService attr_reader :auth private :auth @@ -15,10 +15,10 @@ def call private def user - @user ||= User.find_by(email: auth["info"]["email"]) + @user ||= User.find_by(email: auth.info.email) end def create_social_profile - user.social_profiles.create!(provider: auth["provider"], uid: auth["uid"]) + user.social_profiles.create!(provider: auth.provider, uid: auth.uid) end end diff --git a/app/interactors/unverified_auth_organizer.rb b/app/interactors/unverified_auth_organizer.rb index fa95aa15..50d4e7cb 100644 --- a/app/interactors/unverified_auth_organizer.rb +++ b/app/interactors/unverified_auth_organizer.rb @@ -7,7 +7,7 @@ def initialize(auth) end def user - check_user_with_email! + check_user_by_email! found_user.send_confirmation_instructions unless found_user.confirmed? found_user @@ -15,9 +15,9 @@ def user private - def check_user_with_email! - user_with_email = User.find_by(email: auth["info"]["email"]) - fail AuthVerificationPolicy::OauthError, "Please, connect your account from profile page." if user_with_email + def check_user_by_email! + user_by_email = User.find_by(email: auth.info.email) + fail AuthVerificationPolicy::OauthError, "Please, connect your account from profile page." if user_by_email end def found_user diff --git a/app/interactors/verified_auth_organizer.rb b/app/interactors/verified_auth_organizer.rb index e01ff60b..4bb6c0e8 100644 --- a/app/interactors/verified_auth_organizer.rb +++ b/app/interactors/verified_auth_organizer.rb @@ -17,7 +17,7 @@ def user_found_by_uid end def user_found_by_email - UserFoundByEmail.new(auth).call + FindUserByEmailService.new(auth).call end def new_user diff --git a/spec/interactors/user_found_by_email_spec.rb b/spec/interactors/user_found_by_email_spec.rb index ba61eaec..1523ef11 100644 --- a/spec/interactors/user_found_by_email_spec.rb +++ b/spec/interactors/user_found_by_email_spec.rb @@ -1,6 +1,6 @@ require "rails_helper" -describe UserFoundByEmail do +describe FindUserByEmailService do include_context :auth_hashie let(:service) { described_class.new(auth_hashie) } From 714184966d9b67b1912b19a7f43c3e5fc1f8fd61 Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Tue, 5 Jan 2016 22:26:18 +0300 Subject: [PATCH 28/34] upgrade ruby version --- .ruby-version | 2 +- Gemfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.ruby-version b/.ruby-version index 58594069..530cdd91 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.2.3 +2.2.4 diff --git a/Gemfile b/Gemfile index 5aee83b0..80f956f4 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source "https://rubygems.org" -ruby "2.2.3" +ruby "2.2.4" gem "rails", "4.2.3" gem "pg" From 4e1074008b5318998811c7a4c24bebe4270baa7e Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Tue, 5 Jan 2016 22:56:20 +0300 Subject: [PATCH 29/34] fix specs --- spec/models/social_profile_spec.rb | 2 +- spec/rails_helper.rb | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/spec/models/social_profile_spec.rb b/spec/models/social_profile_spec.rb index a640a9d8..c6acfec7 100644 --- a/spec/models/social_profile_spec.rb +++ b/spec/models/social_profile_spec.rb @@ -1,7 +1,7 @@ require "rails_helper" describe SocialProfile do - subject { create :social_profile } + subject { create(:social_profile, uid: "abc123") } it { is_expected.to belong_to :user } it { is_expected.to validate_presence_of :user } diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 0a0460d2..4afb59c2 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -12,3 +12,10 @@ config.use_transactional_fixtures = false config.infer_spec_type_from_file_location! end + +Shoulda::Matchers.configure do |config| + config.integrate do |with| + with.test_framework :rspec + with.library :rails + end +end From 08bc1ea84fc7c471f469e92c17e794751e92fa4c Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Tue, 5 Jan 2016 23:06:09 +0300 Subject: [PATCH 30/34] refactor code --- app/interactors/connect_social_profile.rb | 17 ++++++++------- app/interactors/find_user_by_email_service.rb | 7 ++++--- app/interactors/oauth_connect_organizer.rb | 21 +++++++++++-------- app/interactors/process_user_confirmation.rb | 13 ------------ .../oauth_connect_organizer_spec.rb | 8 ++++--- .../process_user_confirmation_spec.rb | 17 --------------- .../unverified_auth_organizer_spec.rb | 2 +- 7 files changed, 31 insertions(+), 54 deletions(-) delete mode 100644 app/interactors/process_user_confirmation.rb delete mode 100644 spec/interactors/process_user_confirmation_spec.rb diff --git a/app/interactors/connect_social_profile.rb b/app/interactors/connect_social_profile.rb index 7efbaea3..2b92e7d8 100644 --- a/app/interactors/connect_social_profile.rb +++ b/app/interactors/connect_social_profile.rb @@ -3,15 +3,12 @@ class ConnectSocialProfile private :user, :auth def initialize(user, auth) - @user, @auth = user, auth + @user = user + @auth = auth end def call - if social_profile - social_profile.update_attribute(:user, user) - else - create_social_profile! - end + social_profile ? update_social_profile : create_social_profile end private @@ -20,7 +17,11 @@ def social_profile @social_profile ||= SocialProfile.from_omniauth(auth) end - def create_social_profile! - user.social_profiles.create!(provider: auth["provider"], uid: auth["uid"]) + def update_social_profile + social_profile.update_attribute(:user, user) + end + + def create_social_profile + user.social_profiles.create!(provider: auth.provider, uid: auth.uid) end end diff --git a/app/interactors/find_user_by_email_service.rb b/app/interactors/find_user_by_email_service.rb index 641155df..6e9e6b87 100644 --- a/app/interactors/find_user_by_email_service.rb +++ b/app/interactors/find_user_by_email_service.rb @@ -7,9 +7,10 @@ def initialize(auth) end def call - return unless user - create_social_profile - user + if user + create_social_profile + user + end end private diff --git a/app/interactors/oauth_connect_organizer.rb b/app/interactors/oauth_connect_organizer.rb index c7d81728..439fef80 100644 --- a/app/interactors/oauth_connect_organizer.rb +++ b/app/interactors/oauth_connect_organizer.rb @@ -3,7 +3,8 @@ class OauthConnectOrganizer private :auth, :user def initialize(auth, user) - @auth, @user = auth, user + @auth = auth + @user = user end def call @@ -16,19 +17,21 @@ def call private - def fail_oauth - fail AuthVerificationPolicy::OauthError, "Please confirm your account before connecting your #{auth.provider} account." - end - def auth_verified? AuthVerificationPolicy.new(auth).verified? end - def connect_social_profile - ConnectSocialProfile.new(user, auth).call + def process_user_confirmation + user.confirm + user.send_reset_password_instructions end - def process_user_confirmation - ProcessUserConfirmation.new(user).call + def fail_oauth + fail AuthVerificationPolicy::OauthError, + "Please confirm your account before connecting your #{auth.provider} account." + end + + def connect_social_profile + ConnectSocialProfile.new(user, auth).call end end diff --git a/app/interactors/process_user_confirmation.rb b/app/interactors/process_user_confirmation.rb deleted file mode 100644 index 773e5069..00000000 --- a/app/interactors/process_user_confirmation.rb +++ /dev/null @@ -1,13 +0,0 @@ -class ProcessUserConfirmation - attr_reader :user - private :user - - def initialize(user) - @user = user - end - - def call - user.confirm - user.send_reset_password_instructions - end -end diff --git a/spec/interactors/oauth_connect_organizer_spec.rb b/spec/interactors/oauth_connect_organizer_spec.rb index 48976fca..3d1aa1e2 100644 --- a/spec/interactors/oauth_connect_organizer_spec.rb +++ b/spec/interactors/oauth_connect_organizer_spec.rb @@ -33,12 +33,14 @@ context "when auth verified" do before do - allow(ProcessUserConfirmation).to receive_message_chain(:new, :call) + allow(user).to receive(:confirm) + allow(user).to receive(:send_reset_password_instructions) service.call end - it "invokes ProcessUserConfirmation" do - expect(ProcessUserConfirmation).to have_received(:new).with(user) + it "confirms user" do + expect(user).to have_received(:confirm) + expect(user).to have_received(:send_reset_password_instructions) end end end diff --git a/spec/interactors/process_user_confirmation_spec.rb b/spec/interactors/process_user_confirmation_spec.rb deleted file mode 100644 index fa8dbade..00000000 --- a/spec/interactors/process_user_confirmation_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require "rails_helper" - -describe ProcessUserConfirmation do - let(:user) { create(:user) } - let(:service) { described_class.new(user) } - - before do - allow(user).to receive(:confirm) - allow(user).to receive(:send_reset_password_instructions) - service.call - end - - it "invokes user confirmation methods" do - expect(user).to have_received(:confirm) - expect(user).to have_received(:send_reset_password_instructions) - end -end diff --git a/spec/interactors/unverified_auth_organizer_spec.rb b/spec/interactors/unverified_auth_organizer_spec.rb index b3b86d21..3ae4eaac 100644 --- a/spec/interactors/unverified_auth_organizer_spec.rb +++ b/spec/interactors/unverified_auth_organizer_spec.rb @@ -30,7 +30,7 @@ context "when user not confirmed" do let!(:user) { create(:user, :from_auth_hashie) } - let(:email) { ActionMailer::Base.deliveries.last } + let(:email) { emails.last } it "sends confirmation notification" do expect(email.subject).to eq("Confirmation instructions") From e7c7821de8de6cdffbf3479d15b6c9cbaa23cc4b Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Tue, 5 Jan 2016 23:50:26 +0300 Subject: [PATCH 31/34] fix cops --- Gemfile.lock | 8 ++++---- app/interactors/find_user_by_email_service.rb | 8 ++++---- config.ru | 2 +- config/environments/development.rb | 2 +- config/environments/test.rb | 2 +- spec/factories/feedbacks.rb | 2 +- ...y_email_spec.rb => find_user_by_email_service_spec.rb} | 0 7 files changed, 12 insertions(+), 12 deletions(-) rename spec/interactors/{user_found_by_email_spec.rb => find_user_by_email_service_spec.rb} (100%) diff --git a/Gemfile.lock b/Gemfile.lock index d6d948f1..7e1f3a87 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -184,13 +184,13 @@ GEM rails (>= 3.0.0) method_source (0.8.2) mime-types (2.6.2) - mini_portile (0.6.2) + mini_portile2 (2.0.0) minitest (5.8.1) multi_json (1.11.2) multi_xml (0.5.5) multipart-post (2.0.0) - nokogiri (1.6.6.2) - mini_portile (~> 0.6.0) + nokogiri (1.6.7.1) + mini_portile2 (~> 2.0.0.rc2) oauth2 (1.0.0) faraday (>= 0.8, < 0.10) jwt (~> 1.0) @@ -465,4 +465,4 @@ DEPENDENCIES webmock BUNDLED WITH - 1.10.6 + 1.11.2 diff --git a/app/interactors/find_user_by_email_service.rb b/app/interactors/find_user_by_email_service.rb index 6e9e6b87..b32dc0f0 100644 --- a/app/interactors/find_user_by_email_service.rb +++ b/app/interactors/find_user_by_email_service.rb @@ -7,10 +7,10 @@ def initialize(auth) end def call - if user - create_social_profile - user - end + return unless user + + create_social_profile + user end private diff --git a/config.ru b/config.ru index 93fb28fa..193e5fed 100644 --- a/config.ru +++ b/config.ru @@ -1,4 +1,4 @@ # This file is used by Rack-based servers to start the application. -require ::File.expand_path("../config/environment", __FILE__) +require ::File.expand_path("../config/environment", __FILE__) run Rails.application diff --git a/config/environments/development.rb b/config/environments/development.rb index 53838903..2f14eb9a 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -44,5 +44,5 @@ # Application specific options # - config.slim_options = { pretty: true } + config.slim_options = { pretty: true } end diff --git a/config/environments/test.rb b/config/environments/test.rb index 88d6da92..e3f40aaa 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -13,7 +13,7 @@ config.eager_load = false # Configure static asset server for tests with Cache-Control for performance. - config.serve_static_files = true + config.serve_static_files = true config.static_cache_control = "public, max-age=3600" # Show full error reports and disable caching. diff --git a/spec/factories/feedbacks.rb b/spec/factories/feedbacks.rb index 5cc030b2..f3893e8f 100644 --- a/spec/factories/feedbacks.rb +++ b/spec/factories/feedbacks.rb @@ -1,7 +1,7 @@ FactoryGirl.define do factory :feedback do email - name { Faker::Name.name } + name { Faker::Name.name } message { Faker::Lorem.paragraph } phone { Faker::PhoneNumber.phone_number } end diff --git a/spec/interactors/user_found_by_email_spec.rb b/spec/interactors/find_user_by_email_service_spec.rb similarity index 100% rename from spec/interactors/user_found_by_email_spec.rb rename to spec/interactors/find_user_by_email_service_spec.rb From db56b6648fdcc433cc73e6b5bf0b3fe558bf66e7 Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Thu, 21 Jan 2016 22:27:15 +0300 Subject: [PATCH 32/34] apply review comments: retrieve providers list dynamically --- app/models/social_profile.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/social_profile.rb b/app/models/social_profile.rb index 87e1087c..1c382d23 100644 --- a/app/models/social_profile.rb +++ b/app/models/social_profile.rb @@ -1,5 +1,5 @@ class SocialProfile < ActiveRecord::Base - PROVIDERS = %i(facebook google_oauth2) + PROVIDERS = OmniAuth.strategies.map { |s| s.to_s.demodulize.underscore }.last(2) belongs_to :user From cabe00c20fed9eef4a426407a2b7ad4acd808dec Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Thu, 21 Jan 2016 22:28:53 +0300 Subject: [PATCH 33/34] upgrade unpatched gems --- Gemfile.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7e1f3a87..01ef885e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -102,7 +102,7 @@ GEM database_cleaner (1.5.0) debug_inspector (0.0.2) decent_exposure (2.3.2) - devise (3.5.2) + devise (3.5.4) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 3.2.6, < 5) @@ -185,11 +185,11 @@ GEM method_source (0.8.2) mime-types (2.6.2) mini_portile2 (2.0.0) - minitest (5.8.1) + minitest (5.8.3) multi_json (1.11.2) multi_xml (0.5.5) multipart-post (2.0.0) - nokogiri (1.6.7.1) + nokogiri (1.6.7.2) mini_portile2 (~> 2.0.0.rc2) oauth2 (1.0.0) faraday (>= 0.8, < 0.10) @@ -277,11 +277,11 @@ GEM rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.0.0) - rake (10.4.2) + rake (10.5.0) ref (2.0.0) require_all (1.3.2) - responders (2.1.0) - railties (>= 4.2.0, < 5) + responders (2.1.1) + railties (>= 4.2.0, < 5.1) rollbar (0.10.14) multi_json (~> 1.5) rspec (3.3.0) @@ -383,7 +383,7 @@ GEM execjs (>= 0.3.0) json (>= 1.8.0) uniform_notifier (1.9.0) - warden (1.2.3) + warden (1.2.4) rack (>= 1.0) web-console (2.2.1) activemodel (>= 4.0) From f6280e6c61fb518a49c3147e0372cb8932e48fad Mon Sep 17 00:00:00 2001 From: Ruslan Khaertdinov Date: Mon, 1 Feb 2016 22:18:43 +0300 Subject: [PATCH 34/34] apply review comments: drop first unused omniauth provider from strategiest list --- app/models/social_profile.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/social_profile.rb b/app/models/social_profile.rb index 1c382d23..e1118f6b 100644 --- a/app/models/social_profile.rb +++ b/app/models/social_profile.rb @@ -1,5 +1,5 @@ class SocialProfile < ActiveRecord::Base - PROVIDERS = OmniAuth.strategies.map { |s| s.to_s.demodulize.underscore }.last(2) + PROVIDERS = OmniAuth.strategies.map { |s| s.to_s.demodulize.underscore }.drop(1) belongs_to :user