Commit 5c510d69b4daa882c3a075c24a287573c93cd470

Authored by 839
1 parent 6ef69937
Exists in master

Add sign in with google omniauth

.env.default
... ... @@ -23,3 +23,5 @@ GITHUB_API_URL=https://api.github.com
23 23 GITHUB_ACCESS_SCOPE='[repo]'
24 24 GITHUB_SITE_TITLE=GitHub
25 25 DEVISE_MODULES='[database_authenticatable,recoverable,rememberable,trackable,validatable,omniauthable]'
  26 +GOOGLE_AUTHENTICATION=true
  27 +GOOGLE_SITE_TITLE=Google
... ...
Gemfile
... ... @@ -50,6 +50,8 @@ gem 'flowdock'
50 50 # ---------------------------------------
51 51 # GitHub OAuth
52 52 gem 'omniauth-github'
  53 +# Google OAuth
  54 +gem 'omniauth-google-oauth2'
53 55  
54 56 gem 'ri_cal'
55 57 gem 'yajl-ruby', platform: 'ruby'
... ...
Gemfile.lock
... ... @@ -228,6 +228,11 @@ GEM
228 228 omniauth-github (1.1.2)
229 229 omniauth (~> 1.0)
230 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 236 omniauth-oauth2 (1.3.1)
232 237 oauth2 (~> 1.0)
233 238 omniauth (~> 1.2)
... ... @@ -460,6 +465,7 @@ DEPENDENCIES
460 465 mongoid-rspec (~> 3.0.0)
461 466 mongoid_rails_migrations
462 467 omniauth-github
  468 + omniauth-google-oauth2
463 469 pjax_rails
464 470 poltergeist
465 471 pry-byebug
... ...
app/assets/stylesheets/errbit.css.erb
... ... @@ -231,6 +231,7 @@ a.action { float: right; font-size: 0.9em;}
231 231 }
232 232  
233 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 236 #action-bar span a.issue-tracker-button {
236 237 padding-left: 5px;
... ...
app/controllers/users/omniauth_callbacks_controller.rb
... ... @@ -39,6 +39,32 @@ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
39 39 end
40 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 68 private def update_user_with_github_attributes(user, login, token)
43 69 user.update_attributes(
44 70 github_login: login,
... ...
app/controllers/users_controller.rb
... ... @@ -51,6 +51,11 @@ class UsersController < ApplicationController
51 51 redirect_to user_path(user)
52 52 end
53 53  
  54 + def unlink_google
  55 + user.update(google_uid: nil)
  56 + redirect_to user_path(user)
  57 + end
  58 +
54 59 protected
55 60  
56 61 def require_user_edit_priviledges
... ...
app/models/user.rb
... ... @@ -8,6 +8,7 @@ class User
8 8 field :email
9 9 field :github_login
10 10 field :github_oauth_token
  11 + field :google_uid
11 12 field :name
12 13 field :admin, type: Boolean, default: false
13 14 field :per_page, type: Fixnum, default: PER_PAGE
... ... @@ -71,6 +72,10 @@ class User
71 72 self[:github_login] = login
72 73 end
73 74  
  75 + def google_account?
  76 + google_uid.present?
  77 + end
  78 +
74 79 def ensure_authentication_token
75 80 if authentication_token.blank?
76 81 self.authentication_token = generate_authentication_token
... ...
app/views/devise/sessions/new.html.haml
... ... @@ -5,6 +5,7 @@
5 5 - content_for :action_bar do
6 6 %div.action-bar
7 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 10 = form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f|
10 11 .required
... ...
app/views/shared/_link_google_account.html.haml 0 → 100644
... ... @@ -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 1 - content_for :title, "Edit #{user.name}"
2 2 - content_for :action_bar do
3 3 = render 'shared/link_github_account', :user => user
  4 + = render 'shared/link_google_account'
4 5 = link_to('cancel', user_path(user), :class => 'button')
5 6  
6 7 = form_for user, :html => {:autocomplete => "off"} do |f|
... ...
app/views/users/show.html.haml
... ... @@ -7,6 +7,7 @@
7 7  
8 8 - content_for :action_bar do
9 9 = render 'shared/link_github_account'
  10 + = render 'shared/link_google_account'
10 11 = link_to 'edit', edit_user_path(user), :class => 'button'
11 12 = link_to 'destroy', user_path(user), :method => :delete,
12 13 :data => { :confirm => t('users.confirm_delete') }, :class => 'delete button'
... ...
config/initializers/devise.rb
... ... @@ -252,6 +252,12 @@ Devise.setup do |config|
252 252 github_options
253 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 261 # ==> Warden configuration
256 262 # If you want to use other strategies, that are not supported by Devise, or
257 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 39 github_access_scope: ['GITHUB_ACCESS_SCOPE'],
40 40 github_api_url: ['GITHUB_API_URL'],
41 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 48 email_delivery_method: ['EMAIL_DELIVERY_METHOD', lambda do |values|
44 49 values[:email_delivery_method] && values[:email_delivery_method].to_sym
... ...
config/routes.rb
... ... @@ -9,6 +9,7 @@ Rails.application.routes.draw do
9 9 resources :users do
10 10 member do
11 11 delete :unlink_github
  12 + delete :unlink_google
12 13 end
13 14 end
14 15  
... ...
spec/acceptance/acceptance_helper.rb
... ... @@ -18,6 +18,14 @@ def mock_auth(user = "test_user", token = "abcdef")
18 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 29 end
22 30  
23 31 def log_in(user)
... ...
spec/acceptance/sign_in_with_google_spec.rb 0 → 100644
... ... @@ -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
... ...