warden-github-rails-thinknear-fork 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +20 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +39 -0
  5. data/CHANGELOG.md +20 -0
  6. data/Gemfile +27 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +250 -0
  9. data/Rakefile +6 -0
  10. data/VERSION +1 -0
  11. data/lib/warden/github/rails.rb +39 -0
  12. data/lib/warden/github/rails/config.rb +49 -0
  13. data/lib/warden/github/rails/controller_helpers.rb +39 -0
  14. data/lib/warden/github/rails/railtie.rb +42 -0
  15. data/lib/warden/github/rails/routes.rb +83 -0
  16. data/lib/warden/github/rails/test_helpers.rb +28 -0
  17. data/lib/warden/github/rails/test_helpers/mock_user.rb +30 -0
  18. data/lib/warden/github/rails/version.rb +9 -0
  19. data/spec/integration/controller_helpers_spec.rb +95 -0
  20. data/spec/integration/membership_spec.rb +183 -0
  21. data/spec/integration/route_spec.rb +82 -0
  22. data/spec/integration/scope_spec.rb +33 -0
  23. data/spec/integration/view_helpers_spec.rb +19 -0
  24. data/spec/rails_app/app/controllers/scoped_controller.rb +28 -0
  25. data/spec/rails_app/app/controllers/unscoped_controller.rb +28 -0
  26. data/spec/rails_app/app/controllers/view_tests_controller.rb +2 -0
  27. data/spec/rails_app/app/views/view_tests/authenticated.html.erb +1 -0
  28. data/spec/rails_app/app/views/view_tests/user.html.erb +1 -0
  29. data/spec/rails_app/config.ru +2 -0
  30. data/spec/rails_app/config/application.rb +20 -0
  31. data/spec/rails_app/config/boot.rb +3 -0
  32. data/spec/rails_app/config/environment.rb +3 -0
  33. data/spec/rails_app/config/environments/development.rb +8 -0
  34. data/spec/rails_app/config/environments/production.rb +8 -0
  35. data/spec/rails_app/config/environments/test.rb +11 -0
  36. data/spec/rails_app/config/initializers/secret_token.rb +1 -0
  37. data/spec/rails_app/config/initializers/session_store.rb +1 -0
  38. data/spec/rails_app/config/initializers/warden_github_rails.rb +12 -0
  39. data/spec/rails_app/config/initializers/wrap_parameters.rb +4 -0
  40. data/spec/rails_app/config/routes.rb +53 -0
  41. data/spec/rails_app/script/rails +6 -0
  42. data/spec/spec_helper.rb +40 -0
  43. data/spec/unit/config_spec.rb +67 -0
  44. data/spec/unit/mock_user_spec.rb +21 -0
  45. data/spec/unit/rails_spec.rb +11 -0
  46. data/spec/unit/test_helpers_spec.rb +39 -0
  47. data/warden-github-rails.gemspec +25 -0
  48. metadata +215 -0
@@ -0,0 +1,49 @@
1
+ module Warden
2
+ module GitHub
3
+ module Rails
4
+ class Config
5
+ BadConfig = Class.new(StandardError)
6
+
7
+ # Default scope to use when not explicitly specified.
8
+ attr_accessor :default_scope
9
+
10
+ # The list of scopes and their configs. This is used to add custom
11
+ # configs to a specific scope. When using a scope that is not listed
12
+ # here, it will use the default configs from warden-github.
13
+ attr_reader :scopes
14
+
15
+ # A hash containing team alias names and their numeric id.
16
+ attr_reader :teams
17
+
18
+ def initialize
19
+ @default_scope = :user
20
+ @scopes = {}
21
+ @teams = {}
22
+ end
23
+
24
+ # Adds a scope with custom configurations to the list of scopes.
25
+ def add_scope(name, config={})
26
+ scopes[name] = config
27
+ end
28
+
29
+ # Maps a team id to a name in order to easier reference it.
30
+ def add_team(name, id)
31
+ teams[name.to_sym] = Integer(id)
32
+ end
33
+
34
+ # Gets the team id for a team id or alias.
35
+ def team_id(team)
36
+ # In ruby 1.8 doing a Integer(:symbol) returns an integer. Thus, test
37
+ # for symbol first.
38
+ if team.is_a? Symbol
39
+ teams.fetch(team)
40
+ else
41
+ Integer(team) rescue teams.fetch(team.to_sym)
42
+ end
43
+ rescue IndexError
44
+ fail BadConfig, "No team id defined for team #{team}."
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,39 @@
1
+ module Warden
2
+ module GitHub
3
+ module Rails
4
+ module ControllerHelpers
5
+ def self.included(klass)
6
+ klass.helper_method(:github_authenticated?, :github_user)
7
+ end
8
+
9
+ # Initiates the OAuth flow if not already authenticated for the
10
+ # specified scope.
11
+ def github_authenticate!(scope=Rails.default_scope)
12
+ request.env['warden'].authenticate!(:scope => scope)
13
+ end
14
+
15
+ # Logs out a user if currently logged in for the specified scope.
16
+ def github_logout(scope=Rails.default_scope)
17
+ request.env['warden'].logout(scope)
18
+ end
19
+
20
+ # Checks whether a user is logged in for the specified scope.
21
+ def github_authenticated?(scope=Rails.default_scope)
22
+ request.env['warden'].authenticated?(scope)
23
+ end
24
+
25
+ # Returns the currently signed in user for the specified scope. See the
26
+ # documentation for Warden::GitHub::User for available methods.
27
+ def github_user(scope=Rails.default_scope)
28
+ request.env['warden'].user(scope)
29
+ end
30
+
31
+ # Accessor for the currently signed in user's session. This will be
32
+ # cleared once logged out.
33
+ def github_session(scope=Rails.default_scope)
34
+ request.env['warden'].session(scope) if github_authenticated?(scope)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,42 @@
1
+ module Warden
2
+ module GitHub
3
+ module Rails
4
+ class Railtie < ::Rails::Railtie
5
+ initializer 'warden-github-rails.warden' do |app|
6
+ # When devise is used, it inserts a warden middlware. Multiple warden
7
+ # middlewares do not work properly. Devise allows for a block to be
8
+ # specified that is invoked when its warden middleware is configured.
9
+ # This makes it possible to setup warden-github-rails through devise.
10
+ if defined?(::Devise)
11
+ ::Devise.warden { |config| setup_scopes(config) }
12
+ else
13
+ app.config.middleware.use Warden::Manager do |config|
14
+ setup_failure_app(config)
15
+ setup_scopes(config)
16
+ end
17
+ end
18
+ end
19
+
20
+ initializer 'warden-github-rails.helpers' do
21
+ ActiveSupport.on_load(:action_controller) do
22
+ include ControllerHelpers
23
+ end
24
+ end
25
+
26
+ def setup_scopes(config)
27
+ Rails.scopes.each do |scope, scope_config|
28
+ config.scope_defaults scope, :strategies => [:github],
29
+ :config => scope_config
30
+ end
31
+ end
32
+
33
+ def setup_failure_app(config)
34
+ config.failure_app = lambda do |env|
35
+ [403, {}, [env['warden'].message]]
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,83 @@
1
+ module Warden
2
+ module GitHub
3
+ module Rails
4
+ module Routes
5
+ # Enforces an authenticated GitHub user for the routes. If not
6
+ # authenticated, it initiates the OAuth flow.
7
+ #
8
+ # Team and organization memberships can be checked by specifying a hash
9
+ # such as `:team => 'foobar'` or `:org => 'my_company'`.
10
+ def github_authenticate(scope=nil, options={}, &routes_block)
11
+ github_constraint(scope, options, routes_block) do |warden, scope|
12
+ warden.authenticate!(:scope => scope)
13
+ end
14
+ end
15
+
16
+ # The routes will only be visible to authenticated GitHub users. When
17
+ # not authenticated, it does not initiate the OAuth flow.
18
+ #
19
+ # Team and organization memberships can be checked by specifying a hash
20
+ # such as `:team => 'foobar'` or `:org => 'my_company'`.
21
+ def github_authenticated(scope=nil, options={}, &routes_block)
22
+ github_constraint(scope, options, routes_block) do |warden, scope|
23
+ warden.authenticated?(:scope => scope)
24
+ end
25
+ end
26
+
27
+ # The routes will only be visible to all but authenticated GitHub users.
28
+ #
29
+ # This constraint currently does not check for memberships since of its
30
+ # limited usage.
31
+ def github_unauthenticated(scope=nil, options={}, &routes_block)
32
+ github_constraint(scope, options, routes_block) do |warden, scope|
33
+ not warden.authenticated?(:scope => scope)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def github_constraint(scope, options, routes_block, &block)
40
+ options, scope = scope, nil if scope.is_a? Hash
41
+ scope ||= Rails.default_scope
42
+
43
+ constraint = lambda do |request|
44
+ warden = request.env['warden']
45
+
46
+ if block.call(warden, scope)
47
+ if (user = warden.user(scope))
48
+ evaled_options = github_eval_options(options, request)
49
+ github_enforce_options(user, evaled_options)
50
+ else
51
+ true
52
+ end
53
+ end
54
+ end
55
+
56
+ constraints(constraint, &routes_block)
57
+ end
58
+
59
+ def github_enforce_options(user, options)
60
+ if (team = options[:team])
61
+ user.team_member?(Rails.team_id(team))
62
+ elsif (org = options[:org] || options[:organization])
63
+ user.organization_member?(org)
64
+ else
65
+ true
66
+ end
67
+ end
68
+
69
+ def github_eval_options(options, request)
70
+ Hash[options.map { |k,v|
71
+ if v.is_a?(Proc)
72
+ [k, v.call(request)]
73
+ else
74
+ [k, v]
75
+ end
76
+ }]
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ ActionDispatch::Routing::Mapper.send(:include, Warden::GitHub::Rails::Routes)
@@ -0,0 +1,28 @@
1
+ require 'warden/github'
2
+ require 'warden/github/rails/test_helpers/mock_user'
3
+
4
+ module Warden
5
+ module GitHub
6
+ module Rails
7
+ module TestHelpers
8
+ include ::Warden::Test::Helpers
9
+
10
+ # Login a mock GitHub user and return it.
11
+ def github_login(scope=Rails.default_scope)
12
+ MockUser.new.tap do |user|
13
+ login_as(user, :scope => scope)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ # Add a method to Rack::Response to easily determine if a request resulted in an
22
+ # OAuth redirect to GitHub.
23
+ class Rack::Response
24
+ def github_oauth_redirect?
25
+ redirect? and
26
+ location.start_with?('https://github.com/login/oauth/authorize')
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ module Warden
2
+ module GitHub
3
+ module Rails
4
+ module TestHelpers
5
+ class MockUser < User
6
+ attr_reader :memberships
7
+
8
+ def initialize(*args)
9
+ super
10
+ @memberships = { :team => [], :org => [] }
11
+ end
12
+
13
+ def stub_membership(args)
14
+ args.each do |type, values|
15
+ memberships.fetch(type).concat(Array(values))
16
+ end
17
+ end
18
+
19
+ def team_member?(id)
20
+ memberships[:team].include?(id)
21
+ end
22
+
23
+ def organization_member?(id)
24
+ memberships[:org].include?(id)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,9 @@
1
+ module Warden
2
+ module GitHub
3
+ module Rails
4
+ VERSION = File.read(
5
+ File.expand_path('../../../../../VERSION', __FILE__)
6
+ ).strip
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'controller helpers' do
4
+ {
5
+ :scoped => :admin,
6
+ :unscoped => Warden::GitHub::Rails.default_scope
7
+ }.each do |type, scope|
8
+ context "when using #{type}" do
9
+ describe '#github_authenticate!' do
10
+ subject(:request) { get "/#{type}/authenticate" }
11
+
12
+ context 'when not logged in' do
13
+ it 'initiates the oauth flow' do
14
+ expect(request).to be_github_oauth_redirect
15
+ end
16
+ end
17
+
18
+ context 'when logged in' do
19
+ before { github_login(scope) }
20
+ it 'does nothing' do
21
+ expect(request).to be_ok
22
+ end
23
+ end
24
+ end
25
+
26
+ describe '#github_logout' do
27
+ context 'when not logged in' do
28
+ it 'does nothing' do
29
+ expect(get("/#{type}/logout").body).to eq('false')
30
+ end
31
+ end
32
+
33
+ context 'when logged in' do
34
+ it 'logs out the user' do
35
+ github_login(scope)
36
+ expect(get("/#{type}/logout").body).to eq('true')
37
+ expect(get("/#{type}/logout").body).to eq('false')
38
+ end
39
+ end
40
+ end
41
+
42
+ describe '#github_authenticated?' do
43
+ subject(:request) { get "/#{type}/authenticated" }
44
+
45
+ context 'when not logged in' do
46
+ it 'returns false' do
47
+ expect(request.body).to eq('false')
48
+ end
49
+ end
50
+
51
+ context 'when logged in' do
52
+ it 'returns true' do
53
+ github_login(scope)
54
+ expect(request.body).to eq('true')
55
+ end
56
+ end
57
+ end
58
+
59
+ describe '#github_user' do
60
+ subject(:request) { get "/#{type}/user" }
61
+
62
+ context 'when not logged in' do
63
+ it 'returns nil' do
64
+ expect(request.body).to be_blank
65
+ end
66
+ end
67
+
68
+ context 'when logged in' do
69
+ it 'returns the logged in user' do
70
+ github_login(scope)
71
+ expect(request.body).to \
72
+ include('Warden::GitHub::Rails::TestHelpers::MockUser')
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '#github_session' do
78
+ subject(:request) { get "/#{type}/session" }
79
+
80
+ context 'when not logged in' do
81
+ it 'should be nil' do
82
+ expect(request.body).to be_blank
83
+ end
84
+ end
85
+
86
+ context 'when logged in' do
87
+ it "returns the user's session" do
88
+ github_login(scope)
89
+ expect(request.body).to eq({ :foo => :bar }.to_s)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,183 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'request to a protected resource' do
4
+ context 'that requires a team membership' do
5
+ context 'which is specified by a numeric team id' do
6
+ subject { get '/team/protected' }
7
+
8
+ context 'when not logged in' do
9
+ it { should be_github_oauth_redirect }
10
+ end
11
+
12
+ context 'when logged in' do
13
+ context 'and team member' do
14
+ before do
15
+ user = github_login
16
+ user.stub_membership(:team => 123)
17
+ end
18
+
19
+ it { should be_ok }
20
+ end
21
+
22
+ context 'and not team member' do
23
+ before { github_login }
24
+ it { should be_not_found}
25
+ end
26
+ end
27
+ end
28
+
29
+ context 'which is specified by a team alias' do
30
+ subject { get '/team_alias/protected' }
31
+
32
+ context 'when not logged in' do
33
+ it { should be_github_oauth_redirect }
34
+ end
35
+
36
+ context 'when logged in' do
37
+ context 'and team member' do
38
+ before do
39
+ user = github_login
40
+ user.stub_membership(:team => 456)
41
+ end
42
+
43
+ it { should be_ok }
44
+ end
45
+
46
+ context 'and not team member' do
47
+ before { github_login }
48
+ it { should be_not_found}
49
+ end
50
+ end
51
+ end
52
+
53
+ context 'which is specified by a lambda' do
54
+ subject { get '/dynamic_team/123' }
55
+
56
+ context 'when logged in' do
57
+ context 'and team member' do
58
+ before do
59
+ user = github_login
60
+ user.stub_membership(:team => 123)
61
+ end
62
+
63
+ it { should be_ok }
64
+ end
65
+
66
+ context 'and not team member' do
67
+ before { github_login }
68
+ it { should be_not_found}
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ context 'that requires an organization membership' do
75
+ { :org => :foobar_inc, :organization => 'some_org' }.each do |key, value|
76
+ context "which is specified as #{key}" do
77
+ subject { get "/#{key}/protected" }
78
+
79
+ context 'when not logged in' do
80
+ it { should be_github_oauth_redirect }
81
+ end
82
+
83
+ context 'when logged in' do
84
+ context 'and organization member' do
85
+ before do
86
+ user = github_login
87
+ user.stub_membership(:org => value)
88
+ end
89
+
90
+ it { should be_ok }
91
+ end
92
+
93
+ context 'and not organization member' do
94
+ before { github_login }
95
+ it { should be_not_found }
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ context 'which is specified by a lambda' do
102
+ subject { get '/dynamic_org/some_org' }
103
+
104
+ context 'when logged in' do
105
+ context 'and organization member' do
106
+ before do
107
+ user = github_login
108
+ user.stub_membership(:org => 'some_org')
109
+ end
110
+
111
+ it { should be_ok }
112
+ end
113
+
114
+ context 'and not organization member' do
115
+ before { github_login }
116
+ it { should be_not_found}
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ describe 'request to a resource that only exists when logged in' do
124
+ context 'that requires a team membership' do
125
+ context 'which is specified by a numeric team id' do
126
+ subject { get '/team/conditional' }
127
+
128
+ context 'when team member' do
129
+ before do
130
+ user = github_login
131
+ user.stub_membership(:team => 123)
132
+ end
133
+
134
+ it { should be_ok }
135
+ end
136
+
137
+ context 'when not team member' do
138
+ before { github_login }
139
+ it { should be_not_found}
140
+ end
141
+ end
142
+
143
+ context 'which is specified by a team alias' do
144
+ subject { get '/team_alias/conditional' }
145
+
146
+ context 'when team member' do
147
+ before do
148
+ user = github_login
149
+ user.stub_membership(:team => 456)
150
+ end
151
+
152
+ it { should be_ok }
153
+ end
154
+
155
+ context 'when not team member' do
156
+ before { github_login }
157
+ it { should be_not_found}
158
+ end
159
+ end
160
+ end
161
+
162
+ context 'that requires an organization membership' do
163
+ { :org => :foobar_inc, :organization => 'some_org' }.each do |key, value|
164
+ context "which is specified as #{key}" do
165
+ subject { get "/#{key}/conditional" }
166
+
167
+ context 'when organization member' do
168
+ before do
169
+ user = github_login
170
+ user.stub_membership(:org => value)
171
+ end
172
+
173
+ it { should be_ok }
174
+ end
175
+
176
+ context 'when not organization member' do
177
+ before { github_login }
178
+ it { should be_not_found }
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end