doorkeeper 3.0.0 → 4.0.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.

Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +4 -0
  3. data/.travis.yml +9 -7
  4. data/CONTRIBUTING.md +2 -0
  5. data/Gemfile +10 -3
  6. data/NEWS.md +79 -2
  7. data/README.md +56 -51
  8. data/RELEASING.md +2 -2
  9. data/Rakefile +1 -1
  10. data/app/assets/stylesheets/doorkeeper/admin/application.css +1 -5
  11. data/app/controllers/doorkeeper/application_metal_controller.rb +1 -2
  12. data/app/controllers/doorkeeper/applications_controller.rb +2 -2
  13. data/app/controllers/doorkeeper/authorizations_controller.rb +1 -1
  14. data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -1
  15. data/app/controllers/doorkeeper/tokens_controller.rb +1 -1
  16. data/app/helpers/doorkeeper/dashboard_helper.rb +13 -11
  17. data/app/views/doorkeeper/applications/show.html.erb +1 -1
  18. data/app/views/doorkeeper/authorizations/new.html.erb +1 -1
  19. data/app/views/layouts/doorkeeper/admin.html.erb +5 -2
  20. data/config/locales/en.yml +1 -0
  21. data/doorkeeper.gemspec +7 -7
  22. data/lib/doorkeeper/config.rb +10 -15
  23. data/lib/doorkeeper/engine.rb +11 -7
  24. data/lib/doorkeeper/errors.rb +6 -0
  25. data/lib/doorkeeper/helpers/controller.rb +7 -1
  26. data/lib/doorkeeper/models/access_grant_mixin.rb +9 -5
  27. data/lib/doorkeeper/models/access_token_mixin.rb +28 -22
  28. data/lib/doorkeeper/models/application_mixin.rb +3 -7
  29. data/lib/doorkeeper/models/concerns/expirable.rb +2 -2
  30. data/lib/doorkeeper/models/concerns/ownership.rb +6 -1
  31. data/lib/doorkeeper/models/concerns/revocable.rb +19 -2
  32. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +1 -1
  33. data/lib/doorkeeper/oauth/authorization_code_request.rb +10 -5
  34. data/lib/doorkeeper/oauth/client/credentials.rb +1 -1
  35. data/lib/doorkeeper/oauth/client_credentials/creator.rb +3 -4
  36. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +2 -1
  37. data/lib/doorkeeper/oauth/client_credentials_request.rb +7 -4
  38. data/lib/doorkeeper/oauth/code_response.rb +13 -14
  39. data/lib/doorkeeper/oauth/error.rb +5 -1
  40. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +1 -1
  41. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +2 -1
  42. data/lib/doorkeeper/oauth/password_access_token_request.rb +6 -10
  43. data/lib/doorkeeper/oauth/refresh_token_request.rb +29 -12
  44. data/lib/doorkeeper/oauth/scopes.rb +2 -2
  45. data/lib/doorkeeper/oauth/token.rb +6 -5
  46. data/lib/doorkeeper/oauth/token_response.rb +1 -1
  47. data/lib/doorkeeper/orm/active_record/access_grant.rb +2 -2
  48. data/lib/doorkeeper/orm/active_record/access_token.rb +10 -2
  49. data/lib/doorkeeper/orm/active_record/application.rb +4 -9
  50. data/lib/doorkeeper/orm/active_record.rb +0 -15
  51. data/lib/doorkeeper/rails/helpers.rb +13 -3
  52. data/lib/doorkeeper/rails/routes/mapper.rb +1 -1
  53. data/lib/doorkeeper/rails/routes.rb +2 -1
  54. data/lib/doorkeeper/request/authorization_code.rb +10 -15
  55. data/lib/doorkeeper/request/client_credentials.rb +9 -15
  56. data/lib/doorkeeper/request/code.rb +7 -13
  57. data/lib/doorkeeper/request/password.rb +18 -13
  58. data/lib/doorkeeper/request/refresh_token.rb +11 -13
  59. data/lib/doorkeeper/request/strategy.rb +17 -0
  60. data/lib/doorkeeper/request/token.rb +7 -13
  61. data/lib/doorkeeper/request.rb +18 -8
  62. data/lib/doorkeeper/server.rb +2 -2
  63. data/lib/doorkeeper/version.rb +1 -1
  64. data/lib/doorkeeper.rb +1 -1
  65. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +29 -0
  66. data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb +1 -1
  67. data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb +11 -0
  68. data/lib/generators/doorkeeper/templates/initializer.rb +2 -2
  69. data/lib/generators/doorkeeper/templates/migration.rb +23 -5
  70. data/spec/controllers/authorizations_controller_spec.rb +0 -14
  71. data/spec/controllers/protected_resources_controller_spec.rb +138 -15
  72. data/spec/controllers/tokens_controller_spec.rb +30 -0
  73. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -4
  74. data/spec/dummy/app/controllers/home_controller.rb +1 -1
  75. data/spec/dummy/app/controllers/metal_controller.rb +1 -1
  76. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +3 -3
  77. data/spec/dummy/app/models/user.rb +0 -4
  78. data/spec/dummy/config/application.rb +2 -36
  79. data/spec/dummy/config/environment.rb +1 -1
  80. data/spec/dummy/config/environments/test.rb +4 -15
  81. data/spec/dummy/config/initializers/active_record_belongs_to_required_by_default.rb +6 -0
  82. data/spec/dummy/config/initializers/doorkeeper.rb +2 -2
  83. data/spec/dummy/db/migrate/{20130902165751_create_doorkeeper_tables.rb → 20151223192035_create_doorkeeper_tables.rb} +24 -5
  84. data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +11 -0
  85. data/spec/dummy/db/schema.rb +23 -22
  86. data/spec/lib/config_spec.rb +2 -2
  87. data/spec/lib/models/revocable_spec.rb +27 -4
  88. data/spec/lib/oauth/authorization_code_request_spec.rb +1 -1
  89. data/spec/lib/oauth/client_credentials/creator_spec.rb +25 -1
  90. data/spec/lib/oauth/code_response_spec.rb +34 -0
  91. data/spec/lib/oauth/error_response_spec.rb +7 -7
  92. data/spec/lib/oauth/error_spec.rb +9 -5
  93. data/spec/lib/oauth/password_access_token_request_spec.rb +5 -5
  94. data/spec/lib/oauth/refresh_token_request_spec.rb +34 -3
  95. data/spec/lib/oauth/scopes_spec.rb +1 -2
  96. data/spec/lib/oauth/token_spec.rb +12 -5
  97. data/spec/lib/request/strategy_spec.rb +53 -0
  98. data/spec/lib/server_spec.rb +1 -1
  99. data/spec/models/doorkeeper/access_grant_spec.rb +5 -5
  100. data/spec/models/doorkeeper/access_token_spec.rb +49 -5
  101. data/spec/models/doorkeeper/application_spec.rb +2 -10
  102. data/spec/requests/flows/authorization_code_spec.rb +26 -0
  103. data/spec/requests/flows/password_spec.rb +26 -5
  104. data/spec/requests/flows/refresh_token_spec.rb +95 -17
  105. data/spec/spec_helper_integration.rb +10 -0
  106. data/spec/support/helpers/model_helper.rb +27 -5
  107. data/spec/support/http_method_shim.rb +24 -0
  108. data/spec/support/shared/controllers_shared_context.rb +13 -4
  109. data/spec/support/shared/models_shared_examples.rb +1 -1
  110. metadata +46 -38
  111. data/lib/generators/doorkeeper/application_scopes_generator.rb +0 -34
  112. data/lib/generators/doorkeeper/templates/add_scopes_to_oauth_applications.rb +0 -5
  113. data/spec/dummy/db/migrate/20141209001746_add_scopes_to_oauth_applications.rb +0 -5
  114. /data/spec/dummy/db/migrate/{20130902175349_add_owner_to_application.rb → 20151223200000_add_owner_to_application.rb} +0 -0
@@ -11,20 +11,21 @@ describe 'Revocable' do
11
11
 
12
12
  describe :revoke do
13
13
  it 'updates :revoked_at attribute with current time' do
14
- clock = double now: double
15
- expect(subject).to receive(:update_attribute).with(:revoked_at, clock.now)
14
+ utc = double utc: double
15
+ clock = double now: utc
16
+ expect(subject).to receive(:update_attribute).with(:revoked_at, clock.now.utc)
16
17
  subject.revoke(clock)
17
18
  end
18
19
  end
19
20
 
20
21
  describe :revoked? do
21
22
  it 'is revoked if :revoked_at has passed' do
22
- allow(subject).to receive(:revoked_at).and_return(Time.now - 1000)
23
+ allow(subject).to receive(:revoked_at).and_return(Time.now.utc - 1000)
23
24
  expect(subject).to be_revoked
24
25
  end
25
26
 
26
27
  it 'is not revoked if :revoked_at has not passed' do
27
- allow(subject).to receive(:revoked_at).and_return(Time.now + 1000)
28
+ allow(subject).to receive(:revoked_at).and_return(Time.now.utc + 1000)
28
29
  expect(subject).not_to be_revoked
29
30
  end
30
31
 
@@ -33,4 +34,26 @@ describe 'Revocable' do
33
34
  expect(subject).not_to be_revoked
34
35
  end
35
36
  end
37
+
38
+ describe :revoke_previous_refresh_token! do
39
+ it "revokes the previous token if existing, and resets the
40
+ `previous_refresh_token` attribute" do
41
+ previous_token = FactoryGirl.create(
42
+ :access_token,
43
+ refresh_token: "refresh_token"
44
+ )
45
+ current_token = FactoryGirl.create(
46
+ :access_token,
47
+ previous_refresh_token: previous_token.refresh_token
48
+ )
49
+
50
+ expect_any_instance_of(
51
+ Doorkeeper::AccessToken
52
+ ).to receive(:revoke).and_call_original
53
+ current_token.revoke_previous_refresh_token!
54
+
55
+ expect(current_token.previous_refresh_token).to be_empty
56
+ expect(previous_token.reload).to be_revoked
57
+ end
58
+ end
36
59
  end
@@ -18,7 +18,7 @@ module Doorkeeper::OAuth
18
18
  it 'issues a new token for the client' do
19
19
  expect do
20
20
  subject.authorize
21
- end.to change { client.access_tokens.count }.by(1)
21
+ end.to change { client.reload.access_tokens.count }.by(1)
22
22
  end
23
23
 
24
24
  it "issues the token with same grant's scopes" do
@@ -11,8 +11,32 @@ class Doorkeeper::OAuth::ClientCredentialsRequest
11
11
  end.to change { Doorkeeper::AccessToken.count }.by(1)
12
12
  end
13
13
 
14
+ context "when reuse_access_token is true" do
15
+ it "returns the existing valid token" do
16
+ allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
17
+ existing_token = subject.call(client, scopes)
18
+
19
+ result = subject.call(client, scopes)
20
+
21
+ expect(Doorkeeper::AccessToken.count).to eq(1)
22
+ expect(result).to eq(existing_token)
23
+ end
24
+ end
25
+
26
+ context "when reuse_access_token is false" do
27
+ it "returns a new token" do
28
+ allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(false)
29
+ existing_token = subject.call(client, scopes)
30
+
31
+ result = subject.call(client, scopes)
32
+
33
+ expect(Doorkeeper::AccessToken.count).to eq(2)
34
+ expect(result).not_to eq(existing_token)
35
+ end
36
+ end
37
+
14
38
  it 'returns false if creation fails' do
15
- expect(Doorkeeper::AccessToken).to receive(:create).and_return(false)
39
+ expect(Doorkeeper::AccessToken).to receive(:find_or_create_for).and_return(false)
16
40
  created = subject.call(client, scopes)
17
41
  expect(created).to be_falsey
18
42
  end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ module Doorkeeper
4
+ module OAuth
5
+ describe CodeResponse do
6
+ describe '.redirect_uri' do
7
+ context 'when generating the redirect URI for an implicit grant' do
8
+ let :pre_auth do
9
+ double(
10
+ :pre_auth,
11
+ client: double(:application, id: 1),
12
+ redirect_uri: 'http://tst.com/cb',
13
+ state: nil,
14
+ scopes: Scopes.from_string('public'),
15
+ )
16
+ end
17
+
18
+ let :auth do
19
+ Authorization::Token.new(pre_auth, double(id: 1)).tap do |c|
20
+ c.issue_token
21
+ allow(c.token).to receive(:expires_in_seconds).and_return(3600)
22
+ end
23
+ end
24
+
25
+ subject { CodeResponse.new(pre_auth, auth, response_on_fragment: true).redirect_uri }
26
+
27
+ it 'includes the remaining TTL of the token relative to the time the token was generated' do
28
+ expect(subject).to include('expires_in=3600')
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -37,9 +37,9 @@ module Doorkeeper::OAuth
37
37
  subject { ErrorResponse.new(name: :some_error, state: :some_state).body }
38
38
 
39
39
  describe '#body' do
40
- it { should have_key(:error) }
41
- it { should have_key(:error_description) }
42
- it { should have_key(:state) }
40
+ it { expect(subject).to have_key(:error) }
41
+ it { expect(subject).to have_key(:error_description) }
42
+ it { expect(subject).to have_key(:state) }
43
43
  end
44
44
  end
45
45
 
@@ -47,15 +47,15 @@ module Doorkeeper::OAuth
47
47
  let(:error_response) { ErrorResponse.new(name: :some_error, state: :some_state) }
48
48
  subject { error_response.authenticate_info }
49
49
 
50
- it { should include("realm=\"#{error_response.realm}\"") }
51
- it { should include("error=\"#{error_response.name}\"") }
52
- it { should include("error_description=\"#{error_response.description}\"") }
50
+ it { expect(subject).to include("realm=\"#{error_response.realm}\"") }
51
+ it { expect(subject).to include("error=\"#{error_response.name}\"") }
52
+ it { expect(subject).to include("error_description=\"#{error_response.description}\"") }
53
53
  end
54
54
 
55
55
  describe '.headers' do
56
56
  subject { ErrorResponse.new(name: :some_error, state: :some_state).headers }
57
57
 
58
- it { should include 'WWW-Authenticate' }
58
+ it { expect(subject).to include 'WWW-Authenticate' }
59
59
  end
60
60
  end
61
61
  end
@@ -4,15 +4,19 @@ require 'doorkeeper/oauth/error'
4
4
 
5
5
  module Doorkeeper::OAuth
6
6
  describe Error do
7
- subject { Error.new(:some_error, :some_state) }
7
+ subject(:error) { Error.new(:some_error, :some_state) }
8
8
 
9
- it { should respond_to(:name) }
10
- it { should respond_to(:state) }
9
+ it { expect(subject).to respond_to(:name) }
10
+ it { expect(subject).to respond_to(:state) }
11
11
 
12
12
  describe :description do
13
13
  it 'is translated from translation messages' do
14
- expect(I18n).to receive(:translate).with(:some_error, scope: [:doorkeeper, :errors, :messages])
15
- subject.description
14
+ expect(I18n).to receive(:translate).with(
15
+ :some_error,
16
+ scope: [:doorkeeper, :errors, :messages],
17
+ default: :server_error
18
+ )
19
+ error.description
16
20
  end
17
21
  end
18
22
  end
@@ -11,23 +11,22 @@ module Doorkeeper::OAuth
11
11
  custom_access_token_expires_in: ->(_app) { nil }
12
12
  )
13
13
  end
14
- let(:credentials) { Client::Credentials.new(client.uid, client.secret) }
15
14
  let(:client) { FactoryGirl.create(:application) }
16
15
  let(:owner) { double :owner, id: 99 }
17
16
 
18
17
  subject do
19
- PasswordAccessTokenRequest.new(server, credentials, owner)
18
+ PasswordAccessTokenRequest.new(server, client, owner)
20
19
  end
21
20
 
22
21
  it 'issues a new token for the client' do
23
22
  expect do
24
23
  subject.authorize
25
- end.to change { client.access_tokens.count }.by(1)
24
+ end.to change { client.reload.access_tokens.count }.by(1)
26
25
  end
27
26
 
28
27
  it 'issues a new token without a client' do
29
28
  expect do
30
- subject.credentials = nil
29
+ subject.client = nil
31
30
  subject.authorize
32
31
  end.to change { Doorkeeper::AccessToken.count }.by(1)
33
32
  end
@@ -35,6 +34,7 @@ module Doorkeeper::OAuth
35
34
  it 'does not issue a new token with an invalid client' do
36
35
  expect do
37
36
  subject.client = nil
37
+ subject.parameters = { client_id: 'bad_id' }
38
38
  subject.authorize
39
39
  end.to_not change { Doorkeeper::AccessToken.count }
40
40
 
@@ -48,7 +48,7 @@ module Doorkeeper::OAuth
48
48
  end
49
49
 
50
50
  it 'optionally accepts the client' do
51
- subject.credentials = nil
51
+ subject.client = nil
52
52
  expect(subject).to be_valid
53
53
  end
54
54
 
@@ -2,6 +2,9 @@ require 'spec_helper_integration'
2
2
 
3
3
  module Doorkeeper::OAuth
4
4
  describe RefreshTokenRequest do
5
+ before do
6
+ allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
7
+ end
5
8
  let(:server) do
6
9
  double :server,
7
10
  access_token_expires_in: 2.minutes,
@@ -16,9 +19,7 @@ module Doorkeeper::OAuth
16
19
  subject { RefreshTokenRequest.new server, refresh_token, credentials }
17
20
 
18
21
  it 'issues a new token for the client' do
19
- expect do
20
- subject.authorize
21
- end.to change { client.access_tokens.count }.by(1)
22
+ expect { subject.authorize }.to change { client.reload.access_tokens.count }.by(1)
22
23
  expect(client.reload.access_tokens.last.expires_in).to eq(120)
23
24
  end
24
25
 
@@ -27,6 +28,8 @@ module Doorkeeper::OAuth
27
28
  access_token_expires_in: 2.minutes,
28
29
  custom_access_token_expires_in: ->(_oauth_client) { 1234 }
29
30
 
31
+ allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
32
+
30
33
  RefreshTokenRequest.new(server, refresh_token, credentials).authorize
31
34
 
32
35
  expect(client.reload.access_tokens.last.expires_in).to eq(1234)
@@ -67,6 +70,34 @@ module Doorkeeper::OAuth
67
70
  expect(subject).to be_valid
68
71
  end
69
72
 
73
+ context 'refresh tokens expire on access token use' do
74
+ let(:server) do
75
+ double :server,
76
+ access_token_expires_in: 2.minutes,
77
+ custom_access_token_expires_in: ->(_oauth_client) { 1234 }
78
+ end
79
+
80
+ before do
81
+ allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(true)
82
+ end
83
+
84
+ it 'issues a new token for the client' do
85
+ expect { subject.authorize }.to change { client.reload.access_tokens.count }.by(1)
86
+ end
87
+
88
+ it 'does not revoke the previous token' do
89
+ subject.authorize
90
+ expect(refresh_token).not_to be_revoked
91
+ end
92
+
93
+ it 'sets the previous refresh token in the new access token' do
94
+ subject.authorize
95
+ expect(
96
+ client.access_tokens.last.previous_refresh_token
97
+ ).to eq(refresh_token.refresh_token)
98
+ end
99
+ end
100
+
70
101
  context 'clientless access tokens' do
71
102
  let!(:refresh_token) { FactoryGirl.create(:clientless_access_token, use_refresh_token: true) }
72
103
 
@@ -47,7 +47,7 @@ module Doorkeeper::OAuth
47
47
 
48
48
  subject { Scopes.from_string(string) }
49
49
 
50
- it { should be_a(Scopes) }
50
+ it { expect(subject).to be_a(Scopes) }
51
51
 
52
52
  describe '#all' do
53
53
  it 'should be an array of the expected scopes' do
@@ -67,7 +67,6 @@ module Doorkeeper::OAuth
67
67
 
68
68
  it 'does not change the existing object' do
69
69
  origin = Scopes.from_string('public')
70
- new_scope = origin + Scopes.from_string('admin')
71
70
  expect(origin.to_s).to eq('public')
72
71
  end
73
72
 
@@ -30,7 +30,7 @@ module Doorkeeper
30
30
  it 'stops at the first credentials found' do
31
31
  not_called_method = double
32
32
  expect(not_called_method).not_to receive(:call)
33
- Token.from_request request, ->(r) {}, method, not_called_method
33
+ Token.from_request request, ->(_r) {}, method, not_called_method
34
34
  end
35
35
 
36
36
  it 'returns the credential from extractor method' do
@@ -96,13 +96,20 @@ module Doorkeeper
96
96
  end
97
97
 
98
98
  describe :authenticate do
99
- let(:finder) { double :finder }
100
-
101
- it 'calls the finder if token was found' do
102
- token = ->(r) { 'token' }
99
+ it 'calls the finder if token was returned' do
100
+ token = ->(_r) { 'token' }
103
101
  expect(AccessToken).to receive(:by_token).with('token')
104
102
  Token.authenticate double, token
105
103
  end
104
+
105
+ it 'revokes previous refresh_token if token was found' do
106
+ token = ->(_r) { 'token' }
107
+ expect(
108
+ AccessToken
109
+ ).to receive(:by_token).with('token').and_return(token)
110
+ expect(token).to receive(:revoke_previous_refresh_token!)
111
+ Token.authenticate double, token
112
+ end
106
113
  end
107
114
  end
108
115
  end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+ require 'doorkeeper/request/strategy'
3
+
4
+ module Doorkeeper
5
+ module Request
6
+ describe Strategy do
7
+ let(:server) { double }
8
+ subject(:strategy) { Strategy.new(server) }
9
+
10
+ describe :initialize do
11
+ it "sets the server attribute" do
12
+ expect(strategy.server).to eq server
13
+ end
14
+ end
15
+
16
+ describe :request do
17
+ it "requires an implementation" do
18
+ expect { strategy.request }.to raise_exception NotImplementedError
19
+ end
20
+ end
21
+
22
+ describe "a sample Strategy subclass" do
23
+ let(:fake_request) { double }
24
+
25
+ let(:strategy_class) do
26
+ subclass = Class.new(Strategy) do
27
+ class << self
28
+ attr_accessor :fake_request
29
+ end
30
+
31
+ def request
32
+ self.class.fake_request
33
+ end
34
+ end
35
+
36
+ subclass.fake_request = fake_request
37
+ subclass
38
+ end
39
+
40
+ subject(:strategy) { strategy_class.new(server) }
41
+
42
+ it "provides a request implementation" do
43
+ expect(strategy.request).to eq fake_request
44
+ end
45
+
46
+ it "authorizes the request" do
47
+ expect(fake_request).to receive :authorize
48
+ strategy.authorize
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -45,7 +45,7 @@ describe Doorkeeper::Server do
45
45
 
46
46
  it 'builds the request with selected strategy' do
47
47
  stub_const 'Doorkeeper::Request::Code', fake_class
48
- expect(fake_class).to receive(:build).with(subject)
48
+ expect(fake_class).to receive(:new).with(subject)
49
49
  subject.authorization_request :code
50
50
  end
51
51
  end
@@ -3,7 +3,7 @@ require 'spec_helper_integration'
3
3
  describe Doorkeeper::AccessGrant do
4
4
  subject { FactoryGirl.build(:access_grant) }
5
5
 
6
- it { should be_valid }
6
+ it { expect(subject).to be_valid }
7
7
 
8
8
  it_behaves_like 'an accessible token'
9
9
  it_behaves_like 'a revocable token'
@@ -14,23 +14,23 @@ describe Doorkeeper::AccessGrant do
14
14
  describe 'validations' do
15
15
  it 'is invalid without resource_owner_id' do
16
16
  subject.resource_owner_id = nil
17
- should_not be_valid
17
+ expect(subject).not_to be_valid
18
18
  end
19
19
 
20
20
  it 'is invalid without application_id' do
21
21
  subject.application_id = nil
22
- should_not be_valid
22
+ expect(subject).not_to be_valid
23
23
  end
24
24
 
25
25
  it 'is invalid without token' do
26
26
  subject.save
27
27
  subject.token = nil
28
- should_not be_valid
28
+ expect(subject).not_to be_valid
29
29
  end
30
30
 
31
31
  it 'is invalid without expires_in' do
32
32
  subject.expires_in = nil
33
- should_not be_valid
33
+ expect(subject).not_to be_valid
34
34
  end
35
35
  end
36
36
  end
@@ -4,7 +4,7 @@ module Doorkeeper
4
4
  describe AccessToken do
5
5
  subject { FactoryGirl.build(:access_token) }
6
6
 
7
- it { should be_valid }
7
+ it { expect(subject).to be_valid }
8
8
 
9
9
  it_behaves_like 'an accessible token'
10
10
  it_behaves_like 'a revocable token'
@@ -12,6 +12,11 @@ module Doorkeeper
12
12
  let(:factory_name) { :access_token }
13
13
  end
14
14
 
15
+ module CustomGeneratorArgs
16
+ def self.generate
17
+ end
18
+ end
19
+
15
20
  describe :generate_token do
16
21
  it 'generates a token using the default method' do
17
22
  FactoryGirl.create :access_token
@@ -21,6 +26,10 @@ module Doorkeeper
21
26
  end
22
27
 
23
28
  it 'generates a token using a custom object' do
29
+ eigenclass = class << CustomGeneratorArgs; self; end
30
+ eigenclass.class_eval do
31
+ remove_method :generate
32
+ end
24
33
  module CustomGeneratorArgs
25
34
  def self.generate(opts = {})
26
35
  "custom_generator_token_#{opts[:resource_owner_id]}"
@@ -37,6 +46,10 @@ module Doorkeeper
37
46
  end
38
47
 
39
48
  it 'allows the custom generator to access the application details' do
49
+ eigenclass = class << CustomGeneratorArgs; self; end
50
+ eigenclass.class_eval do
51
+ remove_method :generate
52
+ end
40
53
  module CustomGeneratorArgs
41
54
  def self.generate(opts = {})
42
55
  "custom_generator_token_#{opts[:application].name}"
@@ -53,6 +66,10 @@ module Doorkeeper
53
66
  end
54
67
 
55
68
  it 'allows the custom generator to access the scopes' do
69
+ eigenclass = class << CustomGeneratorArgs; self; end
70
+ eigenclass.class_eval do
71
+ remove_method :generate
72
+ end
56
73
  module CustomGeneratorArgs
57
74
  def self.generate(opts = {})
58
75
  "custom_generator_token_#{opts[:scopes].count}_#{opts[:scopes]}"
@@ -70,6 +87,10 @@ module Doorkeeper
70
87
  end
71
88
 
72
89
  it 'allows the custom generator to access the expiry length' do
90
+ eigenclass = class << CustomGeneratorArgs; self; end
91
+ eigenclass.class_eval do
92
+ remove_method :generate
93
+ end
73
94
  module CustomGeneratorArgs
74
95
  def self.generate(opts = {})
75
96
  "custom_generator_token_#{opts[:expires_in]}"
@@ -85,6 +106,23 @@ module Doorkeeper
85
106
  expect(token.token).to eq 'custom_generator_token_7200'
86
107
  end
87
108
 
109
+ it 'allows the custom generator to access the created time' do
110
+ module CustomGeneratorArgs
111
+ def self.generate(opts = {})
112
+ "custom_generator_token_#{opts[:created_at].to_i}"
113
+ end
114
+ end
115
+
116
+ Doorkeeper.configure do
117
+ orm DOORKEEPER_ORM
118
+ access_token_generator "Doorkeeper::CustomGeneratorArgs"
119
+ end
120
+
121
+ token = FactoryGirl.create :access_token
122
+ created_at = token.created_at
123
+ expect(token.token).to eq "custom_generator_token_#{created_at.to_i}"
124
+ end
125
+
88
126
  it 'raises an error if the custom object does not support generate' do
89
127
  module NoGenerate
90
128
  end
@@ -123,7 +161,7 @@ module Doorkeeper
123
161
  it 'is not valid if token exists' do
124
162
  token1 = FactoryGirl.create :access_token, use_refresh_token: true
125
163
  token2 = FactoryGirl.create :access_token, use_refresh_token: true
126
- token2.send :write_attribute, :refresh_token, token1.refresh_token
164
+ token2.refresh_token = token1.refresh_token
127
165
  expect(token2).not_to be_valid
128
166
  end
129
167
 
@@ -131,9 +169,9 @@ module Doorkeeper
131
169
  token1 = FactoryGirl.create :access_token, use_refresh_token: true
132
170
  token2 = FactoryGirl.create :access_token, use_refresh_token: true
133
171
  expect do
134
- token2.write_attribute :refresh_token, token1.refresh_token
172
+ token2.refresh_token = token1.refresh_token
135
173
  token2.save(validate: false)
136
- end.to raise_error
174
+ end.to raise_error(uniqueness_error)
137
175
  end
138
176
  end
139
177
 
@@ -141,7 +179,13 @@ module Doorkeeper
141
179
  it 'is valid without resource_owner_id' do
142
180
  # For client credentials flow
143
181
  subject.resource_owner_id = nil
144
- should be_valid
182
+ expect(subject).to be_valid
183
+ end
184
+
185
+ it 'is valid without application_id' do
186
+ # For resource owner credentials flow
187
+ subject.application_id = nil
188
+ expect(subject).to be_valid
145
189
  end
146
190
  end
147
191
 
@@ -90,7 +90,7 @@ module Doorkeeper
90
90
  app1 = FactoryGirl.create(:application)
91
91
  app2 = FactoryGirl.create(:application)
92
92
  app2.uid = app1.uid
93
- expect { app2.save!(validate: false) }.to raise_error
93
+ expect { app2.save!(validate: false) }.to raise_error(uniqueness_error)
94
94
  end
95
95
 
96
96
  it 'generate secret on create' do
@@ -129,7 +129,7 @@ module Doorkeeper
129
129
 
130
130
  it 'should destroy its access tokens' do
131
131
  FactoryGirl.create(:access_token, application: new_application)
132
- FactoryGirl.create(:access_token, application: new_application, revoked_at: Time.now)
132
+ FactoryGirl.create(:access_token, application: new_application, revoked_at: Time.now.utc)
133
133
  expect do
134
134
  new_application.destroy
135
135
  end.to change { Doorkeeper::AccessToken.count }.by(-2)
@@ -166,14 +166,6 @@ module Doorkeeper
166
166
  FactoryGirl.create(:access_token, resource_owner_id: resource_owner.id, application: application)
167
167
  expect(Application.authorized_for(resource_owner)).to eq([application])
168
168
  end
169
-
170
- it 'should fail to mass assign a new application', if: ::Rails::VERSION::MAJOR < 4 do
171
- mass_assign = { name: 'Something',
172
- redirect_uri: 'http://somewhere.com/something',
173
- uid: 123,
174
- secret: 'something' }
175
- expect(Application.create(mass_assign).uid).not_to eq(123)
176
- end
177
169
  end
178
170
 
179
171
  describe :authenticate do
@@ -128,3 +128,29 @@ feature 'Authorization Code Flow' do
128
128
  end
129
129
  end
130
130
  end
131
+
132
+ describe 'Authorization Code Flow' do
133
+ before do
134
+ Doorkeeper.configure do
135
+ orm DOORKEEPER_ORM
136
+ use_refresh_token
137
+ end
138
+ client_exists
139
+ end
140
+
141
+ context 'issuing a refresh token' do
142
+ before do
143
+ authorization_code_exists application: @client
144
+ end
145
+
146
+ it 'second of simultaneous client requests get an error for revoked acccess token' do
147
+ authorization_code = Doorkeeper::AccessGrant.first.token
148
+ allow_any_instance_of(Doorkeeper::AccessGrant).to receive(:revoked?).and_return(false, true)
149
+
150
+ post token_endpoint_url(code: authorization_code, client: @client)
151
+
152
+ should_not_have_json 'access_token'
153
+ should_have_json 'error', 'invalid_grant'
154
+ end
155
+ end
156
+ end