doorkeeper 4.2.5 → 4.3.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 (125) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +19 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +17 -0
  4. data/.gitignore +1 -1
  5. data/.hound.yml +2 -13
  6. data/.rubocop.yml +13 -0
  7. data/.travis.yml +13 -4
  8. data/Appraisals +6 -2
  9. data/CODE_OF_CONDUCT.md +46 -0
  10. data/Gemfile +1 -1
  11. data/NEWS.md +28 -0
  12. data/README.md +40 -10
  13. data/RELEASING.md +5 -12
  14. data/SECURITY.md +13 -0
  15. data/app/controllers/doorkeeper/application_controller.rb +1 -5
  16. data/app/controllers/doorkeeper/applications_controller.rb +14 -1
  17. data/app/controllers/doorkeeper/tokens_controller.rb +13 -1
  18. data/app/helpers/doorkeeper/dashboard_helper.rb +4 -2
  19. data/app/validators/redirect_uri_validator.rb +12 -2
  20. data/app/views/doorkeeper/applications/_form.html.erb +2 -2
  21. data/app/views/doorkeeper/authorizations/new.html.erb +1 -1
  22. data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
  23. data/config/locales/en.yml +3 -5
  24. data/doorkeeper.gemspec +4 -4
  25. data/gemfiles/rails_4_2.gemfile +6 -4
  26. data/gemfiles/rails_5_0.gemfile +4 -4
  27. data/gemfiles/rails_5_1.gemfile +6 -7
  28. data/gemfiles/rails_5_2.gemfile +12 -0
  29. data/gemfiles/rails_master.gemfile +14 -0
  30. data/lib/doorkeeper/config.rb +55 -55
  31. data/lib/doorkeeper/engine.rb +3 -3
  32. data/lib/doorkeeper/errors.rb +18 -0
  33. data/lib/doorkeeper/grape/helpers.rb +13 -8
  34. data/lib/doorkeeper/helpers/controller.rb +9 -20
  35. data/lib/doorkeeper/models/access_token_mixin.rb +14 -7
  36. data/lib/doorkeeper/models/application_mixin.rb +11 -6
  37. data/lib/doorkeeper/models/concerns/expirable.rb +7 -5
  38. data/lib/doorkeeper/oauth/authorization/token.rb +22 -18
  39. data/lib/doorkeeper/oauth/authorization_code_request.rb +6 -1
  40. data/lib/doorkeeper/oauth/base_request.rb +5 -5
  41. data/lib/doorkeeper/oauth/client/credentials.rb +2 -2
  42. data/lib/doorkeeper/oauth/client.rb +2 -2
  43. data/lib/doorkeeper/oauth/error.rb +2 -2
  44. data/lib/doorkeeper/oauth/error_response.rb +1 -2
  45. data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
  46. data/lib/doorkeeper/oauth/invalid_token_response.rb +2 -3
  47. data/lib/doorkeeper/oauth/password_access_token_request.rb +1 -0
  48. data/lib/doorkeeper/oauth/refresh_token_request.rb +1 -0
  49. data/lib/doorkeeper/oauth/scopes.rb +18 -8
  50. data/lib/doorkeeper/oauth/token.rb +1 -1
  51. data/lib/doorkeeper/oauth/token_introspection.rb +128 -0
  52. data/lib/doorkeeper/orm/active_record/access_grant.rb +1 -1
  53. data/lib/doorkeeper/orm/active_record/access_token.rb +1 -23
  54. data/lib/doorkeeper/orm/active_record/application.rb +1 -1
  55. data/lib/doorkeeper/orm/active_record/base_record.rb +11 -0
  56. data/lib/doorkeeper/orm/active_record.rb +20 -8
  57. data/lib/doorkeeper/rails/helpers.rb +5 -6
  58. data/lib/doorkeeper/rails/routes.rb +9 -7
  59. data/lib/doorkeeper/request.rb +7 -1
  60. data/lib/doorkeeper/validations.rb +3 -2
  61. data/lib/doorkeeper/version.rb +13 -1
  62. data/lib/doorkeeper.rb +1 -0
  63. data/lib/generators/doorkeeper/application_owner_generator.rb +11 -2
  64. data/lib/generators/doorkeeper/migration_generator.rb +13 -1
  65. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +7 -1
  66. data/lib/generators/doorkeeper/templates/{add_owner_to_application_migration.rb → add_owner_to_application_migration.rb.erb} +1 -1
  67. data/lib/generators/doorkeeper/templates/{add_previous_refresh_token_to_access_tokens.rb → add_previous_refresh_token_to_access_tokens.rb.erb} +1 -1
  68. data/lib/generators/doorkeeper/templates/initializer.rb +19 -3
  69. data/lib/generators/doorkeeper/templates/{migration.rb → migration.rb.erb} +1 -1
  70. data/spec/controllers/applications_controller_spec.rb +15 -4
  71. data/spec/controllers/authorizations_controller_spec.rb +31 -16
  72. data/spec/controllers/protected_resources_controller_spec.rb +28 -19
  73. data/spec/controllers/token_info_controller_spec.rb +17 -13
  74. data/spec/controllers/tokens_controller_spec.rb +138 -4
  75. data/spec/dummy/config/initializers/doorkeeper.rb +1 -1
  76. data/spec/dummy/config/initializers/{active_record_belongs_to_required_by_default.rb → new_framework_defaults.rb} +1 -1
  77. data/spec/dummy/config/initializers/secret_token.rb +0 -1
  78. data/spec/factories.rb +1 -1
  79. data/spec/generators/application_owner_generator_spec.rb +24 -5
  80. data/spec/generators/migration_generator_spec.rb +24 -3
  81. data/spec/generators/previous_refresh_token_generator_spec.rb +57 -0
  82. data/spec/grape/grape_integration_spec.rb +135 -0
  83. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
  84. data/spec/lib/config_spec.rb +115 -12
  85. data/spec/lib/models/expirable_spec.rb +0 -1
  86. data/spec/lib/models/revocable_spec.rb +2 -2
  87. data/spec/lib/oauth/authorization_code_request_spec.rb +39 -11
  88. data/spec/lib/oauth/base_request_spec.rb +2 -7
  89. data/spec/lib/oauth/client_credentials/creator_spec.rb +1 -1
  90. data/spec/lib/oauth/client_credentials_integration_spec.rb +1 -1
  91. data/spec/lib/oauth/client_credentials_request_spec.rb +1 -0
  92. data/spec/lib/oauth/code_request_spec.rb +1 -3
  93. data/spec/lib/oauth/helpers/uri_checker_spec.rb +5 -0
  94. data/spec/lib/oauth/invalid_token_response_spec.rb +1 -1
  95. data/spec/lib/oauth/password_access_token_request_spec.rb +9 -3
  96. data/spec/lib/oauth/refresh_token_request_spec.rb +19 -7
  97. data/spec/lib/oauth/scopes_spec.rb +28 -1
  98. data/spec/lib/oauth/token_request_spec.rb +6 -8
  99. data/spec/lib/server_spec.rb +10 -0
  100. data/spec/models/doorkeeper/access_grant_spec.rb +1 -1
  101. data/spec/models/doorkeeper/access_token_spec.rb +72 -48
  102. data/spec/models/doorkeeper/application_spec.rb +51 -18
  103. data/spec/requests/applications/applications_request_spec.rb +5 -5
  104. data/spec/requests/endpoints/token_spec.rb +8 -1
  105. data/spec/requests/flows/authorization_code_errors_spec.rb +11 -1
  106. data/spec/requests/flows/authorization_code_spec.rb +1 -0
  107. data/spec/requests/flows/client_credentials_spec.rb +1 -1
  108. data/spec/requests/flows/implicit_grant_errors_spec.rb +2 -2
  109. data/spec/requests/flows/refresh_token_spec.rb +4 -4
  110. data/spec/requests/flows/revoke_token_spec.rb +15 -15
  111. data/spec/requests/protected_resources/metal_spec.rb +1 -1
  112. data/spec/requests/protected_resources/private_api_spec.rb +1 -1
  113. data/spec/routing/custom_controller_routes_spec.rb +4 -0
  114. data/spec/routing/default_routes_spec.rb +5 -1
  115. data/spec/spec_helper_integration.rb +15 -5
  116. data/spec/support/dependencies/factory_girl.rb +2 -2
  117. data/spec/support/helpers/access_token_request_helper.rb +1 -1
  118. data/spec/support/helpers/model_helper.rb +9 -4
  119. data/spec/support/helpers/request_spec_helper.rb +7 -3
  120. data/spec/support/helpers/url_helper.rb +8 -8
  121. data/spec/support/shared/controllers_shared_context.rb +2 -6
  122. data/spec/support/shared/models_shared_examples.rb +4 -4
  123. data/spec/validators/redirect_uri_validator_spec.rb +51 -6
  124. data/spec/version/version_spec.rb +15 -0
  125. metadata +42 -27
@@ -60,13 +60,15 @@ Doorkeeper.configure do
60
60
  # Change the way client credentials are retrieved from the request object.
61
61
  # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
62
62
  # falls back to the `:client_id` and `:client_secret` params from the `params` object.
63
- # Check out the wiki for more information on customization
63
+ # Check out https://github.com/doorkeeper-gem/doorkeeper/wiki/Changing-how-clients-are-authenticated
64
+ # for more information on customization
64
65
  # client_credentials :from_basic, :from_params
65
66
 
66
67
  # Change the way access token is authenticated from the request object.
67
68
  # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
68
69
  # falls back to the `:access_token` or `:bearer_token` params from the `params` object.
69
- # Check out the wiki for more information on customization
70
+ # Check out https://github.com/doorkeeper-gem/doorkeeper/wiki/Changing-how-clients-are-authenticated
71
+ # for more information on customization
70
72
  # access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param
71
73
 
72
74
  # Change the native redirect uri for client apps
@@ -80,7 +82,21 @@ Doorkeeper.configure do
80
82
  # by default in non-development environments). OAuth2 delegates security in
81
83
  # communication to the HTTPS protocol so it is wise to keep this enabled.
82
84
  #
85
+ # Callable objects such as proc, lambda, block or any object that responds to
86
+ # #call can be used in order to allow conditional checks (to allow non-SSL
87
+ # redirects to localhost for example).
88
+ #
83
89
  # force_ssl_in_redirect_uri !Rails.env.development?
90
+ #
91
+ # force_ssl_in_redirect_uri { |uri| uri.host != 'localhost' }
92
+
93
+ # Specify what redirect URI's you want to block during creation. Any redirect
94
+ # URI is whitelisted by default.
95
+ #
96
+ # You can use this option in order to forbid URI's with 'javascript' scheme
97
+ # for example.
98
+ #
99
+ # forbid_redirect_uri { |uri| uri.scheme.to_s.downcase == 'javascript' }
84
100
 
85
101
  # Specify what grant flows are enabled in array of Strings. The valid
86
102
  # strings and the flows they enable are:
@@ -98,7 +114,7 @@ Doorkeeper.configure do
98
114
  # http://tools.ietf.org/html/rfc6819#section-4.4.2
99
115
  # http://tools.ietf.org/html/rfc6819#section-4.4.3
100
116
  #
101
- # grant_flows %w(authorization_code client_credentials)
117
+ # grant_flows %w[authorization_code client_credentials]
102
118
 
103
119
  # Under some circumstances you might want to have applications auto-approved,
104
120
  # so that the user skips the authorization step.
@@ -1,4 +1,4 @@
1
- class CreateDoorkeeperTables < ActiveRecord::Migration
1
+ class CreateDoorkeeperTables < ActiveRecord::Migration<%= migration_version %>
2
2
  def change
3
3
  create_table :oauth_applications do |t|
4
4
  t.string :name, null: false
@@ -19,13 +19,24 @@ module Doorkeeper
19
19
  post :create, doorkeeper_application: {
20
20
  name: 'Example',
21
21
  redirect_uri: 'https://example.com' }
22
- end.to_not change { Doorkeeper::Application.count }
22
+ end.not_to change { Doorkeeper::Application.count }
23
23
  end
24
24
  end
25
25
 
26
26
  context 'when admin is authenticated' do
27
+ render_views
28
+
27
29
  before do
28
- allow(Doorkeeper.configuration).to receive(:authenticate_admin).and_return(->(arg) { true })
30
+ allow(Doorkeeper.configuration).to receive(:authenticate_admin).and_return(->(*) { true })
31
+ end
32
+
33
+ it 'sorts applications by created_at' do
34
+ first_application = FactoryBot.create(:application)
35
+ second_application = FactoryBot.create(:application)
36
+ expect(Doorkeeper::Application).to receive(:ordered_by).and_call_original
37
+ get :index
38
+ expect(response.body).to have_selector("tbody tr:first-child#application_#{first_application.id}")
39
+ expect(response.body).to have_selector("tbody tr:last-child#application_#{second_application.id}")
29
40
  end
30
41
 
31
42
  it 'creates application' do
@@ -38,7 +49,7 @@ module Doorkeeper
38
49
  end
39
50
 
40
51
  it 'does not allow mass assignment of uid or secret' do
41
- application = FactoryGirl.create(:application)
52
+ application = FactoryBot.create(:application)
42
53
  put :update, id: application.id, doorkeeper_application: {
43
54
  uid: '1A2B3C4D',
44
55
  secret: '1A2B3C4D' }
@@ -47,7 +58,7 @@ module Doorkeeper
47
58
  end
48
59
 
49
60
  it 'updates application' do
50
- application = FactoryGirl.create(:application)
61
+ application = FactoryBot.create(:application)
51
62
  put :update, id: application.id, doorkeeper_application: {
52
63
  name: 'Example',
53
64
  redirect_uri: 'https://example.com' }
@@ -3,18 +3,33 @@ require 'spec_helper_integration'
3
3
  describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
4
4
  include AuthorizationRequestHelper
5
5
 
6
- def fragments(param)
7
- fragment = URI.parse(response.location).fragment
8
- Rack::Utils.parse_query(fragment)[param]
6
+ if Rails::VERSION::MAJOR >= 5
7
+ class ActionDispatch::TestResponse
8
+ def query_params
9
+ @_query_params ||= begin
10
+ fragment = URI.parse(location).fragment
11
+ Rack::Utils.parse_query(fragment)
12
+ end
13
+ end
14
+ end
15
+ else
16
+ class ActionController::TestResponse
17
+ def query_params
18
+ @_query_params ||= begin
19
+ fragment = URI.parse(location).fragment
20
+ Rack::Utils.parse_query(fragment)
21
+ end
22
+ end
23
+ end
9
24
  end
10
25
 
11
26
  def translated_error_message(key)
12
27
  I18n.translate key, scope: [:doorkeeper, :errors, :messages]
13
28
  end
14
29
 
15
- let(:client) { FactoryGirl.create :application }
30
+ let(:client) { FactoryBot.create :application }
16
31
  let(:user) { User.create!(name: 'Joe', password: 'sekret') }
17
- let(:access_token) { FactoryGirl.build :access_token, resource_owner_id: user.id, application_id: client.id }
32
+ let(:access_token) { FactoryBot.build :access_token, resource_owner_id: user.id, application_id: client.id }
18
33
 
19
34
  before do
20
35
  allow(Doorkeeper.configuration).to receive(:grant_flows).and_return(["implicit"])
@@ -35,15 +50,15 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
35
50
  end
36
51
 
37
52
  it 'includes access token in fragment' do
38
- expect(fragments('access_token')).to eq(Doorkeeper::AccessToken.first.token)
53
+ expect(response.query_params['access_token']).to eq(Doorkeeper::AccessToken.first.token)
39
54
  end
40
55
 
41
56
  it 'includes token type in fragment' do
42
- expect(fragments('token_type')).to eq('bearer')
57
+ expect(response.query_params['token_type']).to eq('bearer')
43
58
  end
44
59
 
45
60
  it 'includes token expiration in fragment' do
46
- expect(fragments('expires_in').to_i).to eq(2.hours.to_i)
61
+ expect(response.query_params['expires_in'].to_i).to eq(2.hours.to_i)
47
62
  end
48
63
 
49
64
  it 'issues the token for the current client' do
@@ -70,15 +85,15 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
70
85
  end
71
86
 
72
87
  it 'does not include access token in fragment' do
73
- expect(fragments('access_token')).to be_nil
88
+ expect(response.query_params['access_token']).to be_nil
74
89
  end
75
90
 
76
91
  it 'includes error in fragment' do
77
- expect(fragments('error')).to eq('invalid_scope')
92
+ expect(response.query_params['error']).to eq('invalid_scope')
78
93
  end
79
94
 
80
95
  it 'includes error description in fragment' do
81
- expect(fragments('error_description')).to eq(translated_error_message(:invalid_scope))
96
+ expect(response.query_params['error_description']).to eq(translated_error_message(:invalid_scope))
82
97
  end
83
98
 
84
99
  it 'does not issue any access token' do
@@ -95,7 +110,7 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
95
110
  end
96
111
 
97
112
  it 'returns the existing access token in a fragment' do
98
- expect(fragments('access_token')).to eq(access_token.token)
113
+ expect(response.query_params['access_token']).to eq(access_token.token)
99
114
  end
100
115
 
101
116
  it 'does not creates a new access token' do
@@ -129,7 +144,7 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
129
144
  describe 'GET #new code request with native url and skip_authorization true' do
130
145
  before do
131
146
  allow(Doorkeeper.configuration).to receive(:grant_flows).
132
- and_return(%w(authorization_code))
147
+ and_return(%w[authorization_code])
133
148
  allow(Doorkeeper.configuration).to receive(:skip_authorization).and_return(proc do
134
149
  true
135
150
  end)
@@ -139,7 +154,7 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
139
154
 
140
155
  it 'should redirect immediately' do
141
156
  expect(response).to be_redirect
142
- expect(response.location).to match(/oauth\/authorize\//)
157
+ expect(response.location).to match(/oauth\/authorize\/native\?code=#{Doorkeeper::AccessGrant.first.token}/)
143
158
  end
144
159
 
145
160
  it 'should issue a grant' do
@@ -169,11 +184,11 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
169
184
  end
170
185
 
171
186
  it 'includes token type in fragment' do
172
- expect(fragments('token_type')).to eq('bearer')
187
+ expect(response.query_params['token_type']).to eq('bearer')
173
188
  end
174
189
 
175
190
  it 'includes token expiration in fragment' do
176
- expect(fragments('expires_in').to_i).to eq(2.hours.to_i)
191
+ expect(response.query_params['expires_in'].to_i).to eq(2.hours.to_i)
177
192
  end
178
193
 
179
194
  it 'issues the token for the current client' do
@@ -9,11 +9,9 @@ module ControllerActions
9
9
  render plain: 'show'
10
10
  end
11
11
 
12
- def doorkeeper_unauthorized_render_options(*)
13
- end
12
+ def doorkeeper_unauthorized_render_options(*); end
14
13
 
15
- def doorkeeper_forbidden_render_options(*)
16
- end
14
+ def doorkeeper_forbidden_render_options(*); end
17
15
  end
18
16
 
19
17
  describe 'doorkeeper authorize filter' do
@@ -74,12 +72,12 @@ describe 'doorkeeper authorize filter' do
74
72
  context 'with valid token', token: :valid do
75
73
  it 'allows into index action' do
76
74
  get :index, access_token: token_string
77
- expect(response).to be_success
75
+ expect(response).to be_successful
78
76
  end
79
77
 
80
78
  it 'allows into show action' do
81
79
  get :show, id: '4', access_token: token_string
82
- expect(response).to be_success
80
+ expect(response).to be_successful
83
81
  end
84
82
  end
85
83
 
@@ -109,15 +107,16 @@ describe 'doorkeeper authorize filter' do
109
107
 
110
108
  it 'allows if the token has particular scopes' do
111
109
  token = double(Doorkeeper::AccessToken,
112
- accessible?: true, scopes: %w(write public),
110
+ accessible?: true, scopes: %w[write public],
113
111
  previous_refresh_token: "",
114
112
  revoke_previous_refresh_token!: true)
115
113
  expect(token).to receive(:acceptable?).with([:write]).and_return(true)
116
114
  expect(
117
115
  Doorkeeper::AccessToken
118
116
  ).to receive(:by_token).with(token_string).and_return(token)
117
+
119
118
  get :index, access_token: token_string
120
- expect(response).to be_success
119
+ expect(response).to be_successful
121
120
  end
122
121
 
123
122
  it 'does not allow if the token does not include given scope' do
@@ -129,6 +128,7 @@ describe 'doorkeeper authorize filter' do
129
128
  Doorkeeper::AccessToken
130
129
  ).to receive(:by_token).with(token_string).and_return(token)
131
130
  expect(token).to receive(:acceptable?).with([:write]).and_return(false)
131
+
132
132
  get :index, access_token: token_string
133
133
  expect(response.status).to eq 403
134
134
  expect(response.header).to_not include('WWW-Authenticate')
@@ -146,14 +146,17 @@ describe 'doorkeeper authorize filter' do
146
146
  before do
147
147
  module ControllerActions
148
148
  remove_method :doorkeeper_unauthorized_render_options
149
+
149
150
  def doorkeeper_unauthorized_render_options(error: nil)
150
151
  { json: ActiveSupport::JSON.encode(error_message: error.description) }
151
152
  end
152
153
  end
153
154
  end
155
+
154
156
  after do
155
157
  module ControllerActions
156
158
  remove_method :doorkeeper_unauthorized_render_options
159
+
157
160
  def doorkeeper_unauthorized_render_options(error: nil)
158
161
  end
159
162
  end
@@ -164,9 +167,9 @@ describe 'doorkeeper authorize filter' do
164
167
  expect(response.status).to eq 401
165
168
  expect(response.content_type).to eq('application/json')
166
169
  expect(response.header['WWW-Authenticate']).to match(/^Bearer/)
167
- parsed_body = JSON.parse(response.body)
168
- expect(parsed_body).not_to be_nil
169
- expect(parsed_body['error_message']).to match('token is invalid')
170
+
171
+ expect(json_response).not_to be_nil
172
+ expect(json_response['error_message']).to match('token is invalid')
170
173
  end
171
174
  end
172
175
 
@@ -174,16 +177,18 @@ describe 'doorkeeper authorize filter' do
174
177
  before do
175
178
  module ControllerActions
176
179
  remove_method :doorkeeper_unauthorized_render_options
177
- def doorkeeper_unauthorized_render_options(error: nil)
180
+
181
+ def doorkeeper_unauthorized_render_options(**)
178
182
  { plain: 'Unauthorized' }
179
183
  end
180
184
  end
181
185
  end
186
+
182
187
  after do
183
188
  module ControllerActions
184
189
  remove_method :doorkeeper_unauthorized_render_options
185
- def doorkeeper_unauthorized_render_options(error: nil)
186
- end
190
+
191
+ def doorkeeper_unauthorized_render_options(error: nil); end
187
192
  end
188
193
  end
189
194
 
@@ -206,8 +211,8 @@ describe 'doorkeeper authorize filter' do
206
211
  after do
207
212
  module ControllerActions
208
213
  remove_method :doorkeeper_forbidden_render_options
209
- def doorkeeper_forbidden_render_options(*)
210
- end
214
+
215
+ def doorkeeper_forbidden_render_options(*); end
211
216
  end
212
217
  end
213
218
 
@@ -223,12 +228,14 @@ describe 'doorkeeper authorize filter' do
223
228
  expired?: false, previous_refresh_token: "",
224
229
  revoke_previous_refresh_token!: true)
225
230
  end
231
+
226
232
  let(:token_string) { '1A2DUWE' }
227
233
 
228
234
  context 'with a JSON custom render' do
229
235
  before do
230
236
  module ControllerActions
231
237
  remove_method :doorkeeper_forbidden_render_options
238
+
232
239
  def doorkeeper_forbidden_render_options(*)
233
240
  { json: { error_message: 'Forbidden' } }
234
241
  end
@@ -240,9 +247,9 @@ describe 'doorkeeper authorize filter' do
240
247
  expect(response.header).to_not include('WWW-Authenticate')
241
248
  expect(response.content_type).to eq('application/json')
242
249
  expect(response.status).to eq 403
243
- parsed_body = JSON.parse(response.body)
244
- expect(parsed_body).not_to be_nil
245
- expect(parsed_body['error_message']).to match('Forbidden')
250
+
251
+ expect(json_response).not_to be_nil
252
+ expect(json_response['error_message']).to match('Forbidden')
246
253
  end
247
254
  end
248
255
 
@@ -267,6 +274,7 @@ describe 'doorkeeper authorize filter' do
267
274
  before do
268
275
  module ControllerActions
269
276
  remove_method :doorkeeper_forbidden_render_options
277
+
270
278
  def doorkeeper_forbidden_render_options(*)
271
279
  { plain: 'Forbidden' }
272
280
  end
@@ -285,6 +293,7 @@ describe 'doorkeeper authorize filter' do
285
293
  before do
286
294
  module ControllerActions
287
295
  remove_method :doorkeeper_forbidden_render_options
296
+
288
297
  def doorkeeper_forbidden_render_options(*)
289
298
  { respond_not_found_when_forbidden: true, plain: 'Not Found' }
290
299
  end
@@ -1,26 +1,23 @@
1
1
  require 'spec_helper_integration'
2
2
 
3
3
  describe Doorkeeper::TokenInfoController do
4
- describe 'when requesting tokeninfo with valid token' do
5
- let(:doorkeeper_token) { FactoryGirl.create(:access_token) }
4
+ describe 'when requesting token info with valid token' do
5
+ let(:doorkeeper_token) { FactoryBot.create(:access_token) }
6
6
 
7
7
  before(:each) do
8
8
  allow(controller).to receive(:doorkeeper_token) { doorkeeper_token }
9
9
  end
10
10
 
11
- def do_get
12
- get :show
13
- end
14
-
15
11
  describe 'successful request' do
16
-
17
12
  it 'responds with tokeninfo' do
18
- do_get
13
+ get :show
14
+
19
15
  expect(response.body).to eq(doorkeeper_token.to_json)
20
16
  end
21
17
 
22
18
  it 'responds with a 200 status' do
23
- do_get
19
+ get :show
20
+
24
21
  expect(response.status).to eq 200
25
22
  end
26
23
  end
@@ -29,8 +26,10 @@ describe Doorkeeper::TokenInfoController do
29
26
  before(:each) do
30
27
  allow(controller).to receive(:doorkeeper_token).and_return(nil)
31
28
  end
29
+
32
30
  it 'responds with 401 when doorkeeper_token is not valid' do
33
- do_get
31
+ get :show
32
+
34
33
  expect(response.status).to eq 401
35
34
  expect(response.headers['WWW-Authenticate']).to match(/^Bearer/)
36
35
  end
@@ -38,14 +37,19 @@ describe Doorkeeper::TokenInfoController do
38
37
  it 'responds with 401 when doorkeeper_token is invalid, expired or revoked' do
39
38
  allow(controller).to receive(:doorkeeper_token).and_return(doorkeeper_token)
40
39
  allow(doorkeeper_token).to receive(:accessible?).and_return(false)
41
- do_get
40
+
41
+ get :show
42
+
42
43
  expect(response.status).to eq 401
43
44
  expect(response.headers['WWW-Authenticate']).to match(/^Bearer/)
44
45
  end
45
46
 
46
47
  it 'responds body message for error' do
47
- do_get
48
- expect(response.body).to eq(Doorkeeper::OAuth::ErrorResponse.new(name: :invalid_request, status: :unauthorized).body.to_json)
48
+ get :show
49
+
50
+ expect(response.body).to eq(
51
+ Doorkeeper::OAuth::ErrorResponse.new(name: :invalid_request, status: :unauthorized).body.to_json
52
+ )
49
53
  end
50
54
  end
51
55
  end
@@ -2,9 +2,7 @@ require 'spec_helper_integration'
2
2
 
3
3
  describe Doorkeeper::TokensController do
4
4
  describe 'when authorization has succeeded' do
5
- let :token do
6
- double(:token, authorize: true)
7
- end
5
+ let(:token) { double(:token, authorize: true) }
8
6
 
9
7
  before do
10
8
  allow(controller).to receive(:token) { token }
@@ -57,7 +55,7 @@ describe Doorkeeper::TokensController do
57
55
  }
58
56
  expect(response.status).to eq 401
59
57
  expect(response.headers['WWW-Authenticate']).to match(/Bearer/)
60
- expect(JSON.load(response.body)).to eq expected_response_body
58
+ expect(JSON.parse(response.body)).to eq expected_response_body
61
59
  end
62
60
  end
63
61
 
@@ -85,4 +83,140 @@ describe Doorkeeper::TokensController do
85
83
  post :create
86
84
  end
87
85
  end
86
+
87
+ describe 'when requested token introspection' do
88
+ context 'authorized using Bearer token' do
89
+ let(:client) { FactoryBot.create(:application) }
90
+ let(:access_token) { FactoryBot.create(:access_token, application: client) }
91
+
92
+ it 'responds with full token introspection' do
93
+ request.headers['Authorization'] = "Bearer #{access_token.token}"
94
+
95
+ post :introspect, token: access_token.token
96
+
97
+ should_have_json 'active', true
98
+ expect(json_response).to include('client_id', 'token_type', 'exp', 'iat')
99
+ end
100
+ end
101
+
102
+ context 'authorized using Client Authentication' do
103
+ let(:client) { FactoryBot.create(:application) }
104
+ let(:access_token) { FactoryBot.create(:access_token, application: client) }
105
+
106
+ it 'responds with full token introspection' do
107
+ request.headers['Authorization'] = basic_auth_header_for_client(client)
108
+
109
+ post :introspect, token: access_token.token
110
+
111
+ should_have_json 'active', true
112
+ expect(json_response).to include('client_id', 'token_type', 'exp', 'iat')
113
+ should_have_json 'client_id', client.uid
114
+ end
115
+ end
116
+
117
+ context 'public access token' do
118
+ let(:client) { FactoryBot.create(:application) }
119
+ let(:access_token) { FactoryBot.create(:access_token, application: nil) }
120
+
121
+ it 'responds with full token introspection' do
122
+ request.headers['Authorization'] = basic_auth_header_for_client(client)
123
+
124
+ post :introspect, token: access_token.token
125
+
126
+ should_have_json 'active', true
127
+ expect(json_response).to include('client_id', 'token_type', 'exp', 'iat')
128
+ should_have_json 'client_id', nil
129
+ end
130
+ end
131
+
132
+ context 'token was issued to a different client than is making this request' do
133
+ let(:client) { FactoryBot.create(:application) }
134
+ let(:different_client) { FactoryBot.create(:application) }
135
+ let(:access_token) { FactoryBot.create(:access_token, application: client) }
136
+
137
+ it 'responds with only active state' do
138
+ request.headers['Authorization'] = basic_auth_header_for_client(different_client)
139
+
140
+ post :introspect, token: access_token.token
141
+
142
+ expect(response).to be_successful
143
+
144
+ should_have_json 'active', false
145
+ expect(json_response).not_to include('client_id', 'token_type', 'exp', 'iat')
146
+ end
147
+ end
148
+
149
+ context 'using invalid credentials to authorize' do
150
+ let(:client) { double(uid: '123123', secret: '666999') }
151
+ let(:access_token) { FactoryBot.create(:access_token) }
152
+
153
+ it 'responds with invalid_client error' do
154
+ request.headers['Authorization'] = basic_auth_header_for_client(client)
155
+
156
+ post :introspect, token: access_token.token
157
+
158
+ expect(response).not_to be_successful
159
+ response_status_should_be 401
160
+
161
+ should_not_have_json 'active'
162
+ should_have_json 'error', 'invalid_client'
163
+ end
164
+ end
165
+
166
+ context 'using wrong token value' do
167
+ let(:client) { FactoryBot.create(:application) }
168
+ let(:access_token) { FactoryBot.create(:access_token, application: client) }
169
+
170
+ it 'responds with only active state' do
171
+ request.headers['Authorization'] = basic_auth_header_for_client(client)
172
+
173
+ post :introspect, token: SecureRandom.hex(16)
174
+
175
+ should_have_json 'active', false
176
+ expect(json_response).not_to include('client_id', 'token_type', 'exp', 'iat')
177
+ end
178
+ end
179
+
180
+ context 'when requested Access Token expired' do
181
+ let(:client) { FactoryBot.create(:application) }
182
+ let(:access_token) { FactoryBot.create(:access_token, application: client, created_at: 1.year.ago) }
183
+
184
+ it 'responds with only active state' do
185
+ request.headers['Authorization'] = basic_auth_header_for_client(client)
186
+
187
+ post :introspect, token: access_token.token
188
+
189
+ should_have_json 'active', false
190
+ expect(json_response).not_to include('client_id', 'token_type', 'exp', 'iat')
191
+ end
192
+ end
193
+
194
+ context 'when requested Access Token revoked' do
195
+ let(:client) { FactoryBot.create(:application) }
196
+ let(:access_token) { FactoryBot.create(:access_token, application: client, revoked_at: 1.year.ago) }
197
+
198
+ it 'responds with only active state' do
199
+ request.headers['Authorization'] = basic_auth_header_for_client(client)
200
+
201
+ post :introspect, token: access_token.token
202
+
203
+ should_have_json 'active', false
204
+ expect(json_response).not_to include('client_id', 'token_type', 'exp', 'iat')
205
+ end
206
+ end
207
+
208
+ context 'unauthorized (no bearer token or client credentials)' do
209
+ let(:access_token) { FactoryBot.create(:access_token) }
210
+
211
+ it 'responds with invalid_request error' do
212
+ post :introspect, token: access_token.token
213
+
214
+ expect(response).not_to be_successful
215
+ response_status_should_be 401
216
+
217
+ should_not_have_json 'active'
218
+ should_have_json 'error', 'invalid_request'
219
+ end
220
+ end
221
+ end
88
222
  end
@@ -82,7 +82,7 @@ Doorkeeper.configure do
82
82
  # http://tools.ietf.org/html/rfc6819#section-4.4.2
83
83
  # http://tools.ietf.org/html/rfc6819#section-4.4.3
84
84
  #
85
- # grant_flows %w(authorization_code client_credentials)
85
+ # grant_flows %w[authorization_code client_credentials]
86
86
 
87
87
  # Under some circumstances you might want to have applications auto-approved,
88
88
  # so that the user skips the authorization step.
@@ -1,6 +1,6 @@
1
1
  # Require `belongs_to` associations by default. This is a new Rails 5.0
2
2
  # default, so it is introduced as a configuration option to ensure that apps
3
3
  # made on earlier versions of Rails are not affected when upgrading.
4
- if Rails.version.to_i >= 5
4
+ if Rails::VERSION::MAJOR >= 5
5
5
  Rails.application.config.active_record.belongs_to_required_by_default = true
6
6
  end
@@ -5,5 +5,4 @@
5
5
  # Make sure the secret is at least 30 characters and all random,
6
6
  # no regular words or you'll be exposed to dictionary attacks.
7
7
  Dummy::Application.config.secret_key_base =
8
- Dummy::Application.config.secret_token =
9
8
  'c00157b5a1bb6181792f0f4a8a080485de7bab9987e6cf159dc74c4f0573345c1bfa713b5d756e1491fc0b098567e8a619e2f8d268eda86a20a720d05d633780'
data/spec/factories.rb CHANGED
@@ -1,4 +1,4 @@
1
- FactoryGirl.define do
1
+ FactoryBot.define do
2
2
  factory :access_grant, class: Doorkeeper::AccessGrant do
3
3
  sequence(:resource_owner_id) { |n| n }
4
4
  application
@@ -10,13 +10,32 @@ describe 'Doorkeeper::ApplicationOwnerGenerator' do
10
10
  describe 'after running the generator' do
11
11
  before :each do
12
12
  prepare_destination
13
- FileUtils.mkdir(::File.expand_path('config', Pathname(destination_root)))
14
- FileUtils.copy_file(::File.expand_path('../templates/routes.rb', __FILE__), ::File.expand_path('config/routes.rb', Pathname.new(destination_root)))
15
- run_generator
16
13
  end
17
14
 
18
- it 'creates a migration' do
19
- assert_migration 'db/migrate/add_owner_to_application.rb'
15
+ context 'pre Rails 5.0.0' do
16
+ it 'creates a migration with no version specifier' do
17
+ stub_const("ActiveRecord::VERSION::MAJOR", 4)
18
+ stub_const("ActiveRecord::VERSION::MINOR", 2)
19
+
20
+ run_generator
21
+
22
+ assert_migration 'db/migrate/add_owner_to_application.rb' do |migration|
23
+ assert migration.include?("ActiveRecord::Migration\n")
24
+ end
25
+ end
26
+ end
27
+
28
+ context 'post Rails 5.0.0' do
29
+ it 'creates a migration with a version specifier' do
30
+ stub_const("ActiveRecord::VERSION::MAJOR", 5)
31
+ stub_const("ActiveRecord::VERSION::MINOR", 0)
32
+
33
+ run_generator
34
+
35
+ assert_migration 'db/migrate/add_owner_to_application.rb' do |migration|
36
+ assert migration.include?("ActiveRecord::Migration[5.0]\n")
37
+ end
38
+ end
20
39
  end
21
40
  end
22
41
  end