Commit 0d4313dfd5d954c5b8cdfefe63ad7ab0166607b2

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

add github authentication

Gemfile
... ... @@ -7,13 +7,15 @@ gem 'mongoid', '~> 2.2.2'
7 7 gem 'haml'
8 8 gem 'htmlentities', "~> 4.3.0"
9 9 gem 'devise', '~> 1.4.0'
  10 +gem 'omniauth-github'
  11 +gem 'oa-core'
10 12 gem 'lighthouse-api'
11 13 gem 'oruen_redmine_client', :require => 'redmine_client'
12 14 gem 'mongoid_rails_migrations'
13 15 gem 'useragent', '~> 0.3.1'
14 16 gem 'pivotal-tracker'
15 17 gem 'ruby-fogbugz', :require => 'fogbugz'
16   -gem 'octokit', '0.6.4'
  18 +gem 'octokit'
17 19 gem 'inherited_resources'
18 20 gem 'SystemTimer', :platform => :ruby_18
19 21 gem 'hoptoad_notifier', "~> 2.4"
... ... @@ -41,6 +43,8 @@ group :development, :test do
41 43 end
42 44  
43 45 group :test do
  46 + gem 'capybara'
  47 + gem 'launchy'
44 48 gem 'rspec', '~> 2.6'
45 49 gem 'database_cleaner', '~> 0.6.0'
46 50 gem 'email_spec'
... ...
Gemfile.lock
... ... @@ -33,13 +33,22 @@ GEM
33 33 activemodel (= 3.0.10)
34 34 activesupport (= 3.0.10)
35 35 activesupport (3.0.10)
36   - addressable (2.2.6)
  36 + addressable (2.2.7)
37 37 archive-tar-minitar (0.5.2)
38 38 arel (2.0.10)
39 39 bcrypt-ruby (3.0.1)
40 40 bson (1.3.1)
41 41 bson_ext (1.3.1)
42 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 52 columnize (0.3.4)
44 53 crack (0.3.1)
45 54 css_parser (1.2.5)
... ... @@ -60,13 +69,14 @@ GEM
60 69 addressable (~> 2.2.6)
61 70 multipart-post (~> 1.1.0)
62 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 75 haml (3.1.3)
66 76 happymapper (0.4.0)
67 77 libxml-ruby (~> 2.0)
68 78 has_scope (0.5.1)
69   - hashie (1.0.0)
  79 + hashie (1.2.0)
70 80 hoptoad_notifier (2.4.11)
71 81 activesupport
72 82 builder
... ... @@ -78,6 +88,8 @@ GEM
78 88 kaminari (0.12.4)
79 89 rails (>= 3.0.0)
80 90 kgio (2.6.0)
  91 + launchy (2.1.0)
  92 + addressable (~> 2.2.6)
81 93 libxml-ruby (2.2.2)
82 94 lighthouse-api (2.0)
83 95 activeresource (>= 3.0.0)
... ... @@ -103,15 +115,28 @@ GEM
103 115 bundler (>= 1.0.0)
104 116 rails (>= 3.0.0)
105 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 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 140 orm_adapter (0.0.5)
116 141 oruen_redmine_client (0.0.1)
117 142 activeresource (>= 2.3.0)
... ... @@ -128,7 +153,7 @@ GEM
128 153 premailer (1.7.3)
129 154 css_parser (>= 1.1.9)
130 155 htmlentities (>= 4.0.0)
131   - rack (1.2.4)
  156 + rack (1.2.5)
132 157 rack-mount (0.6.14)
133 158 rack (>= 1.0.0)
134 159 rack-ssl-enforcer (0.2.4)
... ... @@ -187,6 +212,12 @@ GEM
187 212 typhoeus
188 213 ruby_core_source (0.1.5)
189 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 221 thor (0.14.6)
191 222 treetop (1.4.10)
192 223 polyglot
... ... @@ -205,6 +236,8 @@ GEM
205 236 webmock (1.7.6)
206 237 addressable (~> 2.2, > 2.2.5)
207 238 crack (>= 0.1.7)
  239 + xpath (0.1.4)
  240 + nokogiri (~> 1.3)
208 241  
209 242 PLATFORMS
210 243 ruby
... ... @@ -214,6 +247,7 @@ DEPENDENCIES
214 247 actionmailer_inline_css (~> 1.3.0)
215 248 bson (= 1.3.1)
216 249 bson_ext (= 1.3.1)
  250 + capybara
217 251 database_cleaner (~> 0.6.0)
218 252 devise (~> 1.4.0)
219 253 email_spec
... ... @@ -223,12 +257,15 @@ DEPENDENCIES
223 257 htmlentities (~> 4.3.0)
224 258 inherited_resources
225 259 kaminari
  260 + launchy
226 261 lighthouse-api
227 262 mongo (= 1.3.1)
228 263 mongoid (~> 2.2.2)
229 264 mongoid_rails_migrations
230 265 nokogiri
231   - octokit (= 0.6.4)
  266 + oa-core
  267 + octokit
  268 + omniauth-github
232 269 oruen_redmine_client
233 270 pivotal-tracker
234 271 rack-ssl-enforcer
... ...
app/controllers/users/omniauth_callbacks_controller.rb 0 → 100644
... ... @@ -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 5  
6 6 devise :database_authenticatable,
7 7 :recoverable, :rememberable, :trackable,
8   - :validatable, :token_authenticatable
  8 + :validatable, :token_authenticatable, :omniauthable
9 9  
10 10 field :email
  11 + field :github_login
11 12 field :name
12 13 field :admin, :type => Boolean, :default => false
13 14 field :per_page, :type => Fixnum, :default => PER_PAGE
14   - field :time_zone, :default => "UTC"
  15 + field :time_zone, :default => "UTC"
15 16  
16 17 after_destroy :destroy_watchers
17 18 before_save :ensure_authentication_token
... ... @@ -39,6 +40,16 @@ class User
39 40 apps.all.include?(app)
40 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 53 protected
43 54  
44 55 def destroy_watchers
... ...
app/views/devise/sessions/new.html.haml
1 1 - content_for :title, 'Sign in'
2 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 7 = form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f|
5 8 .required
6 9 = f.label auth_key
... ...
app/views/users/_fields.html.haml
... ... @@ -13,6 +13,10 @@
13 13 = f.label :email
14 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 20 .required
17 21 = f.label 'Entries per page'
18 22 = f.select :per_page, [10, 20, 30, 50, 75, 100]
... ...
config/config.example.yml
... ... @@ -49,6 +49,13 @@ deployment:
49 49 user: deploy
50 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 59 # Configure SMTP settings. If you are running Errbit on Heroku,
53 60 # sendgrid will be configured by default.
54 61 # ------------------------------------------------------------------------
... ...
config/initializers/_load_config.rb
... ... @@ -11,6 +11,10 @@ if ENV[&#39;HEROKU&#39;]
11 11 Errbit::Config.user_has_username = ENV['ERRBIT_USER_HAS_USERNAME']
12 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 18 Errbit::Config.smtp_settings = {
15 19 :address => "smtp.sendgrid.net",
16 20 :port => "25",
... ...
config/initializers/devise.rb
... ... @@ -118,6 +118,10 @@ Devise.setup do |config|
118 118 # In case of sign_out_all_scopes set to true any logout action will sign out all active scopes.
119 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 125 # ==> Navigation configuration
122 126 # Lists the formats that should be treated as navigational. Formats like
123 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 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 5 # Hoptoad Notifier Routes
6 6 match '/notifier_api/v2/notices' => 'notices#create'
... ...
public/javascripts/application.js
... ... @@ -12,6 +12,8 @@ $(function() {
12 12  
13 13 toggleProblemsCheckboxes();
14 14  
  15 + bindRequiredPasswordMarks();
  16 +
15 17 $('#watcher_name').live("click", function() {
16 18 $(this).closest('form').find('.show').removeClass('show');
17 19 $('#app_watchers_attributes_0_user_id').addClass('show');
... ... @@ -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 117 init();
98 118 });
... ...
spec/acceptance/acceptance_helper.rb 0 → 100644
... ... @@ -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 @@
  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 78 put :update, :id => @user.to_param, :user => {:time_zone => "Warsaw"}
79 79 @user.reload.time_zone.should == "Warsaw"
80 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 86 end
82 87  
83 88 context "when the update is unsuccessful" do
... ... @@ -226,7 +231,6 @@ describe UsersController do
226 231 request.flash[:success].should include('no longer part of your team')
227 232 end
228 233 end
229   -
230 234 end
231   -end
232 235  
  236 +end
... ...
spec/models/user_spec.rb
... ... @@ -8,6 +8,26 @@ describe User do
8 8 user.should_not be_valid
9 9 user.errors[:name].should include("can't be blank")
10 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 31 end
12 32  
13 33 context 'Watchers' do
... ...
spec/spec_helper.rb
... ... @@ -17,7 +17,6 @@ end
17 17 RSpec.configure do |config|
18 18 config.mock_with :rspec
19 19 config.include Devise::TestHelpers, :type => :controller
20   -
21 20 config.filter_run :focused => true
22 21 config.run_all_when_everything_filtered = true
23 22 config.alias_example_to :fit, :focused => true
... ...