Commit 0d4313dfd5d954c5b8cdfefe63ad7ab0166607b2

Authored by Vasiliy Ermolovich
1 parent ffd87fdd
Exists in master and in 1 other branch production

add github authentication

@@ -7,13 +7,15 @@ gem 'mongoid', '~> 2.2.2' @@ -7,13 +7,15 @@ gem 'mongoid', '~> 2.2.2'
7 gem 'haml' 7 gem 'haml'
8 gem 'htmlentities', "~> 4.3.0" 8 gem 'htmlentities', "~> 4.3.0"
9 gem 'devise', '~> 1.4.0' 9 gem 'devise', '~> 1.4.0'
  10 +gem 'omniauth-github'
  11 +gem 'oa-core'
10 gem 'lighthouse-api' 12 gem 'lighthouse-api'
11 gem 'oruen_redmine_client', :require => 'redmine_client' 13 gem 'oruen_redmine_client', :require => 'redmine_client'
12 gem 'mongoid_rails_migrations' 14 gem 'mongoid_rails_migrations'
13 gem 'useragent', '~> 0.3.1' 15 gem 'useragent', '~> 0.3.1'
14 gem 'pivotal-tracker' 16 gem 'pivotal-tracker'
15 gem 'ruby-fogbugz', :require => 'fogbugz' 17 gem 'ruby-fogbugz', :require => 'fogbugz'
16 -gem 'octokit', '0.6.4' 18 +gem 'octokit'
17 gem 'inherited_resources' 19 gem 'inherited_resources'
18 gem 'SystemTimer', :platform => :ruby_18 20 gem 'SystemTimer', :platform => :ruby_18
19 gem 'hoptoad_notifier', "~> 2.4" 21 gem 'hoptoad_notifier', "~> 2.4"
@@ -41,6 +43,8 @@ group :development, :test do @@ -41,6 +43,8 @@ group :development, :test do
41 end 43 end
42 44
43 group :test do 45 group :test do
  46 + gem 'capybara'
  47 + gem 'launchy'
44 gem 'rspec', '~> 2.6' 48 gem 'rspec', '~> 2.6'
45 gem 'database_cleaner', '~> 0.6.0' 49 gem 'database_cleaner', '~> 0.6.0'
46 gem 'email_spec' 50 gem 'email_spec'
@@ -33,13 +33,22 @@ GEM @@ -33,13 +33,22 @@ GEM
33 activemodel (= 3.0.10) 33 activemodel (= 3.0.10)
34 activesupport (= 3.0.10) 34 activesupport (= 3.0.10)
35 activesupport (3.0.10) 35 activesupport (3.0.10)
36 - addressable (2.2.6) 36 + addressable (2.2.7)
37 archive-tar-minitar (0.5.2) 37 archive-tar-minitar (0.5.2)
38 arel (2.0.10) 38 arel (2.0.10)
39 bcrypt-ruby (3.0.1) 39 bcrypt-ruby (3.0.1)
40 bson (1.3.1) 40 bson (1.3.1)
41 bson_ext (1.3.1) 41 bson_ext (1.3.1)
42 builder (2.1.2) 42 builder (2.1.2)
  43 + capybara (1.1.2)
  44 + mime-types (>= 1.16)
  45 + nokogiri (>= 1.3.3)
  46 + rack (>= 1.0.0)
  47 + rack-test (>= 0.5.4)
  48 + selenium-webdriver (~> 2.0)
  49 + xpath (~> 0.1.4)
  50 + childprocess (0.3.1)
  51 + ffi (~> 1.0.6)
43 columnize (0.3.4) 52 columnize (0.3.4)
44 crack (0.3.1) 53 crack (0.3.1)
45 css_parser (1.2.5) 54 css_parser (1.2.5)
@@ -60,13 +69,14 @@ GEM @@ -60,13 +69,14 @@ GEM
60 addressable (~> 2.2.6) 69 addressable (~> 2.2.6)
61 multipart-post (~> 1.1.0) 70 multipart-post (~> 1.1.0)
62 rack (>= 1.1.0, < 2) 71 rack (>= 1.1.0, < 2)
63 - faraday_middleware (0.7.0)  
64 - faraday (~> 0.7.3) 72 + faraday_middleware (0.8.6)
  73 + faraday (>= 0.7.4, < 0.9)
  74 + ffi (1.0.11)
65 haml (3.1.3) 75 haml (3.1.3)
66 happymapper (0.4.0) 76 happymapper (0.4.0)
67 libxml-ruby (~> 2.0) 77 libxml-ruby (~> 2.0)
68 has_scope (0.5.1) 78 has_scope (0.5.1)
69 - hashie (1.0.0) 79 + hashie (1.2.0)
70 hoptoad_notifier (2.4.11) 80 hoptoad_notifier (2.4.11)
71 activesupport 81 activesupport
72 builder 82 builder
@@ -78,6 +88,8 @@ GEM @@ -78,6 +88,8 @@ GEM
78 kaminari (0.12.4) 88 kaminari (0.12.4)
79 rails (>= 3.0.0) 89 rails (>= 3.0.0)
80 kgio (2.6.0) 90 kgio (2.6.0)
  91 + launchy (2.1.0)
  92 + addressable (~> 2.2.6)
81 libxml-ruby (2.2.2) 93 libxml-ruby (2.2.2)
82 lighthouse-api (2.0) 94 lighthouse-api (2.0)
83 activeresource (>= 3.0.0) 95 activeresource (>= 3.0.0)
@@ -103,15 +115,28 @@ GEM @@ -103,15 +115,28 @@ GEM
103 bundler (>= 1.0.0) 115 bundler (>= 1.0.0)
104 rails (>= 3.0.0) 116 rails (>= 3.0.0)
105 railties (>= 3.0.0) 117 railties (>= 3.0.0)
106 - multi_json (1.0.4)  
107 - multipart-post (1.1.4) 118 + multi_json (1.1.0)
  119 + multipart-post (1.1.5)
108 nokogiri (1.5.0) 120 nokogiri (1.5.0)
109 - octokit (0.6.4)  
110 - addressable (~> 2.2.6)  
111 - faraday (~> 0.7.3)  
112 - faraday_middleware (~> 0.7.0.rc1)  
113 - hashie (~> 1.0.0)  
114 - multi_json (~> 1.0.2) 121 + oa-core (0.3.2)
  122 + oauth2 (0.5.2)
  123 + faraday (~> 0.7)
  124 + multi_json (~> 1.0)
  125 + octokit (1.0.0)
  126 + addressable (~> 2.2)
  127 + faraday (~> 0.7)
  128 + faraday_middleware (~> 0.8)
  129 + hashie (~> 1.2)
  130 + multi_json (~> 1.0)
  131 + omniauth (1.0.3)
  132 + hashie (~> 1.2)
  133 + rack
  134 + omniauth-github (1.0.1)
  135 + omniauth (~> 1.0)
  136 + omniauth-oauth2 (~> 1.0)
  137 + omniauth-oauth2 (1.0.0)
  138 + oauth2 (~> 0.5.0)
  139 + omniauth (~> 1.0)
115 orm_adapter (0.0.5) 140 orm_adapter (0.0.5)
116 oruen_redmine_client (0.0.1) 141 oruen_redmine_client (0.0.1)
117 activeresource (>= 2.3.0) 142 activeresource (>= 2.3.0)
@@ -128,7 +153,7 @@ GEM @@ -128,7 +153,7 @@ GEM
128 premailer (1.7.3) 153 premailer (1.7.3)
129 css_parser (>= 1.1.9) 154 css_parser (>= 1.1.9)
130 htmlentities (>= 4.0.0) 155 htmlentities (>= 4.0.0)
131 - rack (1.2.4) 156 + rack (1.2.5)
132 rack-mount (0.6.14) 157 rack-mount (0.6.14)
133 rack (>= 1.0.0) 158 rack (>= 1.0.0)
134 rack-ssl-enforcer (0.2.4) 159 rack-ssl-enforcer (0.2.4)
@@ -187,6 +212,12 @@ GEM @@ -187,6 +212,12 @@ GEM
187 typhoeus 212 typhoeus
188 ruby_core_source (0.1.5) 213 ruby_core_source (0.1.5)
189 archive-tar-minitar (>= 0.5.2) 214 archive-tar-minitar (>= 0.5.2)
  215 + rubyzip (0.9.6.1)
  216 + selenium-webdriver (2.20.0)
  217 + childprocess (>= 0.2.5)
  218 + ffi (~> 1.0)
  219 + multi_json (~> 1.0)
  220 + rubyzip
190 thor (0.14.6) 221 thor (0.14.6)
191 treetop (1.4.10) 222 treetop (1.4.10)
192 polyglot 223 polyglot
@@ -205,6 +236,8 @@ GEM @@ -205,6 +236,8 @@ GEM
205 webmock (1.7.6) 236 webmock (1.7.6)
206 addressable (~> 2.2, > 2.2.5) 237 addressable (~> 2.2, > 2.2.5)
207 crack (>= 0.1.7) 238 crack (>= 0.1.7)
  239 + xpath (0.1.4)
  240 + nokogiri (~> 1.3)
208 241
209 PLATFORMS 242 PLATFORMS
210 ruby 243 ruby
@@ -214,6 +247,7 @@ DEPENDENCIES @@ -214,6 +247,7 @@ DEPENDENCIES
214 actionmailer_inline_css (~> 1.3.0) 247 actionmailer_inline_css (~> 1.3.0)
215 bson (= 1.3.1) 248 bson (= 1.3.1)
216 bson_ext (= 1.3.1) 249 bson_ext (= 1.3.1)
  250 + capybara
217 database_cleaner (~> 0.6.0) 251 database_cleaner (~> 0.6.0)
218 devise (~> 1.4.0) 252 devise (~> 1.4.0)
219 email_spec 253 email_spec
@@ -223,12 +257,15 @@ DEPENDENCIES @@ -223,12 +257,15 @@ DEPENDENCIES
223 htmlentities (~> 4.3.0) 257 htmlentities (~> 4.3.0)
224 inherited_resources 258 inherited_resources
225 kaminari 259 kaminari
  260 + launchy
226 lighthouse-api 261 lighthouse-api
227 mongo (= 1.3.1) 262 mongo (= 1.3.1)
228 mongoid (~> 2.2.2) 263 mongoid (~> 2.2.2)
229 mongoid_rails_migrations 264 mongoid_rails_migrations
230 nokogiri 265 nokogiri
231 - octokit (= 0.6.4) 266 + oa-core
  267 + octokit
  268 + omniauth-github
232 oruen_redmine_client 269 oruen_redmine_client
233 pivotal-tracker 270 pivotal-tracker
234 rack-ssl-enforcer 271 rack-ssl-enforcer
app/controllers/users/omniauth_callbacks_controller.rb 0 → 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  2 + def github
  3 + @user = User.find_for_github_oauth(request.env["omniauth.auth"])
  4 +
  5 + if @user
  6 + flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Github"
  7 + sign_in_and_redirect @user, :event => :authentication
  8 + else
  9 + redirect_to new_user_session_path
  10 + end
  11 + end
  12 +end
app/models/user.rb
@@ -5,13 +5,14 @@ class User @@ -5,13 +5,14 @@ class User
5 5
6 devise :database_authenticatable, 6 devise :database_authenticatable,
7 :recoverable, :rememberable, :trackable, 7 :recoverable, :rememberable, :trackable,
8 - :validatable, :token_authenticatable 8 + :validatable, :token_authenticatable, :omniauthable
9 9
10 field :email 10 field :email
  11 + field :github_login
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
14 - field :time_zone, :default => "UTC" 15 + field :time_zone, :default => "UTC"
15 16
16 after_destroy :destroy_watchers 17 after_destroy :destroy_watchers
17 before_save :ensure_authentication_token 18 before_save :ensure_authentication_token
@@ -39,6 +40,16 @@ class User @@ -39,6 +40,16 @@ class User
39 apps.all.include?(app) 40 apps.all.include?(app)
40 end 41 end
41 42
  43 + def self.find_for_github_oauth(omniauth_env)
  44 + data = omniauth_env.extra.raw_info
  45 +
  46 + User.where(:github_login => data.login).first
  47 + end
  48 +
  49 + def password_required?
  50 + github_login.present? ? false : super
  51 + end
  52 +
42 protected 53 protected
43 54
44 def destroy_watchers 55 def destroy_watchers
app/views/devise/sessions/new.html.haml
1 - content_for :title, 'Sign in' 1 - content_for :title, 'Sign in'
2 - auth_key = Devise.authentication_keys.first 2 - auth_key = Devise.authentication_keys.first
3 3
  4 +- if Errbit::Config.github_authentication
  5 + = link_to "Sign in with Github", user_omniauth_authorize_path(:github)
  6 +
4 = form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| 7 = form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f|
5 .required 8 .required
6 = f.label auth_key 9 = f.label auth_key
app/views/users/_fields.html.haml
@@ -13,6 +13,10 @@ @@ -13,6 +13,10 @@
13 = f.label :email 13 = f.label :email
14 = f.text_field :email 14 = f.text_field :email
15 15
  16 +- if Errbit::Config.github_authentication
  17 + = f.label :github_login
  18 + = f.text_field :github_login
  19 +
16 .required 20 .required
17 = f.label 'Entries per page' 21 = f.label 'Entries per page'
18 = f.select :per_page, [10, 20, 30, 50, 75, 100] 22 = f.select :per_page, [10, 20, 30, 50, 75, 100]
config/config.example.yml
@@ -49,6 +49,13 @@ deployment: @@ -49,6 +49,13 @@ deployment:
49 user: deploy 49 user: deploy
50 deploy_to: /var/www/apps/errbit 50 deploy_to: /var/www/apps/errbit
51 51
  52 +# Github OAuth configuration
  53 +# If you want to use authtiaction through Github you should provide Client ID and Secret keys.
  54 +# You can register a new application here https://github.com/settings/applications
  55 +github_authentication: false
  56 +github_client_id: 'GITHUB_CLIENT_ID'
  57 +github_secret: 'GITHUB_SECRET'
  58 +
52 # Configure SMTP settings. If you are running Errbit on Heroku, 59 # Configure SMTP settings. If you are running Errbit on Heroku,
53 # sendgrid will be configured by default. 60 # sendgrid will be configured by default.
54 # ------------------------------------------------------------------------ 61 # ------------------------------------------------------------------------
config/initializers/_load_config.rb
@@ -11,6 +11,10 @@ if ENV[&#39;HEROKU&#39;] @@ -11,6 +11,10 @@ if ENV[&#39;HEROKU&#39;]
11 Errbit::Config.user_has_username = ENV['ERRBIT_USER_HAS_USERNAME'] 11 Errbit::Config.user_has_username = ENV['ERRBIT_USER_HAS_USERNAME']
12 Errbit::Config.allow_comments_with_issue_tracker = ENV['ERRBIT_ALLOW_COMMENTS_WITH_ISSUE_TRACKER'] 12 Errbit::Config.allow_comments_with_issue_tracker = ENV['ERRBIT_ALLOW_COMMENTS_WITH_ISSUE_TRACKER']
13 13
  14 + Errbit::Config.github_authentication = ENV['GITHUB_AUTHENTICATION']
  15 + Errbit::Config.github_client_id = ENV['GITHUB_CLIENT_ID']
  16 + Errbit::Config.github_secret = ENV['GITHUB_SECRET']
  17 +
14 Errbit::Config.smtp_settings = { 18 Errbit::Config.smtp_settings = {
15 :address => "smtp.sendgrid.net", 19 :address => "smtp.sendgrid.net",
16 :port => "25", 20 :port => "25",
config/initializers/devise.rb
@@ -118,6 +118,10 @@ Devise.setup do |config| @@ -118,6 +118,10 @@ Devise.setup do |config|
118 # In case of sign_out_all_scopes set to true any logout action will sign out all active scopes. 118 # In case of sign_out_all_scopes set to true any logout action will sign out all active scopes.
119 # config.sign_out_all_scopes = false 119 # config.sign_out_all_scopes = false
120 120
  121 + if Errbit::Config.github_authentication || Rails.env.test?
  122 + config.omniauth :github, Errbit::Config.github_client_id, Errbit::Config.github_secret
  123 + end
  124 +
121 # ==> Navigation configuration 125 # ==> Navigation configuration
122 # Lists the formats that should be treated as navigational. Formats like 126 # Lists the formats that should be treated as navigational. Formats like
123 # :html, should redirect to the sign in page when the user does not have 127 # :html, should redirect to the sign in page when the user does not have
config/initializers/omniauth.rb 0 → 100644
config/routes.rb
1 Errbit::Application.routes.draw do 1 Errbit::Application.routes.draw do
2 2
3 - devise_for :users 3 + devise_for :users, :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
4 4
5 # Hoptoad Notifier Routes 5 # Hoptoad Notifier Routes
6 match '/notifier_api/v2/notices' => 'notices#create' 6 match '/notifier_api/v2/notices' => 'notices#create'
public/javascripts/application.js
@@ -12,6 +12,8 @@ $(function() { @@ -12,6 +12,8 @@ $(function() {
12 12
13 toggleProblemsCheckboxes(); 13 toggleProblemsCheckboxes();
14 14
  15 + bindRequiredPasswordMarks();
  16 +
15 $('#watcher_name').live("click", function() { 17 $('#watcher_name').live("click", function() {
16 $(this).closest('form').find('.show').removeClass('show'); 18 $(this).closest('form').find('.show').removeClass('show');
17 $('#app_watchers_attributes_0_user_id').addClass('show'); 19 $('#app_watchers_attributes_0_user_id').addClass('show');
@@ -94,5 +96,23 @@ $(function() { @@ -94,5 +96,23 @@ $(function() {
94 }); 96 });
95 } 97 }
96 98
  99 + function bindRequiredPasswordMarks() {
  100 + $('#user_github_login').keyup(function(event) {
  101 + toggleRequiredPasswordMarks(this)
  102 + });
  103 + }
  104 +
  105 + function toggleRequiredPasswordMarks(input) {
  106 + if($(input).val() == "") {
  107 + $('#user_password').parent().attr('class', 'required')
  108 + $('#user_password_confirmation').parent().attr('class', 'required')
  109 + } else {
  110 + $('#user_password').parent().attr('class', '')
  111 + $('#user_password_confirmation').parent().attr('class', '')
  112 + }
  113 + }
  114 +
  115 + toggleRequiredPasswordMarks();
  116 +
97 init(); 117 init();
98 }); 118 });
spec/acceptance/acceptance_helper.rb 0 → 100644
@@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
  1 +require 'spec_helper'
  2 +require 'capybara/rspec'
  3 +
  4 +OmniAuth.config.test_mode = true
  5 +
  6 +RSpec.configure do |config|
  7 + config.before(:each) do
  8 + OmniAuth.config.mock_auth[:github] = Hashie::Mash.new(
  9 + 'provider' => 'github',
  10 + 'uid' => '1763',
  11 + 'extra' => {
  12 + 'raw_info' => {
  13 + 'login' => 'nashby'
  14 + }
  15 + }
  16 + )
  17 + end
  18 +end
spec/acceptance/login_spec.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +require 'acceptance/acceptance_helper'
  2 +
  3 +feature 'Log in' do
  4 + background do
  5 + Errbit::Config.stub(:github_authentication) { true }
  6 + Fabricate(:user, :github_login => 'nashby')
  7 + end
  8 +
  9 + scenario 'log in via Github' do
  10 + visit '/'
  11 + click_link 'Sign in with Github'
  12 + page.should have_content 'Successfully authorized from Github account'
  13 + end
  14 +end
spec/controllers/users_controller_spec.rb
@@ -78,6 +78,11 @@ describe UsersController do @@ -78,6 +78,11 @@ describe UsersController do
78 put :update, :id => @user.to_param, :user => {:time_zone => "Warsaw"} 78 put :update, :id => @user.to_param, :user => {:time_zone => "Warsaw"}
79 @user.reload.time_zone.should == "Warsaw" 79 @user.reload.time_zone.should == "Warsaw"
80 end 80 end
  81 +
  82 + it "should be able to set github_login option" do
  83 + put :update, :id => @user.to_param, :user => {:github_login => "awesome_name"}
  84 + @user.reload.github_login.should == "awesome_name"
  85 + end
81 end 86 end
82 87
83 context "when the update is unsuccessful" do 88 context "when the update is unsuccessful" do
@@ -226,7 +231,6 @@ describe UsersController do @@ -226,7 +231,6 @@ describe UsersController do
226 request.flash[:success].should include('no longer part of your team') 231 request.flash[:success].should include('no longer part of your team')
227 end 232 end
228 end 233 end
229 -  
230 end 234 end
231 -end  
232 235
  236 +end
spec/models/user_spec.rb
@@ -8,6 +8,26 @@ describe User do @@ -8,6 +8,26 @@ describe User do
8 user.should_not be_valid 8 user.should_not be_valid
9 user.errors[:name].should include("can't be blank") 9 user.errors[:name].should include("can't be blank")
10 end 10 end
  11 +
  12 + it 'requires password without github login' do
  13 + user = Fabricate.build(:user, :password => nil)
  14 + user.should_not be_valid
  15 + user.errors[:password].should include("can't be blank")
  16 + end
  17 +
  18 + it "doesn't require password with github login" do
  19 + user = Fabricate.build(:user, :password => nil, :github_login => 'nashby')
  20 + user.should be_valid
  21 + end
  22 + end
  23 +
  24 + describe '.find_for_github_oauth' do
  25 + let(:auth_hash) { Hashie::Mash.new(:provider => 'github', :extra => { :raw_info => { :login => 'nashby' } }) }
  26 +
  27 + it 'finds user by github login' do
  28 + user = Fabricate(:user, :github_login => 'nashby')
  29 + User.find_for_github_oauth(auth_hash).should == user
  30 + end
11 end 31 end
12 32
13 context 'Watchers' do 33 context 'Watchers' do
spec/spec_helper.rb
@@ -17,7 +17,6 @@ end @@ -17,7 +17,6 @@ end
17 RSpec.configure do |config| 17 RSpec.configure do |config|
18 config.mock_with :rspec 18 config.mock_with :rspec
19 config.include Devise::TestHelpers, :type => :controller 19 config.include Devise::TestHelpers, :type => :controller
20 -  
21 config.filter_run :focused => true 20 config.filter_run :focused => true
22 config.run_all_when_everything_filtered = true 21 config.run_all_when_everything_filtered = true
23 config.alias_example_to :fit, :focused => true 22 config.alias_example_to :fit, :focused => true