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 +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +26 -0
- data/example/simple_app.rb +18 -0
- data/lib/warden/github.rb +2 -0
- data/lib/warden/github/sso.rb +30 -0
- data/lib/warden/github/strategy.rb +5 -1
- data/lib/warden/github/user.rb +24 -5
- data/lib/warden/github/version.rb +1 -1
- data/spec/integration/oauth_spec.rb +17 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/unit/sso_spec.rb +42 -0
- data/spec/unit/user_spec.rb +33 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d16b8a0ce775cffede49bad96b407ea7bb03814
|
4
|
+
data.tar.gz: ad6b95da408a4163251445886b2638c2061b48f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6cd9e974843071be78465b45b44c3332d3ad9ec44069d91768868d85141f9b94415543ee026c88c097e5c27aca85042dd1838ed414085221a9545b47678fff3
|
7
|
+
data.tar.gz: a06e7b94ddafc1eafa30cfb5257e17cf7f8c3077b4660def2cd60f08043aa113365963d00db5eed1db976518aff381cc7fc8a26fbdcf8e1718c2d191f96c6c92
|
data/CHANGELOG.md
CHANGED
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)
|
data/example/simple_app.rb
CHANGED
@@ -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>
|
data/lib/warden/github.rb
CHANGED
@@ -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
|
data/lib/warden/github/user.rb
CHANGED
@@ -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
|
@@ -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
|
data/spec/spec_helper.rb
CHANGED
@@ -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
|
data/spec/unit/user_spec.rb
CHANGED
@@ -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.
|
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-
|
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
|