Commit 5c510d69b4daa882c3a075c24a287573c93cd470

Authored by 839
1 parent 6ef69937
Exists in master

Add sign in with google omniauth

@@ -23,3 +23,5 @@ GITHUB_API_URL=https://api.github.com @@ -23,3 +23,5 @@ GITHUB_API_URL=https://api.github.com
23 GITHUB_ACCESS_SCOPE='[repo]' 23 GITHUB_ACCESS_SCOPE='[repo]'
24 GITHUB_SITE_TITLE=GitHub 24 GITHUB_SITE_TITLE=GitHub
25 DEVISE_MODULES='[database_authenticatable,recoverable,rememberable,trackable,validatable,omniauthable]' 25 DEVISE_MODULES='[database_authenticatable,recoverable,rememberable,trackable,validatable,omniauthable]'
  26 +GOOGLE_AUTHENTICATION=true
  27 +GOOGLE_SITE_TITLE=Google
@@ -50,6 +50,8 @@ gem 'flowdock' @@ -50,6 +50,8 @@ gem 'flowdock'
50 # --------------------------------------- 50 # ---------------------------------------
51 # GitHub OAuth 51 # GitHub OAuth
52 gem 'omniauth-github' 52 gem 'omniauth-github'
  53 +# Google OAuth
  54 +gem 'omniauth-google-oauth2'
53 55
54 gem 'ri_cal' 56 gem 'ri_cal'
55 gem 'yajl-ruby', platform: 'ruby' 57 gem 'yajl-ruby', platform: 'ruby'
@@ -228,6 +228,11 @@ GEM @@ -228,6 +228,11 @@ GEM
228 omniauth-github (1.1.2) 228 omniauth-github (1.1.2)
229 omniauth (~> 1.0) 229 omniauth (~> 1.0)
230 omniauth-oauth2 (~> 1.1) 230 omniauth-oauth2 (~> 1.1)
  231 + omniauth-google-oauth2 (0.4.0)
  232 + jwt (~> 1.5.0)
  233 + multi_json (~> 1.3)
  234 + omniauth (>= 1.1.1)
  235 + omniauth-oauth2 (>= 1.3.1)
231 omniauth-oauth2 (1.3.1) 236 omniauth-oauth2 (1.3.1)
232 oauth2 (~> 1.0) 237 oauth2 (~> 1.0)
233 omniauth (~> 1.2) 238 omniauth (~> 1.2)
@@ -460,6 +465,7 @@ DEPENDENCIES @@ -460,6 +465,7 @@ DEPENDENCIES
460 mongoid-rspec (~> 3.0.0) 465 mongoid-rspec (~> 3.0.0)
461 mongoid_rails_migrations 466 mongoid_rails_migrations
462 omniauth-github 467 omniauth-github
  468 + omniauth-google-oauth2
463 pjax_rails 469 pjax_rails
464 poltergeist 470 poltergeist
465 pry-byebug 471 pry-byebug
app/assets/stylesheets/errbit.css.erb
@@ -231,6 +231,7 @@ a.action { float: right; font-size: 0.9em;} @@ -231,6 +231,7 @@ a.action { float: right; font-size: 0.9em;}
231 } 231 }
232 232
233 #action-bar span.github a:before { font-family: FontAwesome; content: "\f09b"; margin-right: 8px; position: relative; top: 4px; font-size: 26px; } 233 #action-bar span.github a:before { font-family: FontAwesome; content: "\f09b"; margin-right: 8px; position: relative; top: 4px; font-size: 26px; }
  234 +#action-bar span.google a:before { font-family: FontAwesome; content: "\f1a0"; margin-right: 8px; position: relative; top: 4px; font-size: 26px; }
234 235
235 #action-bar span a.issue-tracker-button { 236 #action-bar span a.issue-tracker-button {
236 padding-left: 5px; 237 padding-left: 5px;
app/controllers/users/omniauth_callbacks_controller.rb
@@ -39,6 +39,32 @@ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController @@ -39,6 +39,32 @@ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
39 end 39 end
40 end 40 end
41 41
  42 + def google_oauth2
  43 + google_uid = env['omniauth.auth'].uid
  44 + google_email = env['omniauth.auth'].info.email
  45 + google_user = User.where(google_uid: google_uid).first
  46 + google_site_title = Errbit::Config.google_site_title
  47 + # If user is already signed in, link google details to their account
  48 + if current_user
  49 + # ... unless a user is already registered with same google login
  50 + if google_user && google_user != current_user
  51 + flash[:error] = "User already registered with #{google_site_title} login '#{google_email}'!"
  52 + else
  53 + # Add github details to current user
  54 + current_user.update(google_uid: google_uid)
  55 + flash[:success] = "Successfully linked #{google_email} account!"
  56 + end
  57 + # User must have clicked 'link account' from their user page, so redirect there.
  58 + redirect_to user_path(current_user)
  59 + elsif google_user
  60 + flash[:success] = I18n.t 'devise.omniauth_callbacks.success', kind: google_site_title
  61 + sign_in_and_redirect google_user, event: :authentication
  62 + else
  63 + flash[:error] = "There are no authorized users with #{google_site_title} login '#{google_email}'. Please ask an administrator to register your user account."
  64 + redirect_to new_user_session_path
  65 + end
  66 + end
  67 +
42 private def update_user_with_github_attributes(user, login, token) 68 private def update_user_with_github_attributes(user, login, token)
43 user.update_attributes( 69 user.update_attributes(
44 github_login: login, 70 github_login: login,
app/controllers/users_controller.rb
@@ -51,6 +51,11 @@ class UsersController < ApplicationController @@ -51,6 +51,11 @@ class UsersController < ApplicationController
51 redirect_to user_path(user) 51 redirect_to user_path(user)
52 end 52 end
53 53
  54 + def unlink_google
  55 + user.update(google_uid: nil)
  56 + redirect_to user_path(user)
  57 + end
  58 +
54 protected 59 protected
55 60
56 def require_user_edit_priviledges 61 def require_user_edit_priviledges
app/models/user.rb
@@ -8,6 +8,7 @@ class User @@ -8,6 +8,7 @@ class User
8 field :email 8 field :email
9 field :github_login 9 field :github_login
10 field :github_oauth_token 10 field :github_oauth_token
  11 + field :google_uid
11 field :name 12 field :name
12 field :admin, type: Boolean, default: false 13 field :admin, type: Boolean, default: false
13 field :per_page, type: Fixnum, default: PER_PAGE 14 field :per_page, type: Fixnum, default: PER_PAGE
@@ -71,6 +72,10 @@ class User @@ -71,6 +72,10 @@ class User
71 self[:github_login] = login 72 self[:github_login] = login
72 end 73 end
73 74
  75 + def google_account?
  76 + google_uid.present?
  77 + end
  78 +
74 def ensure_authentication_token 79 def ensure_authentication_token
75 if authentication_token.blank? 80 if authentication_token.blank?
76 self.authentication_token = generate_authentication_token 81 self.authentication_token = generate_authentication_token
app/views/devise/sessions/new.html.haml
@@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
5 - content_for :action_bar do 5 - content_for :action_bar do
6 %div.action-bar 6 %div.action-bar
7 %span.github= link_to "Sign in with #{Errbit::Config.github_site_title}", user_omniauth_authorize_path(:github) 7 %span.github= link_to "Sign in with #{Errbit::Config.github_site_title}", user_omniauth_authorize_path(:github)
  8 + %span.google= link_to "Sign in with #{Errbit::Config.google_site_title}", user_omniauth_authorize_path(:google_oauth2)
8 9
9 = form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| 10 = form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f|
10 .required 11 .required
app/views/shared/_link_google_account.html.haml 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +- if Errbit::Config.google_authentication && user == current_user
  2 + - if user.google_account?
  3 + %span.google= link_to "Unlink #{Errbit::Config.google_site_title} account", unlink_google_user_path, method: :delete, data: { confirm: 'Are you sure?' }
  4 + - else
  5 + %span.google= link_to "Link #{Errbit::Config.google_site_title} account", user_omniauth_authorize_path(:google_oauth2)
app/views/users/edit.html.haml
1 - content_for :title, "Edit #{user.name}" 1 - content_for :title, "Edit #{user.name}"
2 - content_for :action_bar do 2 - content_for :action_bar do
3 = render 'shared/link_github_account', :user => user 3 = render 'shared/link_github_account', :user => user
  4 + = render 'shared/link_google_account'
4 = link_to('cancel', user_path(user), :class => 'button') 5 = link_to('cancel', user_path(user), :class => 'button')
5 6
6 = form_for user, :html => {:autocomplete => "off"} do |f| 7 = form_for user, :html => {:autocomplete => "off"} do |f|
app/views/users/show.html.haml
@@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
7 7
8 - content_for :action_bar do 8 - content_for :action_bar do
9 = render 'shared/link_github_account' 9 = render 'shared/link_github_account'
  10 + = render 'shared/link_google_account'
10 = link_to 'edit', edit_user_path(user), :class => 'button' 11 = link_to 'edit', edit_user_path(user), :class => 'button'
11 = link_to 'destroy', user_path(user), :method => :delete, 12 = link_to 'destroy', user_path(user), :method => :delete,
12 :data => { :confirm => t('users.confirm_delete') }, :class => 'delete button' 13 :data => { :confirm => t('users.confirm_delete') }, :class => 'delete button'
config/initializers/devise.rb
@@ -252,6 +252,12 @@ Devise.setup do |config| @@ -252,6 +252,12 @@ Devise.setup do |config|
252 github_options 252 github_options
253 end 253 end
254 254
  255 + if Errbit::Config.google_authentication || Rails.env.test?
  256 + config.omniauth :google_oauth2,
  257 + Errbit::Config.google_client_id,
  258 + Errbit::Config.google_secret
  259 + end
  260 +
255 # ==> Warden configuration 261 # ==> Warden configuration
256 # If you want to use other strategies, that are not supported by Devise, or 262 # If you want to use other strategies, that are not supported by Devise, or
257 # change the failure app, you can configure them inside the config.warden block. 263 # change the failure app, you can configure them inside the config.warden block.
config/load.rb
@@ -39,6 +39,11 @@ Errbit::Config = Configurator.run( @@ -39,6 +39,11 @@ Errbit::Config = Configurator.run(
39 github_access_scope: ['GITHUB_ACCESS_SCOPE'], 39 github_access_scope: ['GITHUB_ACCESS_SCOPE'],
40 github_api_url: ['GITHUB_API_URL'], 40 github_api_url: ['GITHUB_API_URL'],
41 github_site_title: ['GITHUB_SITE_TITLE'], 41 github_site_title: ['GITHUB_SITE_TITLE'],
  42 + # google
  43 + google_authentication: ['GOOGLE_AUTHENTICATION'],
  44 + google_site_title: ['GOOGLE_SITE_TITLE'],
  45 + google_client_id: ['GOOGLE_CLIENT_ID'],
  46 + google_secret: ['GOOGLE_SECRET'],
42 47
43 email_delivery_method: ['EMAIL_DELIVERY_METHOD', lambda do |values| 48 email_delivery_method: ['EMAIL_DELIVERY_METHOD', lambda do |values|
44 values[:email_delivery_method] && values[:email_delivery_method].to_sym 49 values[:email_delivery_method] && values[:email_delivery_method].to_sym
config/routes.rb
@@ -9,6 +9,7 @@ Rails.application.routes.draw do @@ -9,6 +9,7 @@ Rails.application.routes.draw do
9 resources :users do 9 resources :users do
10 member do 10 member do
11 delete :unlink_github 11 delete :unlink_github
  12 + delete :unlink_google
12 end 13 end
13 end 14 end
14 15
spec/acceptance/acceptance_helper.rb
@@ -18,6 +18,14 @@ def mock_auth(user = "test_user", token = "abcdef") @@ -18,6 +18,14 @@ def mock_auth(user = "test_user", token = "abcdef")
18 'token' => token 18 'token' => token
19 } 19 }
20 ) 20 )
  21 +
  22 + OmniAuth.config.mock_auth[:google_oauth2] = Hashie::Mash.new(
  23 + provider: 'google',
  24 + uid: user,
  25 + info: {
  26 + email: 'errbit@errbit.example.com'
  27 + }
  28 + )
21 end 29 end
22 30
23 def log_in(user) 31 def log_in(user)
spec/acceptance/sign_in_with_google_spec.rb 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +require 'acceptance/acceptance_helper'
  2 +
  3 +feature 'Sign in with Google' do
  4 + background do
  5 + allow(Errbit::Config).to receive(:google_authentication).and_return(true)
  6 + Fabricate(:user, google_uid: 'nashby')
  7 + visit root_path
  8 + end
  9 +
  10 + scenario 'log in via Google with recognized user' do
  11 + mock_auth('nashby')
  12 +
  13 + click_link 'Sign in with Google'
  14 + expect(page).to have_content I18n.t('devise.omniauth_callbacks.success', kind: 'Google')
  15 + end
  16 +
  17 + scenario 'reject unrecognized user if authenticating via Google' do
  18 + mock_auth('unknown_user')
  19 +
  20 + click_link 'Sign in with Google'
  21 + expect(page).to have_content 'There are no authorized users with Google login'
  22 + end
  23 +end