Commit 0d4313dfd5d954c5b8cdfefe63ad7ab0166607b2
1 parent
ffd87fdd
Exists in
master
and in
1 other branch
add github authentication
Showing
17 changed files
with
182 additions
and
21 deletions
Show diff stats
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 | ... | ... |
| ... | ... | @@ -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['HEROKU'] |
| 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/routes.rb
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 | }); | ... | ... |
| ... | ... | @@ -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 | ... | ... |
| ... | ... | @@ -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 | ... | ... |