doorkeeper 5.0.0.rc1 → 5.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of doorkeeper might be problematic. Click here for more details.

Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +16 -0
  3. data/NEWS.md +23 -2
  4. data/README.md +7 -6
  5. data/UPGRADE.md +2 -0
  6. data/app/controllers/doorkeeper/authorizations_controller.rb +1 -1
  7. data/app/controllers/doorkeeper/authorized_applications_controller.rb +4 -1
  8. data/app/controllers/doorkeeper/tokens_controller.rb +9 -10
  9. data/app/views/doorkeeper/applications/index.html.erb +1 -1
  10. data/config/locales/en.yml +1 -0
  11. data/doorkeeper.gemspec +1 -1
  12. data/gemfiles/rails_master.gemfile +4 -1
  13. data/lib/doorkeeper.rb +15 -0
  14. data/lib/doorkeeper/config.rb +18 -5
  15. data/lib/doorkeeper/models/access_grant_mixin.rb +15 -0
  16. data/lib/doorkeeper/models/access_token_mixin.rb +2 -2
  17. data/lib/doorkeeper/oauth/authorization/token.rb +26 -19
  18. data/lib/doorkeeper/oauth/base_request.rb +3 -2
  19. data/lib/doorkeeper/oauth/client.rb +0 -2
  20. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +2 -4
  21. data/lib/doorkeeper/oauth/client_credentials/validation.rb +0 -4
  22. data/lib/doorkeeper/oauth/client_credentials_request.rb +0 -4
  23. data/lib/doorkeeper/oauth/refresh_token_request.rb +2 -2
  24. data/lib/doorkeeper/orm/active_record/application.rb +11 -0
  25. data/lib/doorkeeper/request.rb +0 -7
  26. data/lib/doorkeeper/request/authorization_code.rb +0 -2
  27. data/lib/doorkeeper/request/client_credentials.rb +0 -2
  28. data/lib/doorkeeper/request/code.rb +0 -2
  29. data/lib/doorkeeper/request/password.rb +0 -2
  30. data/lib/doorkeeper/request/refresh_token.rb +0 -2
  31. data/lib/doorkeeper/request/token.rb +0 -2
  32. data/lib/doorkeeper/version.rb +1 -1
  33. data/lib/generators/doorkeeper/templates/initializer.rb +18 -4
  34. data/spec/controllers/authorizations_controller_spec.rb +27 -0
  35. data/spec/controllers/tokens_controller_spec.rb +59 -7
  36. data/spec/dummy/config/initializers/doorkeeper.rb +5 -1
  37. data/spec/lib/config_spec.rb +29 -0
  38. data/spec/lib/oauth/base_request_spec.rb +24 -0
  39. data/spec/models/doorkeeper/access_grant_spec.rb +43 -0
  40. data/spec/models/doorkeeper/access_token_spec.rb +16 -6
  41. data/spec/models/doorkeeper/application_spec.rb +13 -0
  42. data/spec/requests/applications/applications_request_spec.rb +45 -0
  43. data/spec/support/helpers/request_spec_helper.rb +8 -0
  44. metadata +8 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1294d2df3309f8b266a7b9de07d4ebf8dd96fe27
4
- data.tar.gz: 9eea101d6a9b5e72412a373224e8160d0d4cd897
3
+ metadata.gz: f9a6bf0cb64c84923dedcc19d31136a03cb7fab0
4
+ data.tar.gz: ee93150982920fc8fb654b2e447d755e42191d9c
5
5
  SHA512:
6
- metadata.gz: a7146828263c150652126e56c7a4a177bfaecf44a02d93f7f630d99edbc5a91767e4e86964b52f68c590022ff85375dd5004b78cd621b386b9c767cc67b90a3c
7
- data.tar.gz: 15d04141cfc2bc9161531bc5b2a5cd98ee55f8cb144b76878ffe7fffb790c6de380a3857cc30813cd979a6c301bec188da6554dcb04d2d2e910b2cb0f240e857
6
+ metadata.gz: d66636e7818c25feab19f577ee2d344f0fac658906253c18c33e884ff063bc75f0461aa0a2ddb6305404501d58b321916fc64d48be5da4917e0fe1bfb1f1c600
7
+ data.tar.gz: f439482a6a560f1e70453e0a69fd1b468c640f2287fe0d2e5a90fad315214eeef01731235931d57aba3ed29ac85eaed2165e6e072a8c9e5b3792f2c4b7cc4392
data/.gitlab-ci.yml ADDED
@@ -0,0 +1,16 @@
1
+ dependency_scanning:
2
+ image: docker:stable
3
+ variables:
4
+ DOCKER_DRIVER: overlay2
5
+ allow_failure: true
6
+ services:
7
+ - docker:stable-dind
8
+ script:
9
+ - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
10
+ - docker run
11
+ --env DEP_SCAN_DISABLE_REMOTE_CHECKS="${DEP_SCAN_DISABLE_REMOTE_CHECKS:-false}"
12
+ --volume "$PWD:/code"
13
+ --volume /var/run/docker.sock:/var/run/docker.sock
14
+ "registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$SP_VERSION" /code
15
+ artifacts:
16
+ paths: [gl-dependency-scanning-report.json]
data/NEWS.md CHANGED
@@ -6,12 +6,29 @@ upgrade guides.
6
6
  User-visible changes worth mentioning.
7
7
 
8
8
  ## master
9
+
10
+ - [#] Add description here
11
+
12
+ ## 5.0.0.rc2
13
+
14
+ - [#1106] Restrict access to AdminController with 'Forbidden 403' if admin_authenticator is not
15
+ configured by developers..
16
+ - [#1108] Simple formating of callback URLs when listing oauth applications
17
+ - [#1116] `AccessGrant`s will now be revoked along with `AccessToken`s when
18
+ hitting the `AuthorizedApplicationController#destroy` route.
19
+ - [#1114] Make token info endpoint's attributes consistent with token creation
20
+ - [#1119] Fix token revocation for OAuth apps using "implicit" grant flow
21
+ - [#1122] Fix AuthorizationsController#new error response to be in JSON format
22
+
23
+ ## 5.0.0.rc1
24
+
25
+ - [#1103] Allow customizing use_refresh_token
9
26
  - [#1089] Removed enable_pkce_without_secret configuration option
10
27
  - [#1102] Expiration time based on scopes
11
28
  - [#1099] All the configuration variables in `Doorkeeper.configuration` now
12
- always return a non-nil value (`true` or `false`)
29
+ always return a non-nil value (`true` or `false`)
13
30
  - [#1099] ORM / Query optimization: Do not revoke the refresh token if it is not enabled
14
- in `doorkeeper.rb`
31
+ in `doorkeeper.rb`
15
32
  - [#996] Expiration Time Base On Grant Type
16
33
  - [#997] Allow PKCE authorization_code flow as specified in RFC7636
17
34
  - [#907] Fix lookup for matching tokens in certain edge-cases
@@ -39,6 +56,10 @@ User-visible changes worth mentioning.
39
56
  - [#1076] Add config to enforce content type to application/x-www-form-urlencoded
40
57
  - Fix bug with `force_ssl_in_redirect_uri` when it breaks existing applications with an
41
58
  SSL redirect_uri.
59
+
60
+ ## 4.4.0
61
+
62
+ - [#1120] Backport security fix from 5.x for token revocation when using public clients
42
63
 
43
64
  ## 4.3.2
44
65
 
data/README.md CHANGED
@@ -1,14 +1,13 @@
1
- # Doorkeeper - awesome OAuth 2 provider for your Rails app.
1
+ # Doorkeeper awesome OAuth 2 provider for your Rails / Grape app.
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/doorkeeper.svg)](https://rubygems.org/gems/doorkeeper)
4
4
  [![Build Status](https://travis-ci.org/doorkeeper-gem/doorkeeper.svg?branch=master)](https://travis-ci.org/doorkeeper-gem/doorkeeper)
5
- [![Dependency Status](https://gemnasium.com/doorkeeper-gem/doorkeeper.svg?travis)](https://gemnasium.com/doorkeeper-gem/doorkeeper)
6
5
  [![Code Climate](https://codeclimate.com/github/doorkeeper-gem/doorkeeper.svg)](https://codeclimate.com/github/doorkeeper-gem/doorkeeper)
7
6
  [![Coverage Status](https://coveralls.io/repos/github/doorkeeper-gem/doorkeeper/badge.svg?branch=master)](https://coveralls.io/github/doorkeeper-gem/doorkeeper?branch=master)
8
7
  [![Security](https://hakiri.io/github/doorkeeper-gem/doorkeeper/master.svg)](https://hakiri.io/github/doorkeeper-gem/doorkeeper/master)
9
8
 
10
- Doorkeeper is a gem that makes it easy to introduce OAuth 2 provider
11
- functionality to your Rails or Grape application.
9
+ Doorkeeper is a gem (Rails engine) that makes it easy to introduce OAuth 2 provider
10
+ functionality to your Ruby on Rails or Grape application.
12
11
 
13
12
  Supported features:
14
13
 
@@ -28,7 +27,7 @@ Supported features:
28
27
  Please check the documentation for the version of doorkeeper you are using in:
29
28
  https://github.com/doorkeeper-gem/doorkeeper/releases
30
29
 
31
- - See the [wiki](https://github.com/doorkeeper-gem/doorkeeper/wiki)
30
+ - See the [Wiki](https://github.com/doorkeeper-gem/doorkeeper/wiki)
32
31
  - See [upgrade guides](https://github.com/doorkeeper-gem/doorkeeper/wiki/Migration-from-old-versions)
33
32
  - For general questions, please post in [Stack Overflow](http://stackoverflow.com/questions/tagged/doorkeeper)
34
33
  - See [SECURITY.md](SECURITY.md) for this project's security disclose
@@ -276,7 +275,9 @@ protect. For example:
276
275
 
277
276
  ``` ruby
278
277
  class Api::V1::ProductsController < Api::V1::ApiController
279
- before_action :doorkeeper_authorize! # Require access token for all actions
278
+ before_action :doorkeeper_authorize! # Requires access token for all actions
279
+
280
+ # before_action -> { doorkeeper_authorize! :read, :write }
280
281
 
281
282
  # your actions
282
283
  end
data/UPGRADE.md ADDED
@@ -0,0 +1,2 @@
1
+ See [Upgrade Guides](https://github.com/doorkeeper-gem/doorkeeper/wiki/Migration-from-old-versions)
2
+ in the project Wiki.
@@ -33,7 +33,7 @@ module Doorkeeper
33
33
 
34
34
  def render_error
35
35
  if Doorkeeper.configuration.api_only
36
- render json: pre_auth.error_response.body[:error_description],
36
+ render json: pre_auth.error_response.body,
37
37
  status: :bad_request
38
38
  else
39
39
  render :error
@@ -12,7 +12,10 @@ module Doorkeeper
12
12
  end
13
13
 
14
14
  def destroy
15
- AccessToken.revoke_all_for params[:id], current_resource_owner
15
+ Application.revoke_tokens_and_grants_for(
16
+ params[:id],
17
+ current_resource_owner
18
+ )
16
19
 
17
20
  respond_to do |format|
18
21
  format.html do
@@ -56,16 +56,15 @@ module Doorkeeper
56
56
  # https://tools.ietf.org/html/rfc6749#section-2.1
57
57
  # https://tools.ietf.org/html/rfc7009
58
58
  def authorized?
59
- if token.present?
60
- # Client is confidential, therefore client authentication & authorization
61
- # is required
62
- if token.application_id?
63
- # We authorize client by checking token's application
64
- server.client && server.client.application == token.application
65
- else
66
- # Client is public, authentication unnecessary
67
- true
68
- end
59
+ return unless token.present?
60
+ # Client is confidential, therefore client authentication & authorization
61
+ # is required
62
+ if token.application_id? && token.application.confidential?
63
+ # We authorize client by checking token's application
64
+ server.client && server.client.application == token.application
65
+ else
66
+ # Client is public, authentication unnecessary
67
+ true
69
68
  end
70
69
  end
71
70
 
@@ -21,7 +21,7 @@
21
21
  <%= link_to application.name, oauth_application_path(application) %>
22
22
  </td>
23
23
  <td class="align-middle">
24
- <%= application.redirect_uri %>
24
+ <%= simple_format(application.redirect_uri) %>
25
25
  </td>
26
26
  <td class="align-middle">
27
27
  <%= application.confidential? ? t('doorkeeper.applications.index.confidentiality.yes') : t('doorkeeper.applications.index.confidentiality.no') %>
@@ -99,6 +99,7 @@ en:
99
99
  # Configuration error messages
100
100
  credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.'
101
101
  resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfigured.'
102
+ admin_authenticator_not_configured: 'Access to admin panel is forbidden due to Doorkeeper.configure.admin_authenticator being unconfigured.'
102
103
 
103
104
  # Access grant errors
104
105
  unsupported_response_type: 'The authorization server does not support this response type.'
data/doorkeeper.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.add_dependency "railties", ">= 4.2"
20
20
  s.required_ruby_version = ">= 2.1"
21
21
 
22
- s.add_development_dependency "capybara"
22
+ s.add_development_dependency "capybara", '~> 2.18'
23
23
  s.add_development_dependency "coveralls"
24
24
  s.add_development_dependency "grape"
25
25
  s.add_development_dependency "database_cleaner", "~> 1.6"
@@ -9,6 +9,9 @@ gem "appraisal"
9
9
  gem "activerecord-jdbcsqlite3-adapter", platform: :jruby
10
10
  gem "sqlite3", platform: [:ruby, :mswin, :mingw, :x64_mingw]
11
11
  gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw]
12
- gem "rspec-rails", "~> 3.7"
12
+
13
+ %w[rspec-core rspec-expectations rspec-mocks rspec-rails rspec-support].each do |lib|
14
+ gem lib, git: "https://github.com/rspec/#{lib}.git", branch: 'master'
15
+ end
13
16
 
14
17
  gemspec path: "../"
data/lib/doorkeeper.rb CHANGED
@@ -2,6 +2,14 @@ require 'doorkeeper/version'
2
2
  require 'doorkeeper/engine'
3
3
  require 'doorkeeper/config'
4
4
 
5
+ require 'doorkeeper/request/strategy'
6
+ require 'doorkeeper/request/authorization_code'
7
+ require 'doorkeeper/request/client_credentials'
8
+ require 'doorkeeper/request/code'
9
+ require 'doorkeeper/request/password'
10
+ require 'doorkeeper/request/refresh_token'
11
+ require 'doorkeeper/request/token'
12
+
5
13
  require 'doorkeeper/errors'
6
14
  require 'doorkeeper/server'
7
15
  require 'doorkeeper/request'
@@ -27,6 +35,13 @@ require 'doorkeeper/oauth/base_request'
27
35
  require 'doorkeeper/oauth/authorization_code_request'
28
36
  require 'doorkeeper/oauth/refresh_token_request'
29
37
  require 'doorkeeper/oauth/password_access_token_request'
38
+
39
+ require 'doorkeeper/oauth/client_credentials/validation'
40
+ require 'doorkeeper/oauth/client_credentials/creator'
41
+ require 'doorkeeper/oauth/client_credentials/issuer'
42
+ require 'doorkeeper/oauth/client_credentials/validation'
43
+ require 'doorkeeper/oauth/client/credentials'
44
+
30
45
  require 'doorkeeper/oauth/client_credentials_request'
31
46
  require 'doorkeeper/oauth/code_request'
32
47
  require 'doorkeeper/oauth/token_request'
@@ -103,9 +103,12 @@ module Doorkeeper
103
103
  @config.instance_variable_set(:@access_token_methods, methods)
104
104
  end
105
105
 
106
- # Issue access tokens with refresh token (disabled by default)
107
- def use_refresh_token
108
- @config.instance_variable_set(:@refresh_token_enabled, true)
106
+ # Issue access tokens with refresh token (disabled if not set)
107
+ def use_refresh_token(enabled = true, &block)
108
+ @config.instance_variable_set(
109
+ :@refresh_token_enabled,
110
+ block ? block : enabled
111
+ )
109
112
  end
110
113
 
111
114
  # Reuse access token for the same resource owner within an application
@@ -207,7 +210,13 @@ module Doorkeeper
207
210
 
208
211
  option :admin_authenticator,
209
212
  as: :authenticate_admin,
210
- default: ->(_routes) {}
213
+ default: (lambda do |_routes|
214
+ ::Rails.logger.warn(
215
+ I18n.t('doorkeeper.errors.messages.admin_authenticator_not_configured')
216
+ )
217
+
218
+ head :forbidden
219
+ end)
211
220
 
212
221
  option :resource_owner_from_credentials,
213
222
  default: (lambda do |_routes|
@@ -289,7 +298,11 @@ module Doorkeeper
289
298
  end
290
299
 
291
300
  def refresh_token_enabled?
292
- !!(defined?(@refresh_token_enabled) && @refresh_token_enabled)
301
+ if defined?(@refresh_token_enabled)
302
+ @refresh_token_enabled
303
+ else
304
+ false
305
+ end
293
306
  end
294
307
 
295
308
  def enforce_configured_scopes?
@@ -31,6 +31,21 @@ module Doorkeeper
31
31
  find_by(token: token.to_s)
32
32
  end
33
33
 
34
+ # Revokes AccessGrant records that have not been revoked and associated
35
+ # with the specific Application and Resource Owner.
36
+ #
37
+ # @param application_id [Integer]
38
+ # ID of the Application
39
+ # @param resource_owner [ActiveRecord::Base]
40
+ # instance of the Resource Owner model
41
+ #
42
+ def revoke_all_for(application_id, resource_owner, clock = Time)
43
+ where(application_id: application_id,
44
+ resource_owner_id: resource_owner.id,
45
+ revoked_at: nil).
46
+ update_all(revoked_at: clock.now.utc)
47
+ end
48
+
34
49
  # Implements PKCE code_challenge encoding without base64 padding as described in the spec.
35
50
  # https://tools.ietf.org/html/rfc7636#appendix-A
36
51
  # Appendix A. Notes on Implementing Base64url Encoding without Padding
@@ -187,8 +187,8 @@ module Doorkeeper
187
187
  def as_json(_options = {})
188
188
  {
189
189
  resource_owner_id: resource_owner_id,
190
- scopes: scopes,
191
- expires_in_seconds: expires_in_seconds,
190
+ scope: scopes,
191
+ expires_in: expires_in_seconds,
192
192
  application: { uid: application.try(:uid) },
193
193
  created_at: created_at.to_i
194
194
  }
@@ -5,30 +5,37 @@ module Doorkeeper
5
5
  attr_accessor :pre_auth, :resource_owner, :token
6
6
 
7
7
  class << self
8
- def access_token_expires_in(server, pre_auth_or_oauth_client, grant_type, scopes)
9
- if (expiration = custom_expiration(server, pre_auth_or_oauth_client, grant_type, scopes))
10
- expiration
11
- else
12
- server.access_token_expires_in
13
- end
14
- end
15
-
16
- private
17
-
18
- def custom_expiration(server, pre_auth_or_oauth_client, grant_type, scopes)
8
+ def build_context(pre_auth_or_oauth_client, grant_type, scopes)
19
9
  oauth_client = if pre_auth_or_oauth_client.respond_to?(:client)
20
10
  pre_auth_or_oauth_client.client
21
11
  else
22
12
  pre_auth_or_oauth_client
23
13
  end
24
- context = Doorkeeper::OAuth::Authorization::Context.new(
14
+
15
+ Doorkeeper::OAuth::Authorization::Context.new(
25
16
  oauth_client,
26
17
  grant_type,
27
18
  scopes
28
19
  )
20
+ end
21
+
22
+ def access_token_expires_in(server, context)
23
+ if (expiration = server.custom_access_token_expires_in.call(context))
24
+ expiration
25
+ else
26
+ server.access_token_expires_in
27
+ end
28
+ end
29
29
 
30
- server.custom_access_token_expires_in.call(context)
30
+ def refresh_token_enabled?(server, context)
31
+ if server.refresh_token_enabled?.respond_to? :call
32
+ server.refresh_token_enabled?.call(context)
33
+ else
34
+ !!server.refresh_token_enabled?
35
+ end
31
36
  end
37
+
38
+ private
32
39
  end
33
40
 
34
41
  def initialize(pre_auth, resource_owner)
@@ -37,16 +44,16 @@ module Doorkeeper
37
44
  end
38
45
 
39
46
  def issue_token
47
+ context = self.class.build_context(
48
+ pre_auth.client,
49
+ Doorkeeper::OAuth::IMPLICIT,
50
+ pre_auth.scopes
51
+ )
40
52
  @token ||= AccessToken.find_or_create_for(
41
53
  pre_auth.client,
42
54
  resource_owner.id,
43
55
  pre_auth.scopes,
44
- self.class.access_token_expires_in(
45
- configuration,
46
- pre_auth,
47
- Doorkeeper::OAuth::IMPLICIT,
48
- pre_auth.scopes
49
- ),
56
+ self.class.access_token_expires_in(configuration, context),
50
57
  false
51
58
  )
52
59
  end
@@ -31,12 +31,13 @@ module Doorkeeper
31
31
  end
32
32
 
33
33
  def find_or_create_access_token(client, resource_owner_id, scopes, server)
34
+ context = Authorization::Token.build_context(client, grant_type, scopes)
34
35
  @access_token = AccessToken.find_or_create_for(
35
36
  client,
36
37
  resource_owner_id,
37
38
  scopes,
38
- Authorization::Token.access_token_expires_in(server, client, grant_type, scopes),
39
- server.refresh_token_enabled?
39
+ Authorization::Token.access_token_expires_in(server, context),
40
+ Authorization::Token.refresh_token_enabled?(server, context)
40
41
  )
41
42
  end
42
43
 
@@ -1,5 +1,3 @@
1
- require 'doorkeeper/oauth/client/credentials'
2
-
3
1
  module Doorkeeper
4
2
  module OAuth
5
3
  class Client
@@ -1,5 +1,3 @@
1
- require 'doorkeeper/oauth/client_credentials/validation'
2
-
3
1
  module Doorkeeper
4
2
  module OAuth
5
3
  class ClientCredentialsRequest < BaseRequest
@@ -25,12 +23,12 @@ module Doorkeeper
25
23
  private
26
24
 
27
25
  def create_token(client, scopes, creator)
28
- ttl = Authorization::Token.access_token_expires_in(
29
- @server,
26
+ context = Authorization::Token.build_context(
30
27
  client,
31
28
  Doorkeeper::OAuth::CLIENT_CREDENTIALS,
32
29
  scopes
33
30
  )
31
+ ttl = Authorization::Token.access_token_expires_in(@server, context)
34
32
 
35
33
  creator.call(
36
34
  client,
@@ -1,7 +1,3 @@
1
- require 'doorkeeper/validations'
2
- require 'doorkeeper/oauth/scopes'
3
- require 'doorkeeper/oauth/helpers/scope_checker'
4
-
5
1
  module Doorkeeper
6
2
  module OAuth
7
3
  class ClientCredentialsRequest < BaseRequest
@@ -1,7 +1,3 @@
1
- require 'doorkeeper/oauth/client_credentials/creator'
2
- require 'doorkeeper/oauth/client_credentials/issuer'
3
- require 'doorkeeper/oauth/client_credentials/validation'
4
-
5
1
  module Doorkeeper
6
2
  module OAuth
7
3
  class ClientCredentialsRequest < BaseRequest
@@ -65,12 +65,12 @@ module Doorkeeper
65
65
  end
66
66
 
67
67
  def access_token_expires_in
68
- Authorization::Token.access_token_expires_in(
69
- server,
68
+ context = Authorization::Token.build_context(
70
69
  client,
71
70
  Doorkeeper::OAuth::REFRESH_TOKEN,
72
71
  scopes
73
72
  )
73
+ Authorization::Token.access_token_expires_in(server, context)
74
74
  end
75
75
 
76
76
  def validate_token_presence
@@ -34,6 +34,17 @@ module Doorkeeper
34
34
  where(id: resource_access_tokens.select(:application_id).distinct)
35
35
  end
36
36
 
37
+ # Revokes AccessToken and AccessGrant records that have not been revoked and
38
+ # associated with the specific Application and Resource Owner.
39
+ #
40
+ # @param resource_owner [ActiveRecord::Base]
41
+ # instance of the Resource Owner model
42
+ #
43
+ def self.revoke_tokens_and_grants_for(id, resource_owner)
44
+ AccessToken.revoke_all_for(id, resource_owner)
45
+ AccessGrant.revoke_all_for(id, resource_owner)
46
+ end
47
+
37
48
  private
38
49
 
39
50
  def generate_uid
@@ -1,10 +1,3 @@
1
- require 'doorkeeper/request/authorization_code'
2
- require 'doorkeeper/request/client_credentials'
3
- require 'doorkeeper/request/code'
4
- require 'doorkeeper/request/password'
5
- require 'doorkeeper/request/refresh_token'
6
- require 'doorkeeper/request/token'
7
-
8
1
  module Doorkeeper
9
2
  module Request
10
3
  class << self
@@ -1,5 +1,3 @@
1
- require 'doorkeeper/request/strategy'
2
-
3
1
  module Doorkeeper
4
2
  module Request
5
3
  class AuthorizationCode < Strategy
@@ -1,5 +1,3 @@
1
- require 'doorkeeper/request/strategy'
2
-
3
1
  module Doorkeeper
4
2
  module Request
5
3
  class ClientCredentials < Strategy
@@ -1,5 +1,3 @@
1
- require 'doorkeeper/request/strategy'
2
-
3
1
  module Doorkeeper
4
2
  module Request
5
3
  class Code < Strategy
@@ -1,5 +1,3 @@
1
- require 'doorkeeper/request/strategy'
2
-
3
1
  module Doorkeeper
4
2
  module Request
5
3
  class Password < Strategy
@@ -1,5 +1,3 @@
1
- require 'doorkeeper/request/strategy'
2
-
3
1
  module Doorkeeper
4
2
  module Request
5
3
  class RefreshToken < Strategy
@@ -1,5 +1,3 @@
1
- require 'doorkeeper/request/strategy'
2
-
3
1
  module Doorkeeper
4
2
  module Request
5
3
  class Token < Strategy
@@ -8,7 +8,7 @@ module Doorkeeper
8
8
  MAJOR = 5
9
9
  MINOR = 0
10
10
  TINY = 0
11
- PRE = 'rc1'
11
+ PRE = 'rc2'
12
12
 
13
13
  # Full version number
14
14
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
@@ -10,13 +10,20 @@ Doorkeeper.configure do
10
10
  # User.find_by_id(session[:user_id]) || redirect_to(new_user_session_url)
11
11
  end
12
12
 
13
- # If you want to restrict access to the web interface for adding oauth authorized applications,
14
- # you need to declare the block below.
13
+ # If you didn't skip applications controller from Doorkeeper routes in your application routes.rb
14
+ # file then you need to declare this block in order to restrict access to the web interface for
15
+ # adding oauth authorized applications. In other case it will return 403 Forbidden response
16
+ # every time somebody will try to access the admin web interface.
15
17
  #
16
18
  # admin_authenticator do
17
19
  # # Put your admin authentication logic here.
18
20
  # # Example implementation:
19
- # Admin.find_by_id(session[:admin_id]) || redirect_to(new_admin_session_url)
21
+ #
22
+ # if current_user
23
+ # head :forbidden unless current_user.admin?
24
+ # else
25
+ # redirect_to sign_in_url
26
+ # end
20
27
  # end
21
28
 
22
29
  # If you are planning to use Doorkeeper in Rails 5 API-only application, then you might
@@ -66,7 +73,14 @@ Doorkeeper.configure do
66
73
  #
67
74
  # reuse_access_token
68
75
 
69
- # Issue access tokens with refresh token (disabled by default)
76
+ # Issue access tokens with refresh token (disabled by default), you may also
77
+ # pass a block which accepts `context` to customize when to give a refresh
78
+ # token or not. Similar to `custom_access_token_expires_in`, `context` has
79
+ # the properties:
80
+ #
81
+ # `client` - the OAuth client application (see Doorkeeper::OAuth::Client)
82
+ # `grant_type` - the grant type of the request (see Doorkeeper::OAuth)
83
+ # `scopes` - the requested scopes (see Doorkeeper::OAuth::Scopes)
70
84
  #
71
85
  # use_refresh_token
72
86
 
@@ -387,6 +387,33 @@ describe Doorkeeper::AuthorizationsController, 'implicit grant flow' do
387
387
  end
388
388
  end
389
389
 
390
+ describe 'GET #new in API mode with errors' do
391
+ let(:response_json_body) { JSON.parse(response.body) }
392
+
393
+ before do
394
+ default_scopes_exist :public
395
+ allow(Doorkeeper.configuration).to receive(:api_only).and_return(true)
396
+ get :new, params: { an_invalid: 'request' }
397
+ end
398
+
399
+ it 'should render bad request' do
400
+ expect(response).to have_http_status(:bad_request)
401
+ end
402
+
403
+ it 'includes error in body' do
404
+ expect(response_json_body['error']).to eq('unsupported_response_type')
405
+ end
406
+
407
+ it 'includes error description in body' do
408
+ expect(response_json_body['error_description']).to eq(translated_error_message(:unsupported_response_type))
409
+ end
410
+
411
+ it 'does not issue any token' do
412
+ expect(Doorkeeper::AccessGrant.count).to eq 0
413
+ expect(Doorkeeper::AccessToken.count).to eq 0
414
+ end
415
+ end
416
+
390
417
  describe 'GET #new with callbacks' do
391
418
  after do
392
419
  client.update_attribute :redirect_uri, 'urn:ietf:wg:oauth:2.0:oob'
@@ -56,15 +56,67 @@ describe Doorkeeper::TokensController do
56
56
  end
57
57
  end
58
58
 
59
- describe 'when revoke authorization has failed' do
60
- # http://tools.ietf.org/html/rfc7009#section-2.2
61
- it 'returns no error response' do
62
- token = double(:token, authorize: false, application_id?: true)
63
- allow(controller).to receive(:token) { token }
59
+ # http://tools.ietf.org/html/rfc7009#section-2.2
60
+ describe 'revoking tokens' do
61
+ let(:client) { FactoryBot.create(:application) }
62
+ let(:access_token) { FactoryBot.create(:access_token, application: client) }
63
+
64
+ before(:each) do
65
+ allow(controller).to receive(:token) { access_token }
66
+ end
67
+
68
+ context 'when associated app is public' do
69
+ let(:client) { FactoryBot.create(:application, confidential: false) }
70
+
71
+ it 'returns 200' do
72
+ post :revoke
73
+
74
+ expect(response.status).to eq 200
75
+ end
76
+
77
+ it 'revokes the access token' do
78
+ post :revoke
79
+
80
+ expect(access_token.reload).to have_attributes(revoked?: true)
81
+ end
82
+ end
83
+
84
+ context 'when associated app is confidential' do
85
+ let(:client) { FactoryBot.create(:application, confidential: true) }
86
+ let(:oauth_client) { Doorkeeper::OAuth::Client.new(client) }
64
87
 
65
- post :revoke
88
+ before(:each) do
89
+ allow_any_instance_of(Doorkeeper::Server).to receive(:client) { oauth_client }
90
+ end
91
+
92
+ it 'returns 200' do
93
+ post :revoke
94
+
95
+ expect(response.status).to eq 200
96
+ end
97
+
98
+ it 'revokes the access token' do
99
+ post :revoke
100
+
101
+ expect(access_token.reload).to have_attributes(revoked?: true)
102
+ end
103
+
104
+ context 'when authorization fails' do
105
+ let(:some_other_client) { FactoryBot.create(:application, confidential: true) }
106
+ let(:oauth_client) { Doorkeeper::OAuth::Client.new(some_other_client) }
107
+
108
+ it 'returns 200' do
109
+ post :revoke
66
110
 
67
- expect(response.status).to eq 200
111
+ expect(response.status).to eq 200
112
+ end
113
+
114
+ it 'does not revoke the access token' do
115
+ post :revoke
116
+
117
+ expect(access_token.reload).to have_attributes(revoked?: false)
118
+ end
119
+ end
68
120
  end
69
121
  end
70
122
 
@@ -8,7 +8,11 @@ Doorkeeper.configure do
8
8
  User.where(id: session[:user_id]).first || redirect_to(root_url, alert: 'Needs sign in.')
9
9
  end
10
10
 
11
- # If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
11
+ # If you didn't skip applications controller from Doorkeeper routes in your application routes.rb
12
+ # file then you need to declare this block in order to restrict access to the web interface for
13
+ # adding oauth authorized applications. In other case it will return 403 Forbidden response
14
+ # every time somebody will try to access the admin web interface.
15
+ #
12
16
  # admin_authenticator do
13
17
  # # Put your admin authentication logic here.
14
18
  # # Example implementation:
@@ -66,6 +66,17 @@ describe Doorkeeper, 'configuration' do
66
66
  end
67
67
 
68
68
  describe 'admin_authenticator' do
69
+ it 'sets the block that is accessible via authenticate_admin' do
70
+ default_behaviour = 'default behaviour'
71
+ allow(Doorkeeper::Config).to receive(:head).and_return(default_behaviour)
72
+
73
+ Doorkeeper.configure do
74
+ orm DOORKEEPER_ORM
75
+ end
76
+
77
+ expect(subject.authenticate_admin.call({})).to eq(default_behaviour)
78
+ end
79
+
69
80
  it 'sets the block that is accessible via authenticate_admin' do
70
81
  block = proc {}
71
82
  Doorkeeper.configure do
@@ -144,6 +155,24 @@ describe Doorkeeper, 'configuration' do
144
155
  expect(subject.refresh_token_enabled?).to eq(true)
145
156
  end
146
157
 
158
+ it 'can accept a boolean parameter' do
159
+ Doorkeeper.configure do
160
+ orm DOORKEEPER_ORM
161
+ use_refresh_token false
162
+ end
163
+
164
+ expect(subject.refresh_token_enabled?).to eq(false)
165
+ end
166
+
167
+ it 'can accept a block parameter' do
168
+ Doorkeeper.configure do
169
+ orm DOORKEEPER_ORM
170
+ use_refresh_token { |_context| nil }
171
+ end
172
+
173
+ expect(subject.refresh_token_enabled?).to be_a(Proc)
174
+ end
175
+
147
176
  it "does not includes 'refresh_token' in authorization_response_types" do
148
177
  expect(subject.token_grant_types).not_to include 'refresh_token'
149
178
  end
@@ -119,6 +119,30 @@ module Doorkeeper::OAuth
119
119
  )
120
120
  expect(result.expires_in).to eql(500)
121
121
  end
122
+
123
+ it "respects use_refresh_token with a block" do
124
+ server = double(:server,
125
+ access_token_expires_in: 100,
126
+ custom_access_token_expires_in: ->(_context) { nil },
127
+ refresh_token_enabled?: lambda { |context|
128
+ context.scopes == "public"
129
+ })
130
+ result = subject.find_or_create_access_token(
131
+ client,
132
+ "1",
133
+ "public",
134
+ server
135
+ )
136
+ expect(result.refresh_token).to_not be_nil
137
+
138
+ result = subject.find_or_create_access_token(
139
+ client,
140
+ "1",
141
+ "private",
142
+ server
143
+ )
144
+ expect(result.refresh_token).to be_nil
145
+ end
122
146
  end
123
147
 
124
148
  describe "#scopes" do
@@ -33,4 +33,47 @@ describe Doorkeeper::AccessGrant do
33
33
  expect(subject).not_to be_valid
34
34
  end
35
35
  end
36
+
37
+ describe '.revoke_all_for' do
38
+ let(:resource_owner) { double(id: 100) }
39
+ let(:application) { FactoryBot.create :application }
40
+ let(:default_attributes) do
41
+ {
42
+ application: application,
43
+ resource_owner_id: resource_owner.id
44
+ }
45
+ end
46
+
47
+ it 'revokes all tokens for given application and resource owner' do
48
+ FactoryBot.create :access_grant, default_attributes
49
+
50
+ described_class.revoke_all_for(application.id, resource_owner)
51
+
52
+ described_class.all.each do |token|
53
+ expect(token).to be_revoked
54
+ end
55
+ end
56
+
57
+ it 'matches application' do
58
+ access_grant_for_different_app = FactoryBot.create(
59
+ :access_grant,
60
+ default_attributes.merge(application: FactoryBot.create(:application))
61
+ )
62
+
63
+ described_class.revoke_all_for(application.id, resource_owner)
64
+
65
+ expect(access_grant_for_different_app.reload).not_to be_revoked
66
+ end
67
+
68
+ it 'matches resource owner' do
69
+ access_grant_for_different_owner = FactoryBot.create(
70
+ :access_grant,
71
+ default_attributes.merge(resource_owner_id: 90)
72
+ )
73
+
74
+ described_class.revoke_all_for application.id, resource_owner
75
+
76
+ expect(access_grant_for_different_owner.reload).not_to be_revoked
77
+ end
78
+ end
36
79
  end
@@ -306,15 +306,25 @@ module Doorkeeper
306
306
  end
307
307
 
308
308
  it 'matches application' do
309
- FactoryBot.create :access_token, default_attributes.merge(application: FactoryBot.create(:application))
309
+ access_token_for_different_app = FactoryBot.create(
310
+ :access_token,
311
+ default_attributes.merge(application: FactoryBot.create(:application))
312
+ )
313
+
310
314
  AccessToken.revoke_all_for application.id, resource_owner
311
- expect(AccessToken.all).not_to be_empty
315
+
316
+ expect(access_token_for_different_app.reload).not_to be_revoked
312
317
  end
313
318
 
314
319
  it 'matches resource owner' do
315
- FactoryBot.create :access_token, default_attributes.merge(resource_owner_id: 90)
320
+ access_token_for_different_owner = FactoryBot.create(
321
+ :access_token,
322
+ default_attributes.merge(resource_owner_id: 90)
323
+ )
324
+
316
325
  AccessToken.revoke_all_for application.id, resource_owner
317
- expect(AccessToken.all).not_to be_empty
326
+
327
+ expect(access_token_for_different_owner.reload).not_to be_revoked
318
328
  end
319
329
  end
320
330
 
@@ -440,8 +450,8 @@ module Doorkeeper
440
450
  token = FactoryBot.create :access_token
441
451
  token_hash = {
442
452
  resource_owner_id: token.resource_owner_id,
443
- scopes: token.scopes,
444
- expires_in_seconds: token.expires_in_seconds,
453
+ scope: token.scopes,
454
+ expires_in: token.expires_in_seconds,
445
455
  application: { uid: token.application.uid },
446
456
  created_at: token.created_at.to_i
447
457
  }
@@ -206,6 +206,19 @@ module Doorkeeper
206
206
  end
207
207
  end
208
208
 
209
+ describe :revoke_tokens_and_grants_for do
210
+ it 'revokes all access tokens and access grants' do
211
+ application_id = 42
212
+ resource_owner = double
213
+ expect(Doorkeeper::AccessToken).
214
+ to receive(:revoke_all_for).with(application_id, resource_owner)
215
+ expect(Doorkeeper::AccessGrant).
216
+ to receive(:revoke_all_for).with(application_id, resource_owner)
217
+
218
+ Application.revoke_tokens_and_grants_for(application_id, resource_owner)
219
+ end
220
+ end
221
+
209
222
  describe :by_uid_and_secret do
210
223
  context "when application is private/confidential" do
211
224
  it "finds the application via uid/secret" do
@@ -3,6 +3,7 @@ require 'spec_helper'
3
3
  feature 'Adding applications' do
4
4
  context 'in application form' do
5
5
  background do
6
+ i_am_logged_in
6
7
  visit '/oauth/applications/new'
7
8
  end
8
9
 
@@ -96,12 +97,15 @@ end
96
97
 
97
98
  feature 'Listing applications' do
98
99
  background do
100
+ i_am_logged_in
101
+
99
102
  FactoryBot.create :application, name: 'Oauth Dude'
100
103
  FactoryBot.create :application, name: 'Awesome App'
101
104
  end
102
105
 
103
106
  scenario 'application list' do
104
107
  visit '/oauth/applications'
108
+
105
109
  i_should_see 'Awesome App'
106
110
  i_should_see 'Oauth Dude'
107
111
  end
@@ -126,11 +130,14 @@ end
126
130
 
127
131
  feature 'Show application' do
128
132
  given :app do
133
+ i_am_logged_in
134
+
129
135
  FactoryBot.create :application, name: 'Just another oauth app'
130
136
  end
131
137
 
132
138
  scenario 'visiting application page' do
133
139
  visit "/oauth/applications/#{app.id}"
140
+
134
141
  i_should_see 'Just another oauth app'
135
142
  end
136
143
  end
@@ -141,12 +148,15 @@ feature 'Edit application' do
141
148
  end
142
149
 
143
150
  background do
151
+ i_am_logged_in
152
+
144
153
  visit "/oauth/applications/#{app.id}/edit"
145
154
  end
146
155
 
147
156
  scenario 'updating a valid app' do
148
157
  fill_in 'doorkeeper_application[name]', with: 'Serious app'
149
158
  click_button 'Submit'
159
+
150
160
  i_should_see 'Application updated'
151
161
  i_should_see 'Serious app'
152
162
  i_should_not_see 'OMG my app'
@@ -155,21 +165,27 @@ feature 'Edit application' do
155
165
  scenario 'updating an invalid app' do
156
166
  fill_in 'doorkeeper_application[name]', with: ''
157
167
  click_button 'Submit'
168
+
158
169
  i_should_see 'Whoops! Check your form for possible errors'
159
170
  end
160
171
  end
161
172
 
162
173
  feature 'Remove application' do
163
174
  background do
175
+ i_am_logged_in
176
+
164
177
  @app = FactoryBot.create :application
165
178
  end
166
179
 
167
180
  scenario 'deleting an application from list' do
168
181
  visit '/oauth/applications'
182
+
169
183
  i_should_see @app.name
184
+
170
185
  within(:css, "tr#application_#{@app.id}") do
171
186
  click_button 'Destroy'
172
187
  end
188
+
173
189
  i_should_see 'Application deleted'
174
190
  i_should_not_see @app.name
175
191
  end
@@ -177,6 +193,35 @@ feature 'Remove application' do
177
193
  scenario 'deleting an application from show' do
178
194
  visit "/oauth/applications/#{@app.id}"
179
195
  click_button 'Destroy'
196
+
180
197
  i_should_see 'Application deleted'
181
198
  end
182
199
  end
200
+
201
+ context 'when admin authenticator block is default' do
202
+ let(:app) { FactoryBot.create :application, name: 'app' }
203
+
204
+ feature 'application list' do
205
+ scenario 'fails with forbidden' do
206
+ visit '/oauth/applications'
207
+
208
+ should_have_status 403
209
+ end
210
+ end
211
+
212
+ feature 'adding an app' do
213
+ scenario 'fails with forbidden' do
214
+ visit '/oauth/applications/new'
215
+
216
+ should_have_status 403
217
+ end
218
+ end
219
+
220
+ feature 'editing an app' do
221
+ scenario 'fails with forbidden' do
222
+ visit "/oauth/applications/#{app.id}/edit"
223
+
224
+ should_have_status 403
225
+ end
226
+ end
227
+ end
@@ -1,4 +1,8 @@
1
1
  module RequestSpecHelper
2
+ def i_am_logged_in
3
+ allow(Doorkeeper.configuration).to receive(:authenticate_admin).and_return(->(*) {})
4
+ end
5
+
2
6
  def i_should_see(content)
3
7
  expect(page).to have_content(content)
4
8
  end
@@ -39,6 +43,10 @@ module RequestSpecHelper
39
43
  expect(headers[header]).to eq(value)
40
44
  end
41
45
 
46
+ def should_have_status(status)
47
+ expect(page.driver.response.status).to eq(status)
48
+ end
49
+
42
50
  def with_access_token_header(token)
43
51
  with_header 'Authorization', "Bearer #{token}"
44
52
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doorkeeper
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0.rc1
4
+ version: 5.0.0.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felipe Elias Philipp
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2018-06-11 00:00:00.000000000 Z
14
+ date: 2018-07-17 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: railties
@@ -31,16 +31,16 @@ dependencies:
31
31
  name: capybara
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  requirements:
34
- - - ">="
34
+ - - "~>"
35
35
  - !ruby/object:Gem::Version
36
- version: '0'
36
+ version: '2.18'
37
37
  type: :development
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
- - - ">="
41
+ - - "~>"
42
42
  - !ruby/object:Gem::Version
43
- version: '0'
43
+ version: '2.18'
44
44
  - !ruby/object:Gem::Dependency
45
45
  name: coveralls
46
46
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +150,7 @@ files:
150
150
  - ".github/ISSUE_TEMPLATE.md"
151
151
  - ".github/PULL_REQUEST_TEMPLATE.md"
152
152
  - ".gitignore"
153
+ - ".gitlab-ci.yml"
153
154
  - ".hound.yml"
154
155
  - ".rspec"
155
156
  - ".rubocop.yml"
@@ -164,6 +165,7 @@ files:
164
165
  - RELEASING.md
165
166
  - Rakefile
166
167
  - SECURITY.md
168
+ - UPGRADE.md
167
169
  - app/assets/stylesheets/doorkeeper/admin/application.css
168
170
  - app/assets/stylesheets/doorkeeper/application.css
169
171
  - app/controllers/doorkeeper/application_controller.rb