warden-github 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 83a3ef77c9c968b979e04291579d63e3d4a9b846
4
- data.tar.gz: 1d26de519180cb5f2bfad3b1a2e1b53a17f721f1
3
+ metadata.gz: 1d16b8a0ce775cffede49bad96b407ea7bb03814
4
+ data.tar.gz: ad6b95da408a4163251445886b2638c2061b48f2
5
5
  SHA512:
6
- metadata.gz: e4f66055aab939e314e2ba914dc6c976a4e6877ca4cbbee9b1d91f866364564d61ea12bf0f58942800310520d5e6e236fa5fee7f1d908111e980f80d385f672e
7
- data.tar.gz: f220011a871bbc64cbdf0280277b37a5c07a0b06e92637cef245534a861ec583599dea5d8bd38d5aefe635719ad12200d72724e4779c4be0d88e65ff4a7da29a
6
+ metadata.gz: f6cd9e974843071be78465b45b44c3332d3ad9ec44069d91768868d85141f9b94415543ee026c88c097e5c27aca85042dd1838ed414085221a9545b47678fff3
7
+ data.tar.gz: a06e7b94ddafc1eafa30cfb5257e17cf7f8c3077b4660def2cd60f08043aa113365963d00db5eed1db976518aff381cc7fc8a26fbdcf8e1718c2d191f96c6c92
@@ -1,3 +1,7 @@
1
+ v1.2.0 2015/02/18
2
+
3
+ * Implement single sign out for cookie sessions of GitHub properties
4
+
1
5
  v1.0.3 2014/12/13
2
6
 
3
7
  * Reintroduce membership caching to reduce API hits for validating team membership.
data/README.md CHANGED
@@ -143,6 +143,32 @@ If you're looking for an easy way to integrate this into a Sinatra or Rails appl
143
143
  - [sinatra_auth_github](https://github.com/atmos/sinatra_auth_github)
144
144
  - [warden-github-rails](https://github.com/fphilipe/warden-github-rails)
145
145
 
146
+ ## Single Sign Out
147
+
148
+ OAuth applications owned by the GitHub organization are sent an extra browser parameter to ensure that the user remains logged in to github.com. Taking advantage of this is provided by a small module you include into your controller and a before filter. Your `ApplicationController` should resemble something like this.
149
+
150
+
151
+ ```ruby
152
+ class ApplicationController < ActionController::Base
153
+ include Warden::GitHub::SSO
154
+
155
+ protect_from_forgery with: :exception
156
+
157
+ before_filter :verify_logged_in_user
158
+
159
+ private
160
+
161
+ def verify_logged_in_user
162
+ unless github_user && warden_github_sso_session_valid?(github_user, 120)
163
+ request.env['warden'].logout
164
+ request.env['warden'].authenticate!
165
+ end
166
+ end
167
+ end
168
+ ```
169
+
170
+ You can also see single sign out in action in the example app.
171
+
146
172
  ## Additional Information
147
173
 
148
174
  - [warden](https://github.com/hassox/warden)
@@ -2,6 +2,8 @@ require File.expand_path('../setup', __FILE__)
2
2
 
3
3
  module Example
4
4
  class SimpleApp < BaseApp
5
+ include Warden::GitHub::SSO
6
+
5
7
  enable :inline_templates
6
8
 
7
9
  GITHUB_CONFIG = {
@@ -18,16 +20,24 @@ module Example
18
20
  config.serialize_into_session { |user| Warden::GitHub::Verifier.dump(user) }
19
21
  end
20
22
 
23
+ def verify_browser_session
24
+ if env['warden'].user && !warden_github_sso_session_valid?(env['warden'].user, 10)
25
+ env['warden'].logout
26
+ end
27
+ end
28
+
21
29
  get '/' do
22
30
  erb :index
23
31
  end
24
32
 
25
33
  get '/profile' do
34
+ verify_browser_session
26
35
  env['warden'].authenticate!
27
36
  erb :profile
28
37
  end
29
38
 
30
39
  get '/login' do
40
+ verify_browser_session
31
41
  env['warden'].authenticate!
32
42
  redirect '/'
33
43
  end
@@ -86,4 +96,12 @@ __END__
86
96
  <dd><%= env['warden'].user.team_member?(632) %></dd>
87
97
  <dt>GitHub Site Admin:</dt>
88
98
  <dd><%= env['warden'].user.site_admin? %></dd>
99
+ <% if env['warden'].user.using_single_sign_out? %>
100
+ <dt>GitHub Browser Session ID</dt>
101
+ <dd><%= env['warden'].user.browser_session_id %></dd>
102
+ <dt>GitHub Browser Session Valid</dt>
103
+ <dd><%= warden_github_sso_session_valid?(env['warden'].user, 10) %></dd>
104
+ <dt>GitHub Browser Session Verified At</dt>
105
+ <dd><%= Time.at(warden_github_sso_session_verified_at) %></dd>
106
+ <% end %>
89
107
  </dl>
@@ -1,6 +1,8 @@
1
1
  require 'json'
2
2
  require 'warden'
3
+ require 'octokit'
3
4
 
5
+ require 'warden/github/sso'
4
6
  require 'warden/github/user'
5
7
  require 'warden/github/oauth'
6
8
  require 'warden/github/version'
@@ -0,0 +1,30 @@
1
+ module Warden
2
+ module GitHub
3
+ module SSO
4
+ def warden_github_sso_session_valid?(user, expiry_in_seconds = 30)
5
+ return true if defined?(::Rails) && ::Rails.env.test?
6
+ if warden_github_sso_session_needs_reverification?(user, expiry_in_seconds)
7
+ if user.browser_session_valid?(expiry_in_seconds)
8
+ warden_github_sso_session_reverify!
9
+ return true
10
+ end
11
+ return false
12
+ end
13
+ true
14
+ end
15
+
16
+ def warden_github_sso_session_verified_at
17
+ session[:warden_github_sso_session_verified_at] || Time.now.utc.to_i - 86400
18
+ end
19
+
20
+ def warden_github_sso_session_reverify!
21
+ session[:warden_github_sso_session_verified_at] = Time.now.utc.to_i
22
+ end
23
+
24
+ def warden_github_sso_session_needs_reverification?(user, expiry_in_seconds)
25
+ user.using_single_sign_out? &&
26
+ (warden_github_sso_session_verified_at <= (Time.now.utc.to_i - expiry_in_seconds))
27
+ end
28
+ end
29
+ end
30
+ end
@@ -70,6 +70,10 @@ module Warden
70
70
  elsif (error = params['error']) && !error.empty?
71
71
  abort_flow!(error.gsub(/_/, ' '))
72
72
  end
73
+
74
+ if params['browser_session_id']
75
+ custom_session['browser_session_id'] = params['browser_session_id']
76
+ end
73
77
  end
74
78
 
75
79
  def custom_session
@@ -77,7 +81,7 @@ module Warden
77
81
  end
78
82
 
79
83
  def load_user
80
- User.load(oauth.access_token)
84
+ User.load(oauth.access_token, custom_session['browser_session_id'])
81
85
  rescue OAuth::BadVerificationCode => e
82
86
  abort_flow!(e.message)
83
87
  end
@@ -1,11 +1,9 @@
1
- require 'octokit'
2
-
3
1
  module Warden
4
2
  module GitHub
5
- class User < Struct.new(:attribs, :token)
3
+ class User < Struct.new(:attribs, :token, :browser_session_id)
6
4
  ATTRIBUTES = %w[id login name gravatar_id avatar_url email company site_admin].freeze
7
5
 
8
- def self.load(access_token)
6
+ def self.load(access_token, browser_session_id = nil)
9
7
  api = Octokit::Client.new(:access_token => access_token)
10
8
  data = { }
11
9
 
@@ -13,7 +11,7 @@ module Warden
13
11
  data[k.to_s] = v if ATTRIBUTES.include?(k.to_s)
14
12
  end
15
13
 
16
- new(data, access_token)
14
+ new(data, access_token, browser_session_id)
17
15
  end
18
16
 
19
17
  def marshal_dump
@@ -71,6 +69,27 @@ module Warden
71
69
  !!site_admin
72
70
  end
73
71
 
72
+ # Identify if the browser session is still valid
73
+ #
74
+ # Returns: true if the browser session is still active or the GitHub API is unavailable
75
+ def browser_session_valid?(since = 120)
76
+ return true unless using_single_sign_out?
77
+ client = api
78
+ client.get("/user/sessions/active", :browser_session_id => browser_session_id)
79
+ client.last_response.status == 204
80
+ rescue Octokit::ServerError # GitHub API unavailable
81
+ true
82
+ rescue Octokit::ClientError => e # GitHub API failed
83
+ false
84
+ end
85
+
86
+ # Identify if the user is on a GitHub SSO property
87
+ #
88
+ # Returns: true if a browser_session_id is present, false otherwise.
89
+ def using_single_sign_out?
90
+ !(browser_session_id.nil? || browser_session_id == "")
91
+ end
92
+
74
93
  # Access the GitHub API from Octokit
75
94
  #
76
95
  # Octokit is a robust client library for the GitHub API
@@ -1,5 +1,5 @@
1
1
  module Warden
2
2
  module GitHub
3
- VERSION = "1.1.1"
3
+ VERSION = "1.2.0"
4
4
  end
5
5
  end
@@ -100,6 +100,23 @@ describe 'OAuth' do
100
100
  authenticated_uri.query.should eq 'foo=bar'
101
101
  end
102
102
  end
103
+
104
+ context 'with GitHub SSO and code was exchanged for an access token' do
105
+ it 'redirects back to the original path' do
106
+ stub_code_for_token_exchange
107
+ stub_user_retrieval
108
+
109
+ unauthenticated_response = get '/profile?foo=bar'
110
+ github_uri = redirect_uri(unauthenticated_response)
111
+ state = github_uri.query_values['state']
112
+
113
+ callback_response = get "/profile?code=#{code}&state=#{state}&browser_session_id=abcdefghijklmnop"
114
+ authenticated_uri = redirect_uri(callback_response)
115
+
116
+ authenticated_uri.path.should eq '/profile'
117
+ authenticated_uri.query.should eq 'foo=bar'
118
+ end
119
+ end
103
120
  end
104
121
 
105
122
  context 'when not inside OAuth flow' do
@@ -16,4 +16,9 @@ RSpec.configure do |config|
16
16
  def app
17
17
  Example.app
18
18
  end
19
+
20
+ def stub_user_session_request
21
+ stub_request(:get, "https://api.github.com/user/sessions/active?browser_session_id=abcdefghijklmnop").
22
+ with(:headers => {'Accept'=>'application/vnd.github.v3+json', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'token the_token', 'Content-Type'=>'application/json', 'User-Agent'=>"Octokit Ruby Gem #{Octokit::VERSION}"})
23
+ end
19
24
  end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ class FakeController
4
+ def session
5
+ @session ||= {}
6
+ end
7
+ include Warden::GitHub::SSO
8
+ end
9
+
10
+ describe Warden::GitHub::SSO do
11
+ let(:default_attrs) do
12
+ { 'login' => 'john',
13
+ 'name' => 'John Doe',
14
+ 'gravatar_id' => '38581cb351a52002548f40f8066cfecg',
15
+ 'avatar_url' => 'http://example.com/avatar.jpg',
16
+ 'email' => 'john@doe.com',
17
+ 'company' => 'Doe, Inc.' }
18
+ end
19
+
20
+ let(:user) do
21
+ Warden::GitHub::User.new(default_attrs, "the_token", "abcdefghijklmnop")
22
+ end
23
+
24
+ subject do
25
+ FakeController.new
26
+ end
27
+
28
+ describe "warden_github_sso_session_valid?" do
29
+ it "identifies when browsers need to be reverified" do
30
+ subject.session[:warden_github_sso_session_verified_at] = Time.now.utc.to_i - 10
31
+ subject.should be_warden_github_sso_session_valid(user)
32
+
33
+ subject.session[:warden_github_sso_session_verified_at] = Time.now.utc.to_i - 300
34
+ stub_user_session_request.to_return(:status => 204, :body => "", :headers => {})
35
+ subject.should be_warden_github_sso_session_valid(user)
36
+
37
+ subject.session[:warden_github_sso_session_verified_at] = Time.now.utc.to_i - 300
38
+ stub_user_session_request.to_return(:status => 404, :body => "", :headers => {})
39
+ subject.should_not be_warden_github_sso_session_valid(user)
40
+ end
41
+ end
42
+ end
@@ -15,6 +15,10 @@ describe Warden::GitHub::User do
15
15
  described_class.new(default_attrs, token)
16
16
  end
17
17
 
18
+ let(:sso_user) do
19
+ described_class.new(default_attrs, token, "abcdefghijklmnop")
20
+ end
21
+
18
22
  describe '#token' do
19
23
  it 'returns the token' do
20
24
  user.token.should eq token
@@ -108,4 +112,33 @@ describe Warden::GitHub::User do
108
112
  it 'marshals correctly' do
109
113
  Marshal.load(Marshal.dump(user)).should eq user
110
114
  end
115
+
116
+ describe 'single sign out' do
117
+ it "knows if the user is using single sign out" do
118
+ user.should_not be_using_single_sign_out
119
+ sso_user.should be_using_single_sign_out
120
+ end
121
+
122
+ context "browser reverification" do
123
+ it "handles success" do
124
+ stub_user_session_request.to_return(:status => 204, :body => "", :headers => {})
125
+ sso_user.should be_browser_session_valid
126
+ end
127
+
128
+ it "handles failure" do
129
+ stub_user_session_request.to_return(:status => 404, :body => "", :headers => {})
130
+ sso_user.should_not be_browser_session_valid
131
+ end
132
+
133
+ it "handles GitHub being unavailable" do
134
+ stub_user_session_request.to_raise(Octokit::ServerError.new)
135
+ sso_user.should be_browser_session_valid
136
+ end
137
+
138
+ it "handles authentication failures" do
139
+ stub_user_session_request.to_return(:status => 403, :body => "", :headers => {})
140
+ sso_user.should_not be_browser_session_valid
141
+ end
142
+ end
143
+ end
111
144
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: warden-github
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Corey Donohoe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-15 00:00:00.000000000 Z
11
+ date: 2015-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: warden
@@ -216,6 +216,7 @@ files:
216
216
  - lib/warden/github/hook.rb
217
217
  - lib/warden/github/membership_cache.rb
218
218
  - lib/warden/github/oauth.rb
219
+ - lib/warden/github/sso.rb
219
220
  - lib/warden/github/strategy.rb
220
221
  - lib/warden/github/user.rb
221
222
  - lib/warden/github/verifier.rb
@@ -226,6 +227,7 @@ files:
226
227
  - spec/unit/config_spec.rb
227
228
  - spec/unit/membership_cache_spec.rb
228
229
  - spec/unit/oauth_spec.rb
230
+ - spec/unit/sso_spec.rb
229
231
  - spec/unit/user_spec.rb
230
232
  - warden-github.gemspec
231
233
  homepage: http://github.com/atmos/warden-github
@@ -259,4 +261,5 @@ test_files:
259
261
  - spec/unit/config_spec.rb
260
262
  - spec/unit/membership_cache_spec.rb
261
263
  - spec/unit/oauth_spec.rb
264
+ - spec/unit/sso_spec.rb
262
265
  - spec/unit/user_spec.rb