warden-github 0.13.0 → 0.13.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +134 -11
- data/config.ru +6 -1
- data/example/multi_scope_app.rb +84 -0
- data/example/setup.rb +25 -0
- data/example/simple_app.rb +85 -0
- data/lib/warden/github/config.rb +146 -0
- data/lib/warden/github/membership_cache.rb +48 -0
- data/lib/warden/github/strategy.rb +4 -25
- data/lib/warden/github/user.rb +25 -12
- data/lib/warden/github/version.rb +1 -1
- data/lib/warden/github.rb +3 -2
- data/spec/spec_helper.rb +1 -13
- data/spec/unit/config_spec.rb +176 -0
- data/spec/unit/membership_cache_spec.rb +62 -0
- data/spec/unit/user_spec.rb +11 -11
- data/warden-github.gemspec +1 -2
- metadata +45 -53
- data/example/app.rb +0 -98
data/README.md
CHANGED
@@ -1,18 +1,141 @@
|
|
1
|
-
warden-github
|
2
|
-
=============
|
1
|
+
# warden-github
|
3
2
|
|
4
|
-
A [warden](/hassox/warden) strategy that provides
|
3
|
+
A [warden](https://github.com/hassox/warden) strategy that provides OAuth authentication to GitHub.
|
5
4
|
|
6
|
-
|
5
|
+
## The Extension in Action
|
7
6
|
|
8
|
-
|
7
|
+
To play with the extension, follow these steps:
|
9
8
|
|
10
|
-
|
11
|
-
|
9
|
+
1. Check out a copy of the source.
|
10
|
+
2. [Create an application on GitHub](https://github.com/settings/applications/new) and set the callback URL to `http://localhost:9292`
|
11
|
+
3. Run the following command with the client id and client secret obtained from the previous step:
|
12
12
|
|
13
|
-
|
13
|
+
GITHUB_CLIENT_ID="<from GH>" GITHUB_CLIENT_SECRET="<from GH>" bundle exec rackup
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
This will run the example app [example/simple_app.rb](example/simple_app.rb).
|
16
|
+
|
17
|
+
If you wish to see multiple user scopes in action, run the above command with an additional variable:
|
18
|
+
|
19
|
+
MULTI_SCOPE_APP=1 GITHUB_CLIENT_ID="<from GH>" GITHUB_CLIENT_SECRET="<from GH>" bundle exec rackup
|
20
|
+
|
21
|
+
This will run the example app [example/multi_scope_app.rb](example/multi_scope_app.rb).
|
22
|
+
|
23
|
+
4. Point your browser at [http://localhost:9292/](http://localhost:9292) and enjoy!
|
24
|
+
|
25
|
+
## Configuration
|
26
|
+
|
27
|
+
In order to use this strategy, simply tell warden about it.
|
28
|
+
This is done by using `Warden::Manager` as a rack middleware and passing a config block to it.
|
29
|
+
Read more about warden setup at the [warden wiki](https://github.com/hassox/warden/wiki/Setup).
|
30
|
+
|
31
|
+
For simple usage without customization, simply specify it as the default strategy.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
use Warden::Manager do |config|
|
35
|
+
config.failure_app = BadAuthentication
|
36
|
+
config.default_strategies :github
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
In order to pass custom configurations, you need to configure a warden scope.
|
41
|
+
Note that the default warden scope (i.e. when not specifying any explicit scope) is `:default`.
|
42
|
+
|
43
|
+
Here's an example that specifies configs for the default scope and a custom admin scope.
|
44
|
+
Using multiple scopes allows you to have different user types.
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
use Warden::Manager do |config|
|
48
|
+
config.failure_app = BadAuthentication
|
49
|
+
config.default_strategies :github
|
50
|
+
|
51
|
+
config.scope_defaults :default, :config => { :scope => 'user:email' }
|
52
|
+
config.scope_defaults :admin, :config => { :client_id => 'foobar',
|
53
|
+
:client_secret => 'barfoo',
|
54
|
+
:scope => 'user,repo',
|
55
|
+
:redirect_uri => '/admin/oauth/callback' }
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
### Parameters
|
60
|
+
|
61
|
+
The config parameters and their defaults are listed below.
|
62
|
+
Please refer to the [GitHub OAuth documentation](http://developer.github.com/v3/oauth/) for an explanation of their meaning.
|
63
|
+
|
64
|
+
- **client_id:** Defaults to `ENV['GITHUB_CLIENT_ID']` and raises if not present.
|
65
|
+
- **client_secret:** Defaults to `ENV['GITHUB_CLIENT_SECRET']` and raises if not present.
|
66
|
+
- **scope:** Defaults to `nil`.
|
67
|
+
- **redirect_uri:** Defaults to the current path.
|
68
|
+
Note that paths will be expanded to a valid URL using the request url's host.
|
69
|
+
|
70
|
+
### Using with GitHub Enterprise
|
71
|
+
|
72
|
+
GitHub API communication is done entirely through the [octokit gem](https://github.com/pengwynn/octokit).
|
73
|
+
For the OAuth process (which uses another endpoint than the API), the web endpoint is read from octokit.
|
74
|
+
In order to configure octokit for GitHub Enterprise you can either define the two environment variables `OCTOKIT_API_ENDPOINT` and `OCTOKIT_WEB_ENDPOINT`, or configure the `Octokit` module as specified in their [README](https://github.com/pengwynn/octokit#using-with-github-enterprise).
|
75
|
+
|
76
|
+
### JSON Dependency
|
77
|
+
|
78
|
+
This gem and its dependencies do not explicitly depend on any JSON library.
|
79
|
+
If you're on ruby 1.8.7 you'll have to include one explicitly.
|
80
|
+
ruby 1.9 comes with a json library that will be used if no other is specified.
|
81
|
+
|
82
|
+
## Usage
|
83
|
+
|
84
|
+
Some warden methods that you will need:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
env['warden'].authenticate! # => Uses the configs from the default scope.
|
88
|
+
env['warden'].authenticate!(:scope => :admin) # => Uses the configs from the admin scope.
|
89
|
+
|
90
|
+
# Analogous to previous lines, but does not halt if authentication does not succeed.
|
91
|
+
env['warden'].authenticate
|
92
|
+
env['warden'].authenticate(:scope => :admin)
|
93
|
+
|
94
|
+
env['warden'].authenticated? # => Checks whether the default scope is logged in.
|
95
|
+
env['warden'].authenticated?(:admin) # => Checks whether the admin scope is logged in.
|
96
|
+
|
97
|
+
env['warden'].user # => The user for the default scope.
|
98
|
+
env['warden'].user(:admin) # => The user for the admin scope.
|
99
|
+
|
100
|
+
env['warden'].session # => Namespaced session accessor for the default scope.
|
101
|
+
env['warden'].session(:admin) # => Namespaced session accessor for the admin scope.
|
102
|
+
|
103
|
+
env['warden'].logout # => Logs out all scopes.
|
104
|
+
env['warden'].logout(:default) # => Logs out the default scope.
|
105
|
+
env['warden'].logout(:admin) # => Logs out the admin scope.
|
106
|
+
```
|
107
|
+
|
108
|
+
For further documentation, refer to the [warden wiki](https://github.com/hassox/warden/wiki).
|
109
|
+
|
110
|
+
The user object (`Warden::GitHub::User`) responds to the following methods:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
user = env['warden'].user
|
114
|
+
|
115
|
+
user.id # => The GitHub user id.
|
116
|
+
user.login # => The GitHub username.
|
117
|
+
user.name
|
118
|
+
user.gravatar_id # => The md5 email hash to construct a gravatar image.
|
119
|
+
user.email # => Requires user:email or user scope.
|
120
|
+
user.company
|
121
|
+
|
122
|
+
# These require user scope.
|
123
|
+
user.organization_member?('rails') # => Checks 'rails' organization membership.
|
124
|
+
user.organization_public_member?('github') # => Checks publicly disclosed 'github' organization membership.
|
125
|
+
user.team_member?(1234) # => Checks membership in team with id 1234.
|
126
|
+
|
127
|
+
# API access
|
128
|
+
user.api # => Authenticated Octokit::Client for the user.
|
129
|
+
```
|
130
|
+
|
131
|
+
For more information on API access, refer to the [octokit documentation](http://rdoc.info/gems/octokit).
|
132
|
+
|
133
|
+
## Additional Information
|
134
|
+
|
135
|
+
- [warden](https://github.com/hassox/warden)
|
136
|
+
- [octokit](https://github.com/pengwynn/octokit)
|
137
|
+
- [GitHub OAuth Busy Developer's Guide](https://gist.github.com/technoweenie/419219)
|
138
|
+
- [GitHub API documentation](http://developer.github.com)
|
139
|
+
- [List of GitHub OAuth scopes](http://developer.github.com/v3/oauth/#scopes)
|
140
|
+
- [Register a new OAuth application on GitHub](https://github.com/settings/applications/new)
|
17
141
|
|
18
|
-
% GITHUB_CLIENT_ID="<from GH>" GITHUB_CLIENT_SECRET="<from GH>" bundle exec rackup
|
data/config.ru
CHANGED
@@ -15,6 +15,11 @@ rescue LoadError
|
|
15
15
|
end
|
16
16
|
|
17
17
|
require 'warden/github'
|
18
|
-
|
18
|
+
|
19
|
+
if ENV['MULTI_SCOPE_APP']
|
20
|
+
require File.expand_path('../example/multi_scope_app', __FILE__)
|
21
|
+
else
|
22
|
+
require File.expand_path('../example/simple_app', __FILE__)
|
23
|
+
end
|
19
24
|
|
20
25
|
run Example.app
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.expand_path('../setup', __FILE__)
|
2
|
+
|
3
|
+
module Example
|
4
|
+
class MultiScopeApp < BaseApp
|
5
|
+
enable :inline_templates
|
6
|
+
|
7
|
+
GITHUB_CONFIG = {
|
8
|
+
:client_id => ENV['GITHUB_CLIENT_ID'] || 'test_client_id',
|
9
|
+
:client_secret => ENV['GITHUB_CLIENT_SECRET'] || 'test_client_secret'
|
10
|
+
}
|
11
|
+
|
12
|
+
use Warden::Manager do |config|
|
13
|
+
config.failure_app = BadAuthentication
|
14
|
+
config.default_strategies :github
|
15
|
+
config.scope_defaults :default, :config => GITHUB_CONFIG
|
16
|
+
config.scope_defaults :admin, :config => GITHUB_CONFIG.merge(:scope => 'user,notifications')
|
17
|
+
end
|
18
|
+
|
19
|
+
get '/' do
|
20
|
+
erb :index
|
21
|
+
end
|
22
|
+
|
23
|
+
get '/login' do
|
24
|
+
env['warden'].authenticate!
|
25
|
+
redirect '/'
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/admin/login' do
|
29
|
+
env['warden'].authenticate!(:scope => :admin)
|
30
|
+
redirect '/'
|
31
|
+
end
|
32
|
+
|
33
|
+
get '/logout' do
|
34
|
+
if params.include?('all')
|
35
|
+
env['warden'].logout
|
36
|
+
else
|
37
|
+
env['warden'].logout(:default)
|
38
|
+
end
|
39
|
+
redirect '/'
|
40
|
+
end
|
41
|
+
|
42
|
+
get '/admin/logout' do
|
43
|
+
env['warden'].logout(:admin)
|
44
|
+
redirect '/'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.app
|
49
|
+
@app ||= Rack::Builder.new do
|
50
|
+
run MultiScopeApp
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
__END__
|
56
|
+
|
57
|
+
@@ index
|
58
|
+
<html>
|
59
|
+
<body>
|
60
|
+
<h1>Multi Scope App Example</h1>
|
61
|
+
<ul>
|
62
|
+
<% if env['warden'].authenticated? %>
|
63
|
+
<li><a href='/logout'>[User] sign out</a></li>
|
64
|
+
<% else %>
|
65
|
+
<li><a href='/login'>[User] sign in</a></li>
|
66
|
+
<% end %>
|
67
|
+
<% if env['warden'].authenticated?(:admin) %>
|
68
|
+
<li><a href='/admin/logout'>[Admin] sign out</a></li>
|
69
|
+
<% else %>
|
70
|
+
<li><a href='/admin/login'>[Admin] sign in</a></li>
|
71
|
+
<% end %>
|
72
|
+
<% if env['warden'].authenticated? && env['warden'].authenticated?(:admin) %>
|
73
|
+
<li><a href='/logout?all=1'>[User & Admin] sign out</a></li>
|
74
|
+
<% end %>
|
75
|
+
</ul>
|
76
|
+
<hr />
|
77
|
+
<dl>
|
78
|
+
<dt>User:</dt>
|
79
|
+
<dd><%= env['warden'].authenticated? ? env['warden'].user.name : 'Not signed in' %></dd>
|
80
|
+
<dt>Admin:</dt>
|
81
|
+
<dd><%= env['warden'].authenticated?(:admin) ? env['warden'].user(:admin).name : 'Not signed in' %></dd>
|
82
|
+
</dl>
|
83
|
+
</body>
|
84
|
+
</html>
|
data/example/setup.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'yajl/json_gem'
|
3
|
+
|
4
|
+
module Example
|
5
|
+
class BaseApp < Sinatra::Base
|
6
|
+
enable :sessions
|
7
|
+
enable :raise_errors
|
8
|
+
disable :show_exceptions
|
9
|
+
|
10
|
+
get '/debug' do
|
11
|
+
content_type :text
|
12
|
+
env['rack.session'].to_yaml
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class BadAuthentication < Sinatra::Base
|
17
|
+
get '/unauthenticated' do
|
18
|
+
status 403
|
19
|
+
<<-EOS
|
20
|
+
<h2>Unable to authenticate, sorry bud.</h2>
|
21
|
+
<p>#{env['warden'].message}</p>
|
22
|
+
EOS
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require File.expand_path('../setup', __FILE__)
|
2
|
+
|
3
|
+
module Example
|
4
|
+
class SimpleApp < BaseApp
|
5
|
+
enable :inline_templates
|
6
|
+
|
7
|
+
GITHUB_CONFIG = {
|
8
|
+
:client_id => ENV['GITHUB_CLIENT_ID'] || 'test_client_id',
|
9
|
+
:client_secret => ENV['GITHUB_CLIENT_SECRET'] || 'test_client_secret',
|
10
|
+
:scope => 'user'
|
11
|
+
}
|
12
|
+
|
13
|
+
use Warden::Manager do |config|
|
14
|
+
config.failure_app = BadAuthentication
|
15
|
+
config.default_strategies :github
|
16
|
+
config.scope_defaults :default, :config => GITHUB_CONFIG
|
17
|
+
end
|
18
|
+
|
19
|
+
get '/' do
|
20
|
+
erb :index
|
21
|
+
end
|
22
|
+
|
23
|
+
get '/profile' do
|
24
|
+
env['warden'].authenticate!
|
25
|
+
erb :profile
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/login' do
|
29
|
+
env['warden'].authenticate!
|
30
|
+
redirect '/'
|
31
|
+
end
|
32
|
+
|
33
|
+
get '/logout' do
|
34
|
+
env['warden'].logout
|
35
|
+
redirect '/'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.app
|
40
|
+
@app ||= Rack::Builder.new do
|
41
|
+
run SimpleApp
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
__END__
|
47
|
+
|
48
|
+
@@ layout
|
49
|
+
<html>
|
50
|
+
<body>
|
51
|
+
<h1>Simple App Example</h1>
|
52
|
+
<ul>
|
53
|
+
<li><a href='/'>Home</a></li>
|
54
|
+
<li><a href='/profile'>View profile</a><% if !env['warden'].authenticated? %> (implicit sign in)<% end %></li>
|
55
|
+
<% if env['warden'].authenticated? %>
|
56
|
+
<li><a href='/logout'>Sign out</a></li>
|
57
|
+
<% else %>
|
58
|
+
<li><a href='/login'>Sign in</a> (explicit sign in)</li>
|
59
|
+
<% end %>
|
60
|
+
</ul>
|
61
|
+
<hr />
|
62
|
+
<%= yield %>
|
63
|
+
</body>
|
64
|
+
</html>
|
65
|
+
|
66
|
+
@@ index
|
67
|
+
<% if env['warden'].authenticated? %>
|
68
|
+
<h2>
|
69
|
+
<img src='http://gravatar.com/avatar/<%= env['warden'].user.gravatar_id %>.png?r=PG&s=50' />
|
70
|
+
Welcome <%= env['warden'].user.name %>
|
71
|
+
</h2>
|
72
|
+
<% else %>
|
73
|
+
<h2>Welcome stranger</h2>
|
74
|
+
<% end %>
|
75
|
+
|
76
|
+
@@ profile
|
77
|
+
<h2>Profile</h2>
|
78
|
+
<dl>
|
79
|
+
<dt>Rails Org Member:</dt>
|
80
|
+
<dd><%= env['warden'].user.organization_member?('rails') %></dd>
|
81
|
+
<dt>Publicized Rails Org Member:</dt>
|
82
|
+
<dd><%= env['warden'].user.organization_public_member?('rails') %></dd>
|
83
|
+
<dt>Rails Committer Team Member:</dt>
|
84
|
+
<dd><%= env['warden'].user.team_member?(632) %></dd>
|
85
|
+
</dl>
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Warden
|
4
|
+
module GitHub
|
5
|
+
# This class encapsulates the configuration of the strategy. A strategy can
|
6
|
+
# be configured through Warden::Manager by defining a scope's default. Thus,
|
7
|
+
# it is possible to use the same strategy with different configurations by
|
8
|
+
# using multiple scopes.
|
9
|
+
#
|
10
|
+
# To configure a scope, use #scope_defaults inside the Warden::Manager
|
11
|
+
# config block. The first arg is the name of the scope (the default is
|
12
|
+
# :default, so use that to configure the default scope), the second arg is
|
13
|
+
# an options hash which should contain:
|
14
|
+
#
|
15
|
+
# - :strategies : An array of strategies to use for this scope. Since this
|
16
|
+
# strategy is called :github, include it in the array.
|
17
|
+
#
|
18
|
+
# - :config : A hash containing the configs that are used for OAuth.
|
19
|
+
# Valid parameters include :client_id, :client_secret,
|
20
|
+
# :scope, :redirect_uri. Please refer to the OAuth
|
21
|
+
# documentation of the GitHub API for the meaning of these
|
22
|
+
# parameters.
|
23
|
+
#
|
24
|
+
# If :client_id or :client_secret are not specified, they
|
25
|
+
# will be fetched from ENV['GITHUB_CLIENT_ID'] and
|
26
|
+
# ENV['GITHUB_CLIENT_SECRET'], respectively.
|
27
|
+
#
|
28
|
+
# :scope defaults to nil.
|
29
|
+
#
|
30
|
+
# If no :redirect_uri is specified, the current path will
|
31
|
+
# be used. If a path is specified it will be appended to
|
32
|
+
# the request host, forming a valid URL.
|
33
|
+
#
|
34
|
+
# Examples
|
35
|
+
#
|
36
|
+
# use Warden::Manager do |config|
|
37
|
+
# config.failure_app = BadAuthentication
|
38
|
+
#
|
39
|
+
# # The following line doesn't specify any custom configurations, thus
|
40
|
+
# # the default scope will be using the implict client_id,
|
41
|
+
# # client_secret, and redirect_uri.
|
42
|
+
# config.default_strategies :github
|
43
|
+
#
|
44
|
+
# # This configures an additional scope that uses the github strategy
|
45
|
+
# # with custom configuration.
|
46
|
+
# config.scope_defaults :admin, :config => { :client_id => 'foobar',
|
47
|
+
# :client_secret => 'barfoo',
|
48
|
+
# :scope => 'user,repo',
|
49
|
+
# :redirect_uri => '/admin/oauth/callback' }
|
50
|
+
# end
|
51
|
+
class Config
|
52
|
+
BadConfig = Class.new(StandardError)
|
53
|
+
|
54
|
+
include ::Warden::Mixins::Common
|
55
|
+
|
56
|
+
attr_reader :env, :warden_scope
|
57
|
+
|
58
|
+
def initialize(env, warden_scope)
|
59
|
+
@env = env
|
60
|
+
@warden_scope = warden_scope
|
61
|
+
end
|
62
|
+
|
63
|
+
def client_id
|
64
|
+
custom_config[:client_id] ||
|
65
|
+
deprecated_config(:github_client_id) ||
|
66
|
+
ENV['GITHUB_CLIENT_ID'] ||
|
67
|
+
fail(BadConfig, 'Missing client_id configuration.')
|
68
|
+
end
|
69
|
+
|
70
|
+
def client_secret
|
71
|
+
custom_config[:client_secret] ||
|
72
|
+
deprecated_config(:github_secret) ||
|
73
|
+
ENV['GITHUB_CLIENT_SECRET'] ||
|
74
|
+
fail(BadConfig, 'Missing client_secret configuration.')
|
75
|
+
end
|
76
|
+
|
77
|
+
def redirect_uri
|
78
|
+
uri_or_path =
|
79
|
+
custom_config[:redirect_uri] ||
|
80
|
+
deprecated_config(:github_callback_url) ||
|
81
|
+
request.path
|
82
|
+
|
83
|
+
normalized_uri(uri_or_path).to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
def scope
|
87
|
+
custom_config[:scope] || deprecated_config(:github_scopes)
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_hash
|
91
|
+
{ :client_id => client_id,
|
92
|
+
:client_secret => client_secret,
|
93
|
+
:redirect_uri => redirect_uri,
|
94
|
+
:scope => scope }
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def custom_config
|
100
|
+
@custom_config ||=
|
101
|
+
env['warden'].
|
102
|
+
config[:scope_defaults].
|
103
|
+
fetch(warden_scope, {}).
|
104
|
+
fetch(:config, {})
|
105
|
+
end
|
106
|
+
|
107
|
+
def deprecated_config(name)
|
108
|
+
env['warden'].config[name].tap do |config|
|
109
|
+
unless config.nil?
|
110
|
+
warn "[warden-github] Deprecated configuration #{name} used. Please refer to the README for updated configuration instructions."
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def normalized_uri(uri_or_path)
|
116
|
+
uri = URI(request.url)
|
117
|
+
uri.path = extract_path(URI(uri_or_path))
|
118
|
+
uri.query = nil
|
119
|
+
uri.fragment = nil
|
120
|
+
|
121
|
+
correct_scheme(uri)
|
122
|
+
end
|
123
|
+
|
124
|
+
def extract_path(uri)
|
125
|
+
path = uri.path
|
126
|
+
|
127
|
+
if path.start_with?('/')
|
128
|
+
path
|
129
|
+
else
|
130
|
+
"/#{path}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def correct_scheme(uri)
|
135
|
+
if uri.scheme != 'https' && env['HTTP_X_FORWARDED_PROTO'] == 'https'
|
136
|
+
uri.scheme = 'https'
|
137
|
+
# Reparsing will use a different URI subclass, namely URI::HTTPS which
|
138
|
+
# knows the default port for https and strips it if present.
|
139
|
+
uri = URI(uri.to_s)
|
140
|
+
end
|
141
|
+
|
142
|
+
uri
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Warden
|
2
|
+
module GitHub
|
3
|
+
# A hash subclass that acts as a cache for organization and team
|
4
|
+
# membership states. Only membership states that are true are cached. These
|
5
|
+
# are invalidated after a certain time.
|
6
|
+
class MembershipCache < ::Hash
|
7
|
+
CACHE_TIMEOUT = 60 * 5
|
8
|
+
|
9
|
+
# Fetches a membership status by type and id (e.g. 'org', 'my_company')
|
10
|
+
# from cache. If no cached value is present or if the cached value
|
11
|
+
# expired, the block will be invoked and the return value, if true,
|
12
|
+
# cached for e certain time.
|
13
|
+
def fetch_membership(type, id)
|
14
|
+
type = type.to_s
|
15
|
+
id = id.to_s if id.is_a?(Symbol)
|
16
|
+
|
17
|
+
if cached_membership_valid?(type, id)
|
18
|
+
true
|
19
|
+
elsif block_given? && yield
|
20
|
+
cache_membership(type, id)
|
21
|
+
true
|
22
|
+
else
|
23
|
+
false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def cached_membership_valid?(type, id)
|
30
|
+
timestamp = fetch(type).fetch(id)
|
31
|
+
|
32
|
+
if Time.now.to_i > timestamp + CACHE_TIMEOUT
|
33
|
+
fetch(type).delete(id)
|
34
|
+
false
|
35
|
+
else
|
36
|
+
true
|
37
|
+
end
|
38
|
+
rescue IndexError
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
def cache_membership(type, id)
|
43
|
+
hash = self[type] ||= {}
|
44
|
+
hash[id] = Time.now.to_i
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -67,7 +67,7 @@ module Warden
|
|
67
67
|
def validate_flow!
|
68
68
|
if params['state'] != state
|
69
69
|
abort_flow!('State mismatch')
|
70
|
-
elsif (error = params['error']) && !error.
|
70
|
+
elsif (error = params['error']) && !error.empty?
|
71
71
|
abort_flow!(error.gsub(/_/, ' '))
|
72
72
|
end
|
73
73
|
end
|
@@ -88,32 +88,11 @@ module Warden
|
|
88
88
|
|
89
89
|
def oauth
|
90
90
|
@oauth ||= OAuth.new(
|
91
|
-
:code
|
92
|
-
:state => state,
|
93
|
-
:scope => env['warden'].config[:github_scopes],
|
94
|
-
:client_id => env['warden'].config[:github_client_id],
|
95
|
-
:client_secret => env['warden'].config[:github_secret],
|
96
|
-
:redirect_uri => redirect_uri)
|
91
|
+
config.to_hash.merge(:code => params['code'], :state => state))
|
97
92
|
end
|
98
93
|
|
99
|
-
def
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
|
-
def callback_path
|
104
|
-
env['warden'].config[:github_callback_url] || request.path
|
105
|
-
end
|
106
|
-
|
107
|
-
def absolute_uri(request, suffix = nil, proto = "http")
|
108
|
-
port_part = case request.scheme
|
109
|
-
when "http"
|
110
|
-
request.port == 80 ? "" : ":#{request.port}"
|
111
|
-
when "https"
|
112
|
-
request.port == 443 ? "" : ":#{request.port}"
|
113
|
-
end
|
114
|
-
|
115
|
-
proto = "http" if proto.nil?
|
116
|
-
"#{proto}://#{request.host}#{port_part}#{suffix}"
|
94
|
+
def config
|
95
|
+
@config ||= ::Warden::GitHub::Config.new(env, scope)
|
117
96
|
end
|
118
97
|
end
|
119
98
|
end
|
data/lib/warden/github/user.rb
CHANGED
@@ -30,7 +30,9 @@ module Warden
|
|
30
30
|
#
|
31
31
|
# Returns: true if the user is publicized as an org member
|
32
32
|
def organization_public_member?(org_name)
|
33
|
-
|
33
|
+
memberships.fetch_membership(:org_pub, org_name) do
|
34
|
+
api.organization_public_member?(org_name, login)
|
35
|
+
end
|
34
36
|
end
|
35
37
|
|
36
38
|
# Backwards compatibility:
|
@@ -42,7 +44,9 @@ module Warden
|
|
42
44
|
#
|
43
45
|
# Returns: true if the user has access, false otherwise
|
44
46
|
def organization_member?(org_name)
|
45
|
-
|
47
|
+
memberships.fetch_membership(:org, org_name) do
|
48
|
+
api.organization_member?(org_name, login)
|
49
|
+
end
|
46
50
|
end
|
47
51
|
|
48
52
|
# See if the user is a member of the team id
|
@@ -51,17 +55,20 @@ module Warden
|
|
51
55
|
#
|
52
56
|
# Returns: true if the user has access, false otherwise
|
53
57
|
def team_member?(team_id)
|
54
|
-
|
55
|
-
|
58
|
+
memberships.fetch_membership(:team, team_id) do
|
59
|
+
# TODO: Use next line as method body once pengwynn/octokit#206 is public.
|
60
|
+
# api.team_member?(team_id, login)
|
56
61
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
62
|
+
begin
|
63
|
+
# A user is only able to query for team members if they're a member.
|
64
|
+
# Thus, if querying does succeed, they will be in the list and
|
65
|
+
# checking the list won't be necessary.
|
66
|
+
api.team_members(team_id)
|
67
|
+
true
|
68
|
+
rescue Octokit::NotFound
|
69
|
+
false
|
70
|
+
end
|
71
|
+
end
|
65
72
|
end
|
66
73
|
|
67
74
|
# Access the GitHub API from Octokit
|
@@ -76,6 +83,12 @@ module Warden
|
|
76
83
|
# marshaled even when explicitly specifying #marshal_dump.
|
77
84
|
Octokit::Client.new(:login => login, :oauth_token => token)
|
78
85
|
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def memberships
|
90
|
+
attribs['member'] ||= MembershipCache.new
|
91
|
+
end
|
79
92
|
end
|
80
93
|
end
|
81
94
|
end
|
data/lib/warden/github.rb
CHANGED
@@ -5,6 +5,7 @@ require 'warden/github/oauth'
|
|
5
5
|
require 'warden/github/version'
|
6
6
|
require 'warden/github/strategy'
|
7
7
|
require 'warden/github/hook'
|
8
|
+
require 'warden/github/config'
|
9
|
+
require 'warden/github/membership_cache'
|
8
10
|
|
9
|
-
require '
|
10
|
-
require 'securerandom'
|
11
|
+
require 'securerandom'
|
data/spec/spec_helper.rb
CHANGED
@@ -5,25 +5,13 @@ SimpleCov.start do
|
|
5
5
|
end
|
6
6
|
|
7
7
|
require 'warden/github'
|
8
|
-
require File.expand_path('../../example/
|
8
|
+
require File.expand_path('../../example/simple_app', __FILE__)
|
9
9
|
require 'rack/test'
|
10
|
-
require 'webrat'
|
11
10
|
require 'addressable/uri'
|
12
|
-
require 'pp'
|
13
11
|
require 'webmock/rspec'
|
14
12
|
|
15
|
-
Webrat.configure do |config|
|
16
|
-
config.mode = :rack
|
17
|
-
config.application_port = 4567
|
18
|
-
end
|
19
|
-
|
20
13
|
RSpec.configure do |config|
|
21
14
|
config.include(Rack::Test::Methods)
|
22
|
-
config.include(Webrat::Methods)
|
23
|
-
config.include(Webrat::Matchers)
|
24
|
-
|
25
|
-
config.before(:each) do
|
26
|
-
end
|
27
15
|
|
28
16
|
def app
|
29
17
|
Example.app
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Warden::GitHub::Config do
|
4
|
+
let(:warden_scope) { :test_scope }
|
5
|
+
|
6
|
+
let(:env) do
|
7
|
+
{ 'warden' => stub(:config => warden_config) }
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:warden_config) do
|
11
|
+
{ :scope_defaults => { warden_scope => { :config => scope_config } } }
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:scope_config) do
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:request) do
|
19
|
+
stub(:url => 'http://example.com/the/path', :path => '/the/path')
|
20
|
+
end
|
21
|
+
|
22
|
+
subject(:config) do
|
23
|
+
described_class.new(env, warden_scope)
|
24
|
+
end
|
25
|
+
|
26
|
+
before do
|
27
|
+
config.stub(:request => request)
|
28
|
+
end
|
29
|
+
|
30
|
+
def silence_warnings
|
31
|
+
old_verbose, $VERBOSE = $VERBOSE, nil
|
32
|
+
yield
|
33
|
+
ensure
|
34
|
+
$VERBOSE = old_verbose
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#client_id' do
|
38
|
+
context 'when specified in scope config' do
|
39
|
+
it 'returns the client id' do
|
40
|
+
scope_config[:client_id] = 'foobar'
|
41
|
+
config.client_id.should eq 'foobar'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when specified in deprecated config' do
|
46
|
+
it 'returns the client id' do
|
47
|
+
warden_config[:github_client_id] = 'foobar'
|
48
|
+
silence_warnings do
|
49
|
+
config.client_id.should eq 'foobar'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'when specified in ENV' do
|
55
|
+
it 'returns the client id' do
|
56
|
+
ENV.stub(:[]).with('GITHUB_CLIENT_ID').and_return('foobar')
|
57
|
+
config.client_id.should eq 'foobar'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'when not specified' do
|
62
|
+
it 'raises BadConfig' do
|
63
|
+
expect { config.client_id }.to raise_error(described_class::BadConfig)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '#client_secret' do
|
69
|
+
context 'when specified in scope config' do
|
70
|
+
it 'returns the client secret' do
|
71
|
+
scope_config[:client_secret] = 'foobar'
|
72
|
+
config.client_secret.should eq 'foobar'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'when specified in deprecated config' do
|
77
|
+
it 'returns the client secret' do
|
78
|
+
warden_config[:github_secret] = 'foobar'
|
79
|
+
silence_warnings do
|
80
|
+
config.client_secret.should eq 'foobar'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'when specified in ENV' do
|
86
|
+
it 'returns the client secret' do
|
87
|
+
ENV.stub(:[]).with('GITHUB_CLIENT_SECRET').and_return('foobar')
|
88
|
+
silence_warnings do
|
89
|
+
config.client_secret.should eq 'foobar'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'when not specified' do
|
95
|
+
it 'raises BadConfig' do
|
96
|
+
expect { config.client_secret }.to raise_error(described_class::BadConfig)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '#redirect_uri' do
|
102
|
+
context 'when specified in scope config' do
|
103
|
+
it 'returns the expanded redirect uri' do
|
104
|
+
scope_config[:redirect_uri] = '/callback'
|
105
|
+
config.redirect_uri.should eq 'http://example.com/callback'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'when specified path lacks leading slash' do
|
110
|
+
it 'corrects the path and returns the expanded uri' do
|
111
|
+
scope_config[:redirect_uri] = 'callback'
|
112
|
+
config.redirect_uri.should eq 'http://example.com/callback'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'when specified in deprecated config' do
|
117
|
+
it 'returns the expanded redirect uri' do
|
118
|
+
warden_config[:github_callback_url] = '/callback'
|
119
|
+
silence_warnings do
|
120
|
+
config.redirect_uri.should eq 'http://example.com/callback'
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'when not specified' do
|
126
|
+
it 'returns the expanded redirect uri with the current path' do
|
127
|
+
config.redirect_uri.should eq 'http://example.com/the/path'
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'when HTTP_X_FORWARDED_PROTO is set to https' do
|
132
|
+
it 'returns the expanded redirect uri with adjusted scheme' do
|
133
|
+
env['HTTP_X_FORWARDED_PROTO'] = 'https'
|
134
|
+
request.stub(:url => 'http://example.com:443/the/path')
|
135
|
+
config.redirect_uri.should eq 'https://example.com/the/path'
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe '#scope' do
|
141
|
+
context 'when specified in scope config' do
|
142
|
+
it 'returns the client secret' do
|
143
|
+
scope_config[:scope] = 'user'
|
144
|
+
config.scope.should eq 'user'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'when specified in deprecated config' do
|
149
|
+
it 'returns the client secret' do
|
150
|
+
warden_config[:github_scopes] = 'user'
|
151
|
+
silence_warnings do
|
152
|
+
config.scope.should eq 'user'
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'when not specified' do
|
158
|
+
it 'returns nil' do
|
159
|
+
config.scope.should be_nil
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe '#to_hash' do
|
165
|
+
it 'includes all configs' do
|
166
|
+
scope_config.merge!(
|
167
|
+
:scope => 'user',
|
168
|
+
:client_id => 'abc',
|
169
|
+
:client_secret => '123',
|
170
|
+
:redirect_uri => '/foo')
|
171
|
+
|
172
|
+
config.to_hash.keys.
|
173
|
+
should =~ [:scope, :client_id, :client_secret, :redirect_uri]
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Warden::GitHub::MembershipCache do
|
4
|
+
subject(:cache) do
|
5
|
+
described_class.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#fetch_membership' do
|
9
|
+
it 'returns false by default' do
|
10
|
+
cache.fetch_membership('foo', 'bar').should be_false
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when cache valid' do
|
14
|
+
before do
|
15
|
+
cache['foo'] = {}
|
16
|
+
cache['foo']['bar'] = Time.now.to_i - described_class::CACHE_TIMEOUT + 5
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'returns true' do
|
20
|
+
cache.fetch_membership('foo', 'bar').should be_true
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'does not invoke the block' do
|
24
|
+
expect { |b| cache.fetch_membership('foo', 'bar', &b) }.
|
25
|
+
to_not yield_control
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when cache expired' do
|
30
|
+
before do
|
31
|
+
cache['foo'] = {}
|
32
|
+
cache['foo']['bar'] = Time.now.to_i - described_class::CACHE_TIMEOUT - 5
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when no block given' do
|
36
|
+
it 'returns false' do
|
37
|
+
cache.fetch_membership('foo', 'bar').should be_false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'deletes the cached value' do
|
42
|
+
cache.fetch_membership('foo', 'bar')
|
43
|
+
cache['foo'].should_not have_key('bar')
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'invokes the block' do
|
47
|
+
expect { |b| cache.fetch_membership('foo', 'bar', &b) }.
|
48
|
+
to yield_control
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'caches the value when block returns true' do
|
53
|
+
cache.fetch_membership('foo', 'bar') { true }
|
54
|
+
cache.fetch_membership('foo', 'bar').should be_true
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'does not cache the value when block returns false' do
|
58
|
+
cache.fetch_membership('foo', 'bar') { false }
|
59
|
+
cache.fetch_membership('foo', 'bar').should be_false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/spec/unit/user_spec.rb
CHANGED
@@ -46,23 +46,23 @@ describe Warden::GitHub::User do
|
|
46
46
|
|
47
47
|
[:organization_public_member?, :organization_member?].each do |method|
|
48
48
|
describe "##{method}" do
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
context 'when user is not member' do
|
50
|
+
it 'returns false' do
|
51
|
+
stub_api(user, method, ['rails', user.login], false)
|
52
|
+
user.send(method, 'rails').should be_false
|
53
|
+
end
|
54
|
+
end
|
52
55
|
|
53
|
-
|
56
|
+
context 'when user is member' do
|
57
|
+
it 'returns true' do
|
58
|
+
stub_api(user, method, ['rails', user.login], true)
|
59
|
+
user.send(method, 'rails').should be_true
|
60
|
+
end
|
54
61
|
end
|
55
62
|
end
|
56
63
|
end
|
57
64
|
|
58
65
|
describe '#team_member?' do
|
59
|
-
it 'asks the api for team members' do
|
60
|
-
status = double
|
61
|
-
stub_api(user, :team_members, [123], false)
|
62
|
-
|
63
|
-
user.team_member?(123)
|
64
|
-
end
|
65
|
-
|
66
66
|
context 'when user is not member' do
|
67
67
|
it 'returns false' do
|
68
68
|
api = double
|
data/warden-github.gemspec
CHANGED
@@ -15,18 +15,17 @@ Gem::Specification.new do |s|
|
|
15
15
|
|
16
16
|
s.add_dependency "warden", ">1.0"
|
17
17
|
s.add_dependency "octokit", ">=1.22.0"
|
18
|
-
s.add_dependency "yajl-ruby", ">=1.1.0"
|
19
18
|
|
20
19
|
s.add_development_dependency "rack", "~>1.4.1"
|
21
20
|
s.add_development_dependency "rake"
|
22
21
|
s.add_development_dependency "rspec", "~>2.8"
|
23
22
|
s.add_development_dependency "simplecov"
|
24
23
|
s.add_development_dependency "webmock", "~>1.9"
|
25
|
-
s.add_development_dependency "webrat"
|
26
24
|
s.add_development_dependency "sinatra"
|
27
25
|
s.add_development_dependency "shotgun"
|
28
26
|
s.add_development_dependency "addressable", "~>2.2.0"
|
29
27
|
s.add_development_dependency "rack-test", "~>0.5.3"
|
28
|
+
s.add_development_dependency "yajl-ruby"
|
30
29
|
|
31
30
|
s.files = `git ls-files`.split("\n")
|
32
31
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: warden-github
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 41
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 13
|
9
|
-
-
|
10
|
-
version: 0.13.
|
9
|
+
- 1
|
10
|
+
version: 0.13.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Corey Donohoe
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2013-02-
|
18
|
+
date: 2013-02-18 00:00:00 -08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -49,26 +49,10 @@ dependencies:
|
|
49
49
|
version: 1.22.0
|
50
50
|
type: :runtime
|
51
51
|
version_requirements: *id002
|
52
|
-
- !ruby/object:Gem::Dependency
|
53
|
-
name: yajl-ruby
|
54
|
-
prerelease: false
|
55
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
-
none: false
|
57
|
-
requirements:
|
58
|
-
- - ">="
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
hash: 19
|
61
|
-
segments:
|
62
|
-
- 1
|
63
|
-
- 1
|
64
|
-
- 0
|
65
|
-
version: 1.1.0
|
66
|
-
type: :runtime
|
67
|
-
version_requirements: *id003
|
68
52
|
- !ruby/object:Gem::Dependency
|
69
53
|
name: rack
|
70
54
|
prerelease: false
|
71
|
-
requirement: &
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
72
56
|
none: false
|
73
57
|
requirements:
|
74
58
|
- - ~>
|
@@ -80,11 +64,11 @@ dependencies:
|
|
80
64
|
- 1
|
81
65
|
version: 1.4.1
|
82
66
|
type: :development
|
83
|
-
version_requirements: *
|
67
|
+
version_requirements: *id003
|
84
68
|
- !ruby/object:Gem::Dependency
|
85
69
|
name: rake
|
86
70
|
prerelease: false
|
87
|
-
requirement: &
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
88
72
|
none: false
|
89
73
|
requirements:
|
90
74
|
- - ">="
|
@@ -94,11 +78,11 @@ dependencies:
|
|
94
78
|
- 0
|
95
79
|
version: "0"
|
96
80
|
type: :development
|
97
|
-
version_requirements: *
|
81
|
+
version_requirements: *id004
|
98
82
|
- !ruby/object:Gem::Dependency
|
99
83
|
name: rspec
|
100
84
|
prerelease: false
|
101
|
-
requirement: &
|
85
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
102
86
|
none: false
|
103
87
|
requirements:
|
104
88
|
- - ~>
|
@@ -109,11 +93,11 @@ dependencies:
|
|
109
93
|
- 8
|
110
94
|
version: "2.8"
|
111
95
|
type: :development
|
112
|
-
version_requirements: *
|
96
|
+
version_requirements: *id005
|
113
97
|
- !ruby/object:Gem::Dependency
|
114
98
|
name: simplecov
|
115
99
|
prerelease: false
|
116
|
-
requirement: &
|
100
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
117
101
|
none: false
|
118
102
|
requirements:
|
119
103
|
- - ">="
|
@@ -123,11 +107,11 @@ dependencies:
|
|
123
107
|
- 0
|
124
108
|
version: "0"
|
125
109
|
type: :development
|
126
|
-
version_requirements: *
|
110
|
+
version_requirements: *id006
|
127
111
|
- !ruby/object:Gem::Dependency
|
128
112
|
name: webmock
|
129
113
|
prerelease: false
|
130
|
-
requirement: &
|
114
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
131
115
|
none: false
|
132
116
|
requirements:
|
133
117
|
- - ~>
|
@@ -138,25 +122,11 @@ dependencies:
|
|
138
122
|
- 9
|
139
123
|
version: "1.9"
|
140
124
|
type: :development
|
141
|
-
version_requirements: *
|
142
|
-
- !ruby/object:Gem::Dependency
|
143
|
-
name: webrat
|
144
|
-
prerelease: false
|
145
|
-
requirement: &id009 !ruby/object:Gem::Requirement
|
146
|
-
none: false
|
147
|
-
requirements:
|
148
|
-
- - ">="
|
149
|
-
- !ruby/object:Gem::Version
|
150
|
-
hash: 3
|
151
|
-
segments:
|
152
|
-
- 0
|
153
|
-
version: "0"
|
154
|
-
type: :development
|
155
|
-
version_requirements: *id009
|
125
|
+
version_requirements: *id007
|
156
126
|
- !ruby/object:Gem::Dependency
|
157
127
|
name: sinatra
|
158
128
|
prerelease: false
|
159
|
-
requirement: &
|
129
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
160
130
|
none: false
|
161
131
|
requirements:
|
162
132
|
- - ">="
|
@@ -166,11 +136,11 @@ dependencies:
|
|
166
136
|
- 0
|
167
137
|
version: "0"
|
168
138
|
type: :development
|
169
|
-
version_requirements: *
|
139
|
+
version_requirements: *id008
|
170
140
|
- !ruby/object:Gem::Dependency
|
171
141
|
name: shotgun
|
172
142
|
prerelease: false
|
173
|
-
requirement: &
|
143
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
174
144
|
none: false
|
175
145
|
requirements:
|
176
146
|
- - ">="
|
@@ -180,11 +150,11 @@ dependencies:
|
|
180
150
|
- 0
|
181
151
|
version: "0"
|
182
152
|
type: :development
|
183
|
-
version_requirements: *
|
153
|
+
version_requirements: *id009
|
184
154
|
- !ruby/object:Gem::Dependency
|
185
155
|
name: addressable
|
186
156
|
prerelease: false
|
187
|
-
requirement: &
|
157
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
188
158
|
none: false
|
189
159
|
requirements:
|
190
160
|
- - ~>
|
@@ -196,11 +166,11 @@ dependencies:
|
|
196
166
|
- 0
|
197
167
|
version: 2.2.0
|
198
168
|
type: :development
|
199
|
-
version_requirements: *
|
169
|
+
version_requirements: *id010
|
200
170
|
- !ruby/object:Gem::Dependency
|
201
171
|
name: rack-test
|
202
172
|
prerelease: false
|
203
|
-
requirement: &
|
173
|
+
requirement: &id011 !ruby/object:Gem::Requirement
|
204
174
|
none: false
|
205
175
|
requirements:
|
206
176
|
- - ~>
|
@@ -212,7 +182,21 @@ dependencies:
|
|
212
182
|
- 3
|
213
183
|
version: 0.5.3
|
214
184
|
type: :development
|
215
|
-
version_requirements: *
|
185
|
+
version_requirements: *id011
|
186
|
+
- !ruby/object:Gem::Dependency
|
187
|
+
name: yajl-ruby
|
188
|
+
prerelease: false
|
189
|
+
requirement: &id012 !ruby/object:Gem::Requirement
|
190
|
+
none: false
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
hash: 3
|
195
|
+
segments:
|
196
|
+
- 0
|
197
|
+
version: "0"
|
198
|
+
type: :development
|
199
|
+
version_requirements: *id012
|
216
200
|
description: A warden strategy for easy oauth integration with github
|
217
201
|
email:
|
218
202
|
- atmos@atmos.org
|
@@ -231,9 +215,13 @@ files:
|
|
231
215
|
- README.md
|
232
216
|
- Rakefile
|
233
217
|
- config.ru
|
234
|
-
- example/
|
218
|
+
- example/multi_scope_app.rb
|
219
|
+
- example/setup.rb
|
220
|
+
- example/simple_app.rb
|
235
221
|
- lib/warden/github.rb
|
222
|
+
- lib/warden/github/config.rb
|
236
223
|
- lib/warden/github/hook.rb
|
224
|
+
- lib/warden/github/membership_cache.rb
|
237
225
|
- lib/warden/github/oauth.rb
|
238
226
|
- lib/warden/github/strategy.rb
|
239
227
|
- lib/warden/github/user.rb
|
@@ -241,6 +229,8 @@ files:
|
|
241
229
|
- spec/fixtures/user.json
|
242
230
|
- spec/integration/oauth_spec.rb
|
243
231
|
- spec/spec_helper.rb
|
232
|
+
- spec/unit/config_spec.rb
|
233
|
+
- spec/unit/membership_cache_spec.rb
|
244
234
|
- spec/unit/oauth_spec.rb
|
245
235
|
- spec/unit/user_spec.rb
|
246
236
|
- warden-github.gemspec
|
@@ -282,5 +272,7 @@ test_files:
|
|
282
272
|
- spec/fixtures/user.json
|
283
273
|
- spec/integration/oauth_spec.rb
|
284
274
|
- spec/spec_helper.rb
|
275
|
+
- spec/unit/config_spec.rb
|
276
|
+
- spec/unit/membership_cache_spec.rb
|
285
277
|
- spec/unit/oauth_spec.rb
|
286
278
|
- spec/unit/user_spec.rb
|
data/example/app.rb
DELETED
@@ -1,98 +0,0 @@
|
|
1
|
-
require 'sinatra'
|
2
|
-
require 'yajl/json_gem'
|
3
|
-
|
4
|
-
module Example
|
5
|
-
class App < Sinatra::Base
|
6
|
-
enable :sessions
|
7
|
-
enable :raise_errors
|
8
|
-
disable :show_exceptions
|
9
|
-
|
10
|
-
use Warden::Manager do |manager|
|
11
|
-
manager.default_strategies :github
|
12
|
-
manager.failure_app = BadAuthentication
|
13
|
-
|
14
|
-
manager[:github_client_id] = ENV['GITHUB_CLIENT_ID'] || 'ee9aa24b64d82c21535a'
|
15
|
-
manager[:github_secret] = ENV['GITHUB_CLIENT_SECRET'] || 'ed8ff0c54067aefb808dab1ca265865405d08d6f'
|
16
|
-
|
17
|
-
manager[:github_scopes] = ''
|
18
|
-
end
|
19
|
-
|
20
|
-
helpers do
|
21
|
-
def ensure_authenticated
|
22
|
-
unless env['warden'].authenticate!
|
23
|
-
throw(:warden)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def user
|
28
|
-
env['warden'].user
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
get '/' do
|
33
|
-
if user
|
34
|
-
<<-EOS
|
35
|
-
<h2>Hello #{user.name}!</h2>
|
36
|
-
<ul>
|
37
|
-
<li><a href='/profile'>View profile</a></li>
|
38
|
-
<li><a href='/logout'>Sign out</a></li>
|
39
|
-
</ul>
|
40
|
-
EOS
|
41
|
-
else
|
42
|
-
<<-EOS
|
43
|
-
<h2>Hello stranger!</h2>
|
44
|
-
<ul>
|
45
|
-
<li><a href='/profile'>View profile</a> (implicit sign in)</li>
|
46
|
-
<li><a href='/login'>Sign in</a> (explicit sign in)</li>
|
47
|
-
</ul>
|
48
|
-
EOS
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
get '/profile' do
|
53
|
-
ensure_authenticated
|
54
|
-
<<-EOS
|
55
|
-
<h2>Hello #{user.name}!</h2>
|
56
|
-
<ul>
|
57
|
-
<li><a href='/'>Home</a></li>
|
58
|
-
<li><a href='/logout'>Sign out</a></li>
|
59
|
-
</ul>
|
60
|
-
<h3>Profile</h3>
|
61
|
-
<h4>Rails Org Member: #{user.organization_member?('rails')}.</h4>
|
62
|
-
<h4>Publicized Rails Org Member: #{user.organization_public_member?('rails')}.</h4>
|
63
|
-
<h4>Rails Committer Team Member: #{user.team_member?(632)}.</h4>
|
64
|
-
EOS
|
65
|
-
end
|
66
|
-
|
67
|
-
get '/login' do
|
68
|
-
ensure_authenticated
|
69
|
-
redirect '/'
|
70
|
-
end
|
71
|
-
|
72
|
-
get '/logout' do
|
73
|
-
env['warden'].logout
|
74
|
-
redirect '/'
|
75
|
-
end
|
76
|
-
|
77
|
-
get '/debug' do
|
78
|
-
content_type :text
|
79
|
-
env['rack.session'].to_yaml
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
class BadAuthentication < Sinatra::Base
|
84
|
-
get '/unauthenticated' do
|
85
|
-
status 403
|
86
|
-
<<-EOS
|
87
|
-
<h2>Unable to authenticate, sorry bud.</h2>
|
88
|
-
<p>#{env['warden'].message}</p>
|
89
|
-
EOS
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def self.app
|
94
|
-
@app ||= Rack::Builder.new do
|
95
|
-
run App
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|