warden-github 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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