doorkeeper 4.1.0 → 4.2.0

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ba5c804bf7f98c343b73a08ddeb8aca3c16e068f
4
- data.tar.gz: 2b7e888f0adec7f5067bcae28e3b3e2d6d0ade2e
3
+ metadata.gz: 9886d44f18692bd1207493bc04efbad8ccab4e46
4
+ data.tar.gz: 56c74dd71ce5473c11e1ce3a5c6aeb7a264c0ee8
5
5
  SHA512:
6
- metadata.gz: d9945f4ec12a539210541bf179d5ccd3d45d5e9627d7a890d321ba74cb9392913646c0186eeba1cc9b7da143938cc913b47f3295ba53d946c41c203c4bbdd502
7
- data.tar.gz: 5fb30e9a5d74d6ca628cef571f20ffc51bb648a63aae85b91ec9794a5f5bfe6ea4955286ff64bc087253ca7439b54d46ca3a473113049d125764e4ab9a3bb982
6
+ metadata.gz: 25db8a95095afb722206d05f2e42cf98964126329686363c5d17afd6f0a9bbe4ba0cfc224ba60544b6c3c7939351a6396aac7789394b2bb2fddc2c60b9814fa5
7
+ data.tar.gz: 007df96482ad0b1240ae3bd9089eaefa8a9b5d7fc90c40817dfde978baed97051042233980f2762735eae2b2cda1bba8c033774ecb00cbd01a8bcb27f6fb2fbf
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ source "https://rubygems.org"
5
5
  gem "rails", "~> #{ENV["rails"]}"
6
6
 
7
7
  if ENV['rails'].start_with?('5')
8
- gem 'rspec-rails', '3.5.0.beta3'
8
+ gem "rspec-rails", "3.5.1"
9
9
  end
10
10
 
11
11
  gem "activerecord-jdbcsqlite3-adapter", platform: :jruby
data/NEWS.md CHANGED
@@ -4,6 +4,14 @@ User-visible changes worth mentioning.
4
4
 
5
5
  ## master
6
6
 
7
+ ## 4.2.0
8
+
9
+ - Security fix: Address CVE-2016-6582, implement token revocation according to
10
+ spec (tokens might not be revoked if client follows the spec).
11
+ - [#873] Add hooks to Doorkeeper::ApplicationMetalController
12
+ - [#871] Allow downstream users to better utilize doorkeeper spec factories by
13
+ eliminating name conflict on `:user` factory.
14
+
7
15
  ## 4.1.0
8
16
 
9
17
  - [#845] Allow customising the `Doorkeeper::ApplicationController` base
data/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![Dependency Status](https://gemnasium.com/doorkeeper-gem/doorkeeper.svg?travis)](https://gemnasium.com/doorkeeper-gem/doorkeeper)
5
5
  [![Code Climate](https://codeclimate.com/github/doorkeeper-gem/doorkeeper.svg)](https://codeclimate.com/github/doorkeeper-gem/doorkeeper)
6
6
  [![Gem Version](https://badge.fury.io/rb/doorkeeper.svg)](https://rubygems.org/gems/doorkeeper)
7
+ [![Security](https://hakiri.io/github/doorkeeper-gem/doorkeeper/master.svg)](https://hakiri.io/github/doorkeeper-gem/doorkeeper/master)
7
8
 
8
9
  Doorkeeper is a gem that makes it easy to introduce OAuth 2 provider
9
10
  functionality to your Rails or Grape application.
@@ -11,5 +11,7 @@ module Doorkeeper
11
11
  MODULES.each do |mod|
12
12
  include mod
13
13
  end
14
+
15
+ ActiveSupport.run_load_hooks(:doorkeeper_metal_controller, self)
14
16
  end
15
17
  end
@@ -11,29 +11,65 @@ module Doorkeeper
11
11
 
12
12
  # OAuth 2.0 Token Revocation - http://tools.ietf.org/html/rfc7009
13
13
  def revoke
14
- # The authorization server first validates the client credentials
15
- if doorkeeper_token && doorkeeper_token.accessible?
16
- # Doorkeeper does not use the token_type_hint logic described in the RFC 7009
17
- # due to the refresh token implementation that is a field in the access token model.
18
- revoke_token(request.POST['token']) if request.POST['token']
14
+ # The authorization server, if applicable, first authenticates the client
15
+ # and checks its ownership of the provided token.
16
+ #
17
+ # Doorkeeper does not use the token_type_hint logic described in the
18
+ # RFC 7009 due to the refresh token implementation that is a field in
19
+ # the access token model.
20
+ if authorized?
21
+ revoke_token
19
22
  end
20
- # The authorization server responds with HTTP status code 200 if the
21
- # token has been revoked successfully or if the client submitted an invalid token
23
+
24
+ # The authorization server responds with HTTP status code 200 if the token
25
+ # has been revoked successfully or if the client submitted an invalid
26
+ # token
22
27
  render json: {}, status: 200
23
28
  end
24
29
 
25
30
  private
26
31
 
27
- def revoke_token(token)
28
- token = AccessToken.by_token(token) || AccessToken.by_refresh_token(token)
29
- if token && doorkeeper_token.same_credential?(token)
32
+ # OAuth 2.0 Section 2.1 defines two client types, "public" & "confidential".
33
+ # Public clients (as per RFC 7009) do not require authentication whereas
34
+ # confidential clients must be authenticated for their token revocation.
35
+ #
36
+ # Once a confidential client is authenticated, it must be authorized to
37
+ # revoke the provided access or refresh token. This ensures one client
38
+ # cannot revoke another's tokens.
39
+ #
40
+ # Doorkeeper determines the client type implicitly via the presence of the
41
+ # OAuth client associated with a given access or refresh token. Since public
42
+ # clients authenticate the resource owner via "password" or "implicit" grant
43
+ # types, they set the application_id as null (since the claim cannot be
44
+ # verified).
45
+ #
46
+ # https://tools.ietf.org/html/rfc6749#section-2.1
47
+ # https://tools.ietf.org/html/rfc7009
48
+ def authorized?
49
+ if token.present?
50
+ # Client is confidential, therefore client authentication & authorization
51
+ # is required
52
+ if token.application_id?
53
+ # We authorize client by checking token's application
54
+ server.client && server.client.application == token.application
55
+ else
56
+ # Client is public, authentication unnecessary
57
+ true
58
+ end
59
+ end
60
+ end
61
+
62
+ def revoke_token
63
+ if token.accessible?
30
64
  token.revoke
31
- true
32
- else
33
- false
34
65
  end
35
66
  end
36
67
 
68
+ def token
69
+ @token ||= AccessToken.by_token(request.POST['token']) ||
70
+ AccessToken.by_refresh_token(request.POST['token'])
71
+ end
72
+
37
73
  def strategy
38
74
  @strategy ||= server.token_request params[:grant_type]
39
75
  end
@@ -1,3 +1,3 @@
1
1
  module Doorkeeper
2
- VERSION = "4.1.0".freeze
2
+ VERSION = "4.2.0".freeze
3
3
  end
@@ -0,0 +1,10 @@
1
+ require "spec_helper_integration"
2
+
3
+ describe Doorkeeper::ApplicationMetalController do
4
+ it "lazy run hooks" do
5
+ i = 0
6
+ ActiveSupport.on_load(:doorkeeper_metal_controller) { i += 1 }
7
+
8
+ expect(i).to eq 1
9
+ end
10
+ end
@@ -64,7 +64,7 @@ describe Doorkeeper::TokensController do
64
64
  describe 'when revoke authorization has failed' do
65
65
  # http://tools.ietf.org/html/rfc7009#section-2.2
66
66
  it 'returns no error response' do
67
- token = double(:token, authorize: false)
67
+ token = double(:token, authorize: false, application_id?: true)
68
68
  allow(controller).to receive(:token) { token }
69
69
 
70
70
  post :revoke
@@ -22,5 +22,7 @@ FactoryGirl.define do
22
22
  redirect_uri 'https://app.com/callback'
23
23
  end
24
24
 
25
- factory :user
25
+ # do not name this factory :user, otherwise it will conflict with factories
26
+ # from applications that use doorkeeper factories in their own tests
27
+ factory :doorkeeper_testing_user, class: :user
26
28
  end
@@ -30,7 +30,7 @@ module Doorkeeper
30
30
  context 'application owner is required' do
31
31
  before(:each) do
32
32
  require_owner
33
- @owner = FactoryGirl.build_stubbed(:user)
33
+ @owner = FactoryGirl.build_stubbed(:doorkeeper_testing_user)
34
34
  end
35
35
 
36
36
  it 'is invalid without an owner' do
@@ -8,135 +8,149 @@ describe 'Revoke Token Flow' do
8
8
  context 'with default parameters' do
9
9
  let(:client_application) { FactoryGirl.create :application }
10
10
  let(:resource_owner) { User.create!(name: 'John', password: 'sekret') }
11
- let(:authorization_access_token) do
11
+ let(:access_token) do
12
12
  FactoryGirl.create(:access_token,
13
13
  application: client_application,
14
14
  resource_owner_id: resource_owner.id,
15
15
  use_refresh_token: true)
16
16
  end
17
- let(:headers) { { 'HTTP_AUTHORIZATION' => "Bearer #{authorization_access_token.token}" } }
18
17
 
19
- context 'With invalid token to revoke' do
20
- it 'client wants to revoke the given access token' do
21
- post revocation_token_endpoint_url, { token: 'I_AM_AN_INVALIDE_TOKEN' }, headers
22
-
23
- authorization_access_token.reload
24
- # The authorization server responds with HTTP status code 200 if the token
25
- # has been revoked successfully or if the client submitted an invalid token.
26
- expect(response).to be_success
27
- expect(authorization_access_token).to_not be_revoked
18
+ context 'with authenticated, confidential OAuth 2.0 client/application' do
19
+ let(:headers) do
20
+ client_id = client_application.uid
21
+ client_secret = client_application.secret
22
+ credentials = Base64.encode64("#{client_id}:#{client_secret}")
23
+ { 'HTTP_AUTHORIZATION' => "Basic #{credentials}" }
28
24
  end
29
- end
30
-
31
- context 'The access token to revoke is the same than the authorization access token' do
32
- let(:token_to_revoke) { authorization_access_token }
33
25
 
34
- it 'client wants to revoke the given access token' do
35
- post revocation_token_endpoint_url, { token: token_to_revoke.token }, headers
26
+ it 'should revoke the access token provided' do
27
+ post revocation_token_endpoint_url, { token: access_token.token }, headers
36
28
 
37
- token_to_revoke.reload
38
- authorization_access_token.reload
29
+ access_token.reload
39
30
 
40
31
  expect(response).to be_success
41
- expect(token_to_revoke.revoked?).to be_truthy
42
- expect(Doorkeeper::AccessToken.by_refresh_token(token_to_revoke.refresh_token).revoked?).to be_truthy
32
+ expect(access_token.revoked?).to be_truthy
43
33
  end
44
34
 
45
- it 'client wants to revoke the given access token using the POST query string' do
46
- url_with_query_string = revocation_token_endpoint_url + '?' + Rack::Utils.build_query(token: token_to_revoke.token)
47
- post url_with_query_string, {}, headers
35
+ it 'should revoke the refresh token provided' do
36
+ post revocation_token_endpoint_url, { token: access_token.refresh_token }, headers
48
37
 
49
- token_to_revoke.reload
50
- authorization_access_token.reload
38
+ access_token.reload
51
39
 
52
40
  expect(response).to be_success
53
- expect(token_to_revoke.revoked?).to be_falsey
54
- expect(Doorkeeper::AccessToken.by_refresh_token(token_to_revoke.refresh_token).revoked?).to be_falsey
55
- expect(authorization_access_token.revoked?).to be_falsey
41
+ expect(access_token.revoked?).to be_truthy
56
42
  end
57
- end
58
43
 
59
- context 'The access token to revoke app and owners are the same than the authorization access token' do
60
- let(:token_to_revoke) do
61
- FactoryGirl.create(:access_token,
62
- application: client_application,
63
- resource_owner_id: resource_owner.id,
64
- use_refresh_token: true)
44
+ context 'with invalid token to revoke' do
45
+ it 'should not revoke any tokens and respond successfully' do
46
+ num_prev_revoked_tokens = Doorkeeper::AccessToken.where(revoked_at: nil).count
47
+ post revocation_token_endpoint_url, { token: 'I_AM_AN_INVALID_TOKEN' }, headers
48
+
49
+ # The authorization server responds with HTTP status code 200 even if
50
+ # token is invalid
51
+ expect(response).to be_success
52
+ expect(Doorkeeper::AccessToken.where(revoked_at: nil).count).to eq(num_prev_revoked_tokens)
53
+ end
65
54
  end
66
55
 
67
- it 'client wants to revoke the given access token' do
68
- post revocation_token_endpoint_url, { token: token_to_revoke.token }, headers
56
+ context 'with bad credentials and a valid token' do
57
+ let(:headers) do
58
+ client_id = client_application.uid
59
+ credentials = Base64.encode64("#{client_id}:poop")
60
+ { 'HTTP_AUTHORIZATION' => "Basic #{credentials}" }
61
+ end
62
+ it 'should not revoke any tokens and respond successfully' do
63
+ post revocation_token_endpoint_url, { token: access_token.token }, headers
69
64
 
70
- token_to_revoke.reload
71
- authorization_access_token.reload
65
+ access_token.reload
72
66
 
73
- expect(response).to be_success
74
- expect(token_to_revoke.revoked?).to be_truthy
75
- expect(Doorkeeper::AccessToken.by_refresh_token(token_to_revoke.refresh_token).revoked?).to be_truthy
76
- expect(authorization_access_token.revoked?).to be_falsey
67
+ expect(response).to be_success
68
+ expect(access_token.revoked?).to be_falsey
69
+ end
77
70
  end
78
- end
79
71
 
80
- context 'The access token to revoke authorization owner is the same than the authorization access token' do
81
- let(:other_client_application) { FactoryGirl.create :application }
82
- let(:token_to_revoke) do
83
- FactoryGirl.create(:access_token,
84
- application: other_client_application,
85
- resource_owner_id: resource_owner.id,
86
- use_refresh_token: true)
72
+ context 'with no credentials and a valid token' do
73
+ it 'should not revoke any tokens and respond successfully' do
74
+ post revocation_token_endpoint_url, { token: access_token.token }
75
+
76
+ access_token.reload
77
+
78
+ expect(response).to be_success
79
+ expect(access_token.revoked?).to be_falsey
80
+ end
87
81
  end
88
82
 
89
- it 'client wants to revoke the given access token' do
90
- post revocation_token_endpoint_url, { token: token_to_revoke.token }, headers
83
+ context 'with valid token for another client application' do
84
+ let(:other_client_application) { FactoryGirl.create :application }
85
+ let(:headers) do
86
+ client_id = other_client_application.uid
87
+ client_secret = other_client_application.secret
88
+ credentials = Base64.encode64("#{client_id}:#{client_secret}")
89
+ { 'HTTP_AUTHORIZATION' => "Basic #{credentials}" }
90
+ end
91
91
 
92
- token_to_revoke.reload
93
- authorization_access_token.reload
92
+ it 'should not revoke the token as its unauthorized' do
93
+ post revocation_token_endpoint_url, { token: access_token.token }, headers
94
94
 
95
- expect(response).to be_success
96
- expect(token_to_revoke.revoked?).to be_falsey
97
- expect(Doorkeeper::AccessToken.by_refresh_token(token_to_revoke.refresh_token).revoked?).to be_falsey
98
- expect(authorization_access_token.revoked?).to be_falsey
95
+ access_token.reload
96
+
97
+ expect(response).to be_success
98
+ expect(access_token.revoked?).to be_falsey
99
+ end
99
100
  end
100
101
  end
101
102
 
102
- context 'The access token to revoke app is the same than the authorization access token' do
103
- let(:other_resource_owner) { User.create!(name: 'Matheo', password: 'pareto') }
104
- let(:token_to_revoke) do
103
+ context 'with public OAuth 2.0 client/application' do
104
+ let(:access_token) do
105
105
  FactoryGirl.create(:access_token,
106
- application: client_application,
107
- resource_owner_id: other_resource_owner.id,
106
+ application: nil,
107
+ resource_owner_id: resource_owner.id,
108
108
  use_refresh_token: true)
109
109
  end
110
110
 
111
- it 'client wants to revoke the given access token' do
112
- post revocation_token_endpoint_url, { token: token_to_revoke.token }, headers
111
+ it 'should revoke the access token provided' do
112
+ post revocation_token_endpoint_url, { token: access_token.token }
113
113
 
114
- token_to_revoke.reload
115
- authorization_access_token.reload
114
+ access_token.reload
116
115
 
117
116
  expect(response).to be_success
118
- expect(token_to_revoke.revoked?).to be_falsey
119
- expect(Doorkeeper::AccessToken.by_refresh_token(token_to_revoke.refresh_token).revoked?).to be_falsey
120
- expect(authorization_access_token.revoked?).to be_falsey
117
+ expect(access_token.revoked?).to be_truthy
121
118
  end
122
- end
123
119
 
124
- context 'With valid refresh token to revoke' do
125
- let(:token_to_revoke) do
126
- FactoryGirl.create(:access_token,
127
- application: client_application,
128
- resource_owner_id: resource_owner.id,
129
- use_refresh_token: true)
130
- end
120
+ it 'should revoke the refresh token provided' do
121
+ post revocation_token_endpoint_url, { token: access_token.refresh_token }
131
122
 
132
- it 'client wants to revoke the given refresh token' do
133
- post revocation_token_endpoint_url, { token: token_to_revoke.refresh_token, token_type_hint: 'refresh_token' }, headers
134
- authorization_access_token.reload
135
- token_to_revoke.reload
123
+ access_token.reload
136
124
 
137
125
  expect(response).to be_success
138
- expect(Doorkeeper::AccessToken.by_refresh_token(token_to_revoke.refresh_token).revoked?).to be_truthy
139
- expect(authorization_access_token).to_not be_revoked
126
+ expect(access_token.revoked?).to be_truthy
127
+ end
128
+
129
+ context 'with a valid token issued for a confidential client' do
130
+ let(:access_token) do
131
+ FactoryGirl.create(:access_token,
132
+ application: client_application,
133
+ resource_owner_id: resource_owner.id,
134
+ use_refresh_token: true)
135
+ end
136
+
137
+ it 'should not revoke the access token provided' do
138
+ post revocation_token_endpoint_url, { token: access_token.token }
139
+
140
+ access_token.reload
141
+
142
+ expect(response).to be_success
143
+ expect(access_token.revoked?).to be_falsey
144
+ end
145
+
146
+ it 'should not revoke the refresh token provided' do
147
+ post revocation_token_endpoint_url, { token: access_token.token }
148
+
149
+ access_token.reload
150
+
151
+ expect(response).to be_success
152
+ expect(access_token.revoked?).to be_falsey
153
+ end
140
154
  end
141
155
  end
142
156
  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: 4.1.0
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felipe Elias Philipp
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-07-29 00:00:00.000000000 Z
12
+ date: 2016-08-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: railties
@@ -239,6 +239,7 @@ files:
239
239
  - lib/generators/doorkeeper/templates/initializer.rb
240
240
  - lib/generators/doorkeeper/templates/migration.rb
241
241
  - lib/generators/doorkeeper/views_generator.rb
242
+ - spec/controllers/application_metal_controller.rb
242
243
  - spec/controllers/applications_controller_spec.rb
243
244
  - spec/controllers/authorizations_controller_spec.rb
244
245
  - spec/controllers/protected_resources_controller_spec.rb
@@ -383,6 +384,7 @@ signing_key:
383
384
  specification_version: 4
384
385
  summary: OAuth 2 provider for Rails and Grape
385
386
  test_files:
387
+ - spec/controllers/application_metal_controller.rb
386
388
  - spec/controllers/applications_controller_spec.rb
387
389
  - spec/controllers/authorizations_controller_spec.rb
388
390
  - spec/controllers/protected_resources_controller_spec.rb