warden-github 0.13.0 → 0.13.1
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.
- 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
|