privy_wine_bouncer 1.0.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +23 -0
  5. data/.rubocop_todo.yml +182 -0
  6. data/.travis.yml +124 -0
  7. data/CHANGELOG.md +60 -0
  8. data/CONTRIBUTING.md +55 -0
  9. data/Gemfile +21 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +238 -0
  12. data/Rakefile +11 -0
  13. data/UPGRADING.md +62 -0
  14. data/lib/generators/templates/wine_bouncer.rb +9 -0
  15. data/lib/generators/wine_bouncer/initializer_generator.rb +14 -0
  16. data/lib/privy_wine_bouncer.rb +3 -0
  17. data/lib/wine_bouncer/auth_methods/auth_methods.rb +38 -0
  18. data/lib/wine_bouncer/auth_strategies/default.rb +27 -0
  19. data/lib/wine_bouncer/auth_strategies/protected.rb +43 -0
  20. data/lib/wine_bouncer/auth_strategies/swagger.rb +33 -0
  21. data/lib/wine_bouncer/base_strategy.rb +7 -0
  22. data/lib/wine_bouncer/configuration.rb +70 -0
  23. data/lib/wine_bouncer/errors.rb +23 -0
  24. data/lib/wine_bouncer/extension.rb +24 -0
  25. data/lib/wine_bouncer/oauth2.rb +106 -0
  26. data/lib/wine_bouncer/version.rb +5 -0
  27. data/lib/wine_bouncer.rb +14 -0
  28. data/spec/dummy/README.rdoc +28 -0
  29. data/spec/dummy/Rakefile +6 -0
  30. data/spec/dummy/app/api/default_api.rb +71 -0
  31. data/spec/dummy/app/api/protected_api.rb +66 -0
  32. data/spec/dummy/app/api/swagger_api.rb +61 -0
  33. data/spec/dummy/app/assets/config/manifest.js +1 -0
  34. data/spec/dummy/app/assets/images/.keep +0 -0
  35. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  36. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  37. data/spec/dummy/app/controllers/application_controller.rb +7 -0
  38. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  39. data/spec/dummy/app/helpers/application_helper.rb +4 -0
  40. data/spec/dummy/app/mailers/.keep +0 -0
  41. data/spec/dummy/app/models/.keep +0 -0
  42. data/spec/dummy/app/models/concerns/.keep +0 -0
  43. data/spec/dummy/app/models/user.rb +4 -0
  44. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  45. data/spec/dummy/bin/bundle +3 -0
  46. data/spec/dummy/bin/rails +4 -0
  47. data/spec/dummy/bin/rake +4 -0
  48. data/spec/dummy/config/application.rb +31 -0
  49. data/spec/dummy/config/boot.rb +7 -0
  50. data/spec/dummy/config/database.yml +25 -0
  51. data/spec/dummy/config/environment.rb +7 -0
  52. data/spec/dummy/config/environments/development.rb +39 -0
  53. data/spec/dummy/config/environments/production.rb +80 -0
  54. data/spec/dummy/config/environments/test.rb +43 -0
  55. data/spec/dummy/config/initializers/assets.rb +10 -0
  56. data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
  57. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  58. data/spec/dummy/config/initializers/doorkeeper.rb +94 -0
  59. data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  60. data/spec/dummy/config/initializers/inflections.rb +18 -0
  61. data/spec/dummy/config/initializers/mime_types.rb +6 -0
  62. data/spec/dummy/config/initializers/secret_token.rb +6 -0
  63. data/spec/dummy/config/initializers/session_store.rb +5 -0
  64. data/spec/dummy/config/initializers/wrap_parameters.rb +16 -0
  65. data/spec/dummy/config/locales/doorkeeper.en.yml +71 -0
  66. data/spec/dummy/config/locales/en.yml +23 -0
  67. data/spec/dummy/config/routes.rb +8 -0
  68. data/spec/dummy/config/secrets.yml +22 -0
  69. data/spec/dummy/config.ru +4 -0
  70. data/spec/dummy/db/migrate/20140915153344_create_users.rb +11 -0
  71. data/spec/dummy/db/migrate/20140915160601_create_doorkeeper_tables.rb +43 -0
  72. data/spec/dummy/db/schema.rb +62 -0
  73. data/spec/dummy/lib/assets/.keep +0 -0
  74. data/spec/dummy/log/.keep +0 -0
  75. data/spec/dummy/public/404.html +67 -0
  76. data/spec/dummy/public/422.html +67 -0
  77. data/spec/dummy/public/500.html +66 -0
  78. data/spec/dummy/public/favicon.ico +0 -0
  79. data/spec/factories/access_token.rb +13 -0
  80. data/spec/factories/application.rb +8 -0
  81. data/spec/factories/user.rb +7 -0
  82. data/spec/intergration/oauth2_default_strategy_spec.rb +189 -0
  83. data/spec/intergration/oauth2_protected_strategy_spec.rb +199 -0
  84. data/spec/intergration/oauth2_swagger_strategy_spec.rb +156 -0
  85. data/spec/lib/generators/wine_bouncer/initializer_generator_spec.rb +19 -0
  86. data/spec/lib/wine_bouncer/auth_methods/auth_methods_spec.rb +105 -0
  87. data/spec/lib/wine_bouncer/auth_strategies/default_spec.rb +76 -0
  88. data/spec/lib/wine_bouncer/auth_strategies/swagger_spec.rb +115 -0
  89. data/spec/rails_helper.rb +79 -0
  90. data/spec/shared/orm/active_record.rb +4 -0
  91. data/spec/spec_helper.rb +95 -0
  92. data/wine_bouncer.gemspec +33 -0
  93. metadata +386 -0
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/500.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>We're sorry, but something went wrong.</h1>
62
+ </div>
63
+ <p>If you are the application owner check the logs for more information.</p>
64
+ </div>
65
+ </body>
66
+ </html>
File without changes
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :access_token, class: Doorkeeper::AccessToken do
5
+ sequence(:resource_owner_id) { |n| n }
6
+ application
7
+ expires_in { 2.hours }
8
+
9
+ factory :clientless_access_token do
10
+ application { nil }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :application, class: Doorkeeper::Application do
5
+ sequence(:name) { |n| "Application #{n}" }
6
+ redirect_uri { 'https://app.com/callback' }
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :user do
5
+ sequence(:name) { |n| "user-#{n}" }
6
+ end
7
+ end
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+ require 'json'
5
+
6
+ describe Api::MountedDefaultApiUnderTest, type: :api do
7
+ let(:user) { FactoryBot.create :user }
8
+ let(:token) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'public' }
9
+ let(:unscoped_token) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: '' }
10
+ let(:custom_scope) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'custom_scope' } #not a default scope
11
+
12
+ before(:example) do
13
+ WineBouncer.configure do |c|
14
+ c.auth_strategy = :default
15
+
16
+ c.define_resource_owner do
17
+ User.find(doorkeeper_access_token.resource_owner_id) if doorkeeper_access_token
18
+ end
19
+ end
20
+ end
21
+
22
+ context 'tokens and scopes' do
23
+ it 'gives access when the token and scope are correct' do
24
+ get '/default_api/protected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
25
+
26
+ expect(last_response.status).to eq(200)
27
+ json = JSON.parse(last_response.body)
28
+ expect(json).to have_key('hello')
29
+ end
30
+
31
+ it 'gives access when tokens are correct and an non doorkeeper default scope is used.' do
32
+ get '/default_api/oauth2_custom_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{custom_scope.token}"
33
+
34
+ expect(last_response.status).to eq(200)
35
+ json = JSON.parse(last_response.body)
36
+ expect(json).to have_key('hello')
37
+ expect(json['hello']).to eq('oauth2_custom_scope')
38
+ end
39
+
40
+ it 'raises an authentication error when the token is invalid' do
41
+ expect { get '/default_api/protected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}-invalid" }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
42
+ end
43
+
44
+ it 'raises an oauth authentication error when no token is given' do
45
+ expect { get '/default_api/protected' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
46
+ end
47
+
48
+ it 'raises an auth forbidden authentication error when the user scope is not correct' do
49
+ expect { get '/default_api/protected_with_private_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError)
50
+ end
51
+ end
52
+
53
+ context 'unprotected endpoint' do
54
+ it 'allows to call an unprotected endpoint without token' do
55
+ get '/default_api/unprotected'
56
+
57
+ expect(last_response.status).to eq(200)
58
+ json = JSON.parse(last_response.body)
59
+
60
+ expect(json).to have_key('hello')
61
+ expect(json['hello']).to eq('unprotected world')
62
+ end
63
+
64
+ it 'allows to call an unprotected endpoint with token' do
65
+ get '/default_api/unprotected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
66
+
67
+ expect(last_response.status).to eq(200)
68
+ json = JSON.parse(last_response.body)
69
+ expect(json).to have_key('hello')
70
+ expect(json['hello']).to eq('unprotected world')
71
+ end
72
+ end
73
+
74
+ context 'protected_without_scopes' do
75
+ it 'allows to call an protected endpoint without scopes' do
76
+ get '/default_api/protected_without_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
77
+
78
+ expect(last_response.status).to eq(200)
79
+ json = JSON.parse(last_response.body)
80
+ expect(json).to have_key('hello')
81
+ expect(json['hello']).to eq('protected unscoped world')
82
+ end
83
+
84
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
85
+ expect { get '/default_api/protected_without_scope' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
86
+ end
87
+
88
+ it 'raises an error because the user does not have the default scope' do
89
+ expect { get '/default_api/protected_without_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{unscoped_token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError)
90
+ end
91
+ end
92
+
93
+ context 'oauth2_dsl' do
94
+ it 'allows to call an protected endpoint without scopes' do
95
+ get '/default_api/oauth2_dsl', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
96
+
97
+ expect(last_response.status).to eq(200)
98
+ json = JSON.parse(last_response.body)
99
+ expect(json).to have_key('hello')
100
+ expect(json['hello']).to eq('oauth2 dsl')
101
+ end
102
+
103
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
104
+ expect { get '/default_api/oauth2_dsl' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
105
+ end
106
+
107
+ context 'without parameters' do
108
+ it 'accepts tokens with default scopes' do
109
+ get '/swagger_api/oauth2_dsl_default_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
110
+ expect(last_response.status).to eq(200)
111
+ json = JSON.parse(last_response.body)
112
+ expect(json).to have_key('hello')
113
+ expect(json['hello']).to eq('oauth dsl default scopes')
114
+ end
115
+
116
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
117
+ expect { get '/default_api/oauth2_dsl_default_scopes' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
118
+ end
119
+
120
+ it 'raises an error when token scopes are not default scopes ' do
121
+ expect { get '/default_api/oauth2_dsl_default_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{custom_scope.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError)
122
+ end
123
+ end
124
+
125
+ context 'custom scopes' do
126
+ it 'allows to call custom scopes' do
127
+ get '/default_api/oauth2_dsl_custom_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{custom_scope.token}"
128
+ expect(last_response.status).to eq(200)
129
+ json = JSON.parse(last_response.body)
130
+ expect(json).to have_key('hello')
131
+ expect(json['hello']).to eq('oauth2 dsl custom scope')
132
+ end
133
+
134
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
135
+ expect { get '/default_api/oauth2_dsl_custom_scope' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
136
+ end
137
+
138
+ it 'raises an error when token scopes do not match' do
139
+ expect { get '/default_api/oauth2_dsl_custom_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError)
140
+ end
141
+ end
142
+
143
+ context 'oauth2_dsl_multiple_scopes' do
144
+ it 'allows call on the first scope' do
145
+ scope_token = FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'multiple'
146
+ get '/default_api/oauth2_dsl_multiple_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{scope_token.token}"
147
+ expect(last_response.status).to eq(200)
148
+ json = JSON.parse(last_response.body)
149
+ expect(json).to have_key('hello')
150
+ expect(json['hello']).to eq('oauth2 dsl multiple scopes')
151
+ end
152
+
153
+ it 'allows call on the second scope' do
154
+ scope_token = FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'scopes'
155
+ get '/default_api/oauth2_dsl_multiple_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{scope_token.token}"
156
+ expect(last_response.status).to eq(200)
157
+ json = JSON.parse(last_response.body)
158
+ expect(json).to have_key('hello')
159
+ expect(json['hello']).to eq('oauth2 dsl multiple scopes')
160
+ end
161
+
162
+ it 'raises an error scope does not match any of the scopes' do
163
+ expect { get '/default_api/oauth2_dsl_multiple_scopes' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
164
+ end
165
+ end
166
+ end
167
+
168
+ context 'not_described_world' do
169
+ it 'allows to call an endpoint without description' do
170
+ get '/default_api/not_described_world'
171
+ expect(last_response.status).to eq(200)
172
+ json = JSON.parse(last_response.body)
173
+ expect(json).to have_key('hello')
174
+ expect(json['hello']).to eq('non described world')
175
+ end
176
+ end
177
+
178
+ context 'resource_owner' do
179
+ it 'is available in the endpoint' do
180
+ get '/default_api/protected_user', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
181
+
182
+ expect(last_response.status).to eq(200)
183
+ json = JSON.parse(last_response.body)
184
+
185
+ expect(json).to have_key('hello')
186
+ expect(json['hello']).to eq(user.name)
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+ require 'json'
5
+
6
+ describe Api::MountedProtectedApiUnderTest, type: :api do
7
+ let(:user) { FactoryBot.create :user }
8
+ let(:token) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'public' }
9
+ let(:unscoped_token) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: '' }
10
+ let(:custom_scope) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'custom_scope' } #not a default scope
11
+
12
+ before(:example) do
13
+ WineBouncer.configure do |c|
14
+ c.auth_strategy = :protected
15
+
16
+ c.define_resource_owner do
17
+ User.find(doorkeeper_access_token.resource_owner_id) if doorkeeper_access_token
18
+ end
19
+ end
20
+ end
21
+
22
+ context 'when WineBouncer is disabled' do
23
+ before :all do
24
+ WineBouncer.configure do |c|
25
+ c.disable { true }
26
+ end
27
+ end
28
+
29
+ after :all do
30
+ WineBouncer.configure do |c|
31
+ c.disable { false }
32
+ end
33
+ end
34
+
35
+ it 'allows request to protected resource without token' do
36
+ get '/protected_api/protected'
37
+ expect(last_response.status).to eq(200)
38
+ json = JSON.parse(last_response.body)
39
+ expect(json).to have_key('hello')
40
+ end
41
+ end
42
+
43
+ context 'tokens and scopes' do
44
+ it 'gives access when the token and scope are correct' do
45
+ get '/protected_api/protected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
46
+
47
+ expect(last_response.status).to eq(200)
48
+ json = JSON.parse(last_response.body)
49
+ expect(json).to have_key('hello')
50
+ end
51
+
52
+ it 'raises an authentication error when the token is invalid' do
53
+ expect { get '/protected_api/protected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}-invalid" }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
54
+ end
55
+
56
+ it 'raises an oauth authentication error when no token is given' do
57
+ expect { get '/protected_api/protected' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
58
+ end
59
+
60
+ it 'raises an auth forbidden authentication error when the user scope is not correct' do
61
+ expect { get '/protected_api/protected_with_private_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError)
62
+ end
63
+ end
64
+
65
+ context 'unprotected endpoint' do
66
+ it 'allows to call an unprotected endpoint without token' do
67
+ get '/protected_api/unprotected'
68
+
69
+ expect(last_response.status).to eq(200)
70
+ json = JSON.parse(last_response.body)
71
+
72
+ expect(json).to have_key('hello')
73
+ expect(json['hello']).to eq('unprotected world')
74
+ end
75
+
76
+ it 'allows to call an unprotected endpoint with token' do
77
+ get '/protected_api/unprotected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
78
+
79
+ expect(last_response.status).to eq(200)
80
+ json = JSON.parse(last_response.body)
81
+ expect(json).to have_key('hello')
82
+ expect(json['hello']).to eq('unprotected world')
83
+ end
84
+ end
85
+
86
+ context 'protected_without_scopes' do
87
+ it 'does not allow an unauthenticated user to call a protected endpoint' do
88
+ expect { get '/protected_api/protected_without_scope' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
89
+ end
90
+
91
+ it 'allows to call an protected endpoint without scopes' do
92
+ get '/protected_api/protected_without_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
93
+
94
+ expect(last_response.status).to eq(200)
95
+ json = JSON.parse(last_response.body)
96
+ expect(json).to have_key('hello')
97
+ expect(json['hello']).to eq('protected unscoped world')
98
+ end
99
+
100
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
101
+ expect { get '/protected_api/protected_without_scope' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
102
+ end
103
+
104
+ it 'raises an error because the user does not have the default scope' do
105
+ expect { get '/protected_api/protected_without_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{unscoped_token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError)
106
+ end
107
+ end
108
+
109
+ context 'oauth2_dsl' do
110
+ it 'allows to call an protected endpoint without scopes' do
111
+ get '/protected_api/oauth2_dsl', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
112
+
113
+ expect(last_response.status).to eq(200)
114
+ json = JSON.parse(last_response.body)
115
+ expect(json).to have_key('hello')
116
+ expect(json['hello']).to eq('oauth2_dsl')
117
+ end
118
+
119
+ it 'raises an error when an protected endpoint is called without token' do
120
+ expect { get '/protected_api/oauth2_dsl' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
121
+ end
122
+
123
+ context 'without parameters' do
124
+ it 'allows to call an endpoint with default scopes' do
125
+ get '/protected_api/oauth2_protected_with_default_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
126
+ expect(last_response.status).to eq(200)
127
+ json = JSON.parse(last_response.body)
128
+ expect(json).to have_key('hello')
129
+ expect(json['hello']).to eq('default oauth2 dsl')
130
+ end
131
+
132
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
133
+ expect { get '/protected_api/oauth2_protected_with_default_scopes' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
134
+ end
135
+
136
+ it 'raises an error when token scopes are not default scopes ' do
137
+ expect { get '/protected_api/oauth2_protected_with_default_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{custom_scope.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError)
138
+ end
139
+ end
140
+
141
+ context 'custom scopes' do
142
+ it 'protects endpoints with custom scopes' do
143
+ get '/protected_api/oauth2_dsl_custom_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{custom_scope.token}"
144
+ expect(last_response.status).to eq(200)
145
+ json = JSON.parse(last_response.body)
146
+ expect(json).to have_key('hello')
147
+ expect(json['hello']).to eq('custom scope')
148
+ end
149
+
150
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
151
+ expect { get '/protected_api/oauth2_dsl_custom_scope' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
152
+ end
153
+
154
+ it 'raises an error when token scopes do not match' do
155
+ expect { get '/protected_api/oauth2_dsl_custom_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError)
156
+ end
157
+ end
158
+
159
+ context 'public endpoint' do
160
+ it 'allows to call an unprotected endpoint without token' do
161
+ get '/protected_api/unprotected_endpoint'
162
+ expect(last_response.status).to eq(200)
163
+ json = JSON.parse(last_response.body)
164
+ expect(json).to have_key('hello')
165
+ expect(json['hello']).to eq('public oauth2 dsl')
166
+ end
167
+
168
+ it 'allows requests with tokens to public endpoints with tokens' do
169
+ expect { get '/protected_api/oauth2_protected_with_default_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" }.not_to raise_error
170
+ end
171
+ end
172
+ end
173
+
174
+ context 'not_described_world' do
175
+ it 'authentication is required for a non described endpoint' do
176
+ get '/protected_api/not_described_world', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
177
+ expect(last_response.status).to eq(200)
178
+ json = JSON.parse(last_response.body)
179
+ expect(json).to have_key('hello')
180
+ expect(json['hello']).to eq('non described world')
181
+ end
182
+
183
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
184
+ expect { get '/protected_api/not_described_world' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
185
+ end
186
+ end
187
+
188
+ context 'resource_owner' do
189
+ it 'is available in the endpoint' do
190
+ get '/protected_api/protected_user', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
191
+
192
+ expect(last_response.status).to eq(200)
193
+ json = JSON.parse(last_response.body)
194
+
195
+ expect(json).to have_key('hello')
196
+ expect(json['hello']).to eq(user.name)
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+ require 'json'
5
+
6
+ describe Api::MountedSwaggerApiUnderTest, type: :api do
7
+ let(:user) { FactoryBot.create :user }
8
+ let(:token) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'public' }
9
+ let(:unscoped_token) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: '' }
10
+ let(:custom_scope) { FactoryBot.create :clientless_access_token, resource_owner_id: user.id, scopes: 'custom_scope' } #not a default scope
11
+
12
+ before(:example) do
13
+ WineBouncer.configure do |c|
14
+ c.auth_strategy = :swagger
15
+
16
+ c.define_resource_owner do
17
+ User.find(doorkeeper_access_token.resource_owner_id) if doorkeeper_access_token
18
+ end
19
+ end
20
+ end
21
+
22
+ context 'tokens and scopes' do
23
+ it 'gives access when the token and scope are correct' do
24
+ get '/swagger_api/protected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
25
+
26
+ expect(last_response.status).to eq(200)
27
+ json = JSON.parse(last_response.body)
28
+ expect(json).to have_key('hello')
29
+ end
30
+
31
+ it 'raises an authentication error when the token is invalid' do
32
+ expect { get '/swagger_api/protected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}-invalid" }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
33
+ end
34
+
35
+ it 'raises an oauth authentication error when no token is given' do
36
+ expect { get '/swagger_api/protected' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
37
+ end
38
+
39
+ it 'raises an auth forbidden authentication error when the user scope is not correct' do
40
+ expect { get '/swagger_api/protected_with_private_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError)
41
+ end
42
+ end
43
+
44
+ context 'unprotected endpoint' do
45
+ it 'allows to call an unprotected endpoint without token' do
46
+ get '/swagger_api/unprotected'
47
+
48
+ expect(last_response.status).to eq(200)
49
+ json = JSON.parse(last_response.body)
50
+
51
+ expect(json).to have_key('hello')
52
+ expect(json['hello']).to eq('unprotected world')
53
+ end
54
+
55
+ it 'allows to call an unprotected endpoint with token' do
56
+ get '/swagger_api/unprotected', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
57
+
58
+ expect(last_response.status).to eq(200)
59
+ json = JSON.parse(last_response.body)
60
+ expect(json).to have_key('hello')
61
+ expect(json['hello']).to eq('unprotected world')
62
+ end
63
+ end
64
+
65
+ context 'protected_without_scopes' do
66
+ it 'allows to call an protected endpoint without scopes' do
67
+ get '/swagger_api/protected_without_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
68
+
69
+ expect(last_response.status).to eq(200)
70
+ json = JSON.parse(last_response.body)
71
+ expect(json).to have_key('hello')
72
+ expect(json['hello']).to eq('protected unscoped world')
73
+ end
74
+
75
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
76
+ expect { get '/swagger_api/protected_without_scope' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
77
+ end
78
+
79
+ it 'raises an error because the user does not have the default scope' do
80
+ expect { get '/swagger_api/protected_without_scope', nil, 'HTTP_AUTHORIZATION' => "Bearer #{unscoped_token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError)
81
+ end
82
+ end
83
+
84
+ context 'oauth2 dsl' do
85
+ it 'allows to call an protected endpoint without scopes' do
86
+ get '/swagger_api/oauth2_dsl', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
87
+
88
+ expect(last_response.status).to eq(200)
89
+ json = JSON.parse(last_response.body)
90
+ expect(json).to have_key('hello')
91
+ expect(json['hello']).to eq('oauth2_dsl')
92
+ end
93
+
94
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
95
+ expect { get '/swagger_api/oauth2_dsl' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
96
+ end
97
+
98
+ context 'without parameters' do
99
+ it 'accepts tokens with default scopes' do
100
+ get '/swagger_api/oauth2_dsl_default_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
101
+ expect(last_response.status).to eq(200)
102
+ json = JSON.parse(last_response.body)
103
+ expect(json).to have_key('hello')
104
+ expect(json['hello']).to eq('oauth dsl default scopes')
105
+ end
106
+
107
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
108
+ expect { get '/swagger_api/oauth2_dsl_default_scopes' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
109
+ end
110
+
111
+ it 'raises an error when token scopes are not default scopes ' do
112
+ expect { get '/swagger_api/oauth2_dsl_default_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{custom_scope.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError)
113
+ end
114
+ end
115
+
116
+ context 'custom scopes' do
117
+ it 'accepts tokens with default scopes' do
118
+ get '/swagger_api/oauth2_dsl_custom_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{custom_scope.token}"
119
+ expect(last_response.status).to eq(200)
120
+ json = JSON.parse(last_response.body)
121
+ expect(json).to have_key('hello')
122
+ expect(json['hello']).to eq('oauth dsl custom scopes')
123
+ end
124
+
125
+ it 'raises an error when an protected endpoint without scopes is called without token ' do
126
+ expect { get '/swagger_api/oauth2_dsl_custom_scopes' }.to raise_exception(WineBouncer::Errors::OAuthUnauthorizedError)
127
+ end
128
+
129
+ it 'raises an error when token scopes do not match' do
130
+ expect { get '/swagger_api/oauth2_dsl_custom_scopes', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}" }.to raise_exception(WineBouncer::Errors::OAuthForbiddenError)
131
+ end
132
+ end
133
+ end
134
+
135
+ context 'not_described_world' do
136
+ it 'allows to call an endpoint without description' do
137
+ get '/swagger_api/not_described_world'
138
+ expect(last_response.status).to eq(200)
139
+ json = JSON.parse(last_response.body)
140
+ expect(json).to have_key('hello')
141
+ expect(json['hello']).to eq('non described world')
142
+ end
143
+ end
144
+
145
+ context 'resource_owner' do
146
+ it 'is available in the endpoint' do
147
+ get '/swagger_api/protected_user', nil, 'HTTP_AUTHORIZATION' => "Bearer #{token.token}"
148
+
149
+ expect(last_response.status).to eq(200)
150
+ json = JSON.parse(last_response.body)
151
+
152
+ expect(json).to have_key('hello')
153
+ expect(json['hello']).to eq(user.name)
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'generator_spec'
4
+ require 'rails_helper'
5
+ require 'spec_helper'
6
+ require 'generators/wine_bouncer/initializer_generator'
7
+
8
+ describe WineBouncer::Generators::InitializerGenerator, type: :generator do
9
+ destination '/tmp/wine_bouncer'
10
+
11
+ before do
12
+ prepare_destination
13
+ run_generator
14
+ end
15
+
16
+ it 'creates a test initializer' do
17
+ assert_file 'config/initializers/wine_bouncer.rb', /WineBouncer\.configure/
18
+ end
19
+ end