warden-github-rails-thinknear-fork 1.1.0

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