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.
- checksums.yaml +4 -4
- data/.hound.yml +4 -0
- data/.travis.yml +9 -7
- data/CONTRIBUTING.md +2 -0
- data/Gemfile +10 -3
- data/NEWS.md +79 -2
- data/README.md +56 -51
- data/RELEASING.md +2 -2
- data/Rakefile +1 -1
- data/app/assets/stylesheets/doorkeeper/admin/application.css +1 -5
- data/app/controllers/doorkeeper/application_metal_controller.rb +1 -2
- data/app/controllers/doorkeeper/applications_controller.rb +2 -2
- data/app/controllers/doorkeeper/authorizations_controller.rb +1 -1
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -1
- data/app/controllers/doorkeeper/tokens_controller.rb +1 -1
- data/app/helpers/doorkeeper/dashboard_helper.rb +13 -11
- data/app/views/doorkeeper/applications/show.html.erb +1 -1
- data/app/views/doorkeeper/authorizations/new.html.erb +1 -1
- data/app/views/layouts/doorkeeper/admin.html.erb +5 -2
- data/config/locales/en.yml +1 -0
- data/doorkeeper.gemspec +7 -7
- data/lib/doorkeeper/config.rb +10 -15
- data/lib/doorkeeper/engine.rb +11 -7
- data/lib/doorkeeper/errors.rb +6 -0
- data/lib/doorkeeper/helpers/controller.rb +7 -1
- data/lib/doorkeeper/models/access_grant_mixin.rb +9 -5
- data/lib/doorkeeper/models/access_token_mixin.rb +28 -22
- data/lib/doorkeeper/models/application_mixin.rb +3 -7
- data/lib/doorkeeper/models/concerns/expirable.rb +2 -2
- data/lib/doorkeeper/models/concerns/ownership.rb +6 -1
- data/lib/doorkeeper/models/concerns/revocable.rb +19 -2
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +1 -1
- data/lib/doorkeeper/oauth/authorization_code_request.rb +10 -5
- data/lib/doorkeeper/oauth/client/credentials.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +3 -4
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +2 -1
- data/lib/doorkeeper/oauth/client_credentials_request.rb +7 -4
- data/lib/doorkeeper/oauth/code_response.rb +13 -14
- data/lib/doorkeeper/oauth/error.rb +5 -1
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +2 -1
- data/lib/doorkeeper/oauth/password_access_token_request.rb +6 -10
- data/lib/doorkeeper/oauth/refresh_token_request.rb +29 -12
- data/lib/doorkeeper/oauth/scopes.rb +2 -2
- data/lib/doorkeeper/oauth/token.rb +6 -5
- data/lib/doorkeeper/oauth/token_response.rb +1 -1
- data/lib/doorkeeper/orm/active_record/access_grant.rb +2 -2
- data/lib/doorkeeper/orm/active_record/access_token.rb +10 -2
- data/lib/doorkeeper/orm/active_record/application.rb +4 -9
- data/lib/doorkeeper/orm/active_record.rb +0 -15
- data/lib/doorkeeper/rails/helpers.rb +13 -3
- data/lib/doorkeeper/rails/routes/mapper.rb +1 -1
- data/lib/doorkeeper/rails/routes.rb +2 -1
- data/lib/doorkeeper/request/authorization_code.rb +10 -15
- data/lib/doorkeeper/request/client_credentials.rb +9 -15
- data/lib/doorkeeper/request/code.rb +7 -13
- data/lib/doorkeeper/request/password.rb +18 -13
- data/lib/doorkeeper/request/refresh_token.rb +11 -13
- data/lib/doorkeeper/request/strategy.rb +17 -0
- data/lib/doorkeeper/request/token.rb +7 -13
- data/lib/doorkeeper/request.rb +18 -8
- data/lib/doorkeeper/server.rb +2 -2
- data/lib/doorkeeper/version.rb +1 -1
- data/lib/doorkeeper.rb +1 -1
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +29 -0
- data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb +1 -1
- data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb +11 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +2 -2
- data/lib/generators/doorkeeper/templates/migration.rb +23 -5
- data/spec/controllers/authorizations_controller_spec.rb +0 -14
- data/spec/controllers/protected_resources_controller_spec.rb +138 -15
- data/spec/controllers/tokens_controller_spec.rb +30 -0
- data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -4
- data/spec/dummy/app/controllers/home_controller.rb +1 -1
- data/spec/dummy/app/controllers/metal_controller.rb +1 -1
- data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +3 -3
- data/spec/dummy/app/models/user.rb +0 -4
- data/spec/dummy/config/application.rb +2 -36
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/dummy/config/environments/test.rb +4 -15
- data/spec/dummy/config/initializers/active_record_belongs_to_required_by_default.rb +6 -0
- data/spec/dummy/config/initializers/doorkeeper.rb +2 -2
- data/spec/dummy/db/migrate/{20130902165751_create_doorkeeper_tables.rb → 20151223192035_create_doorkeeper_tables.rb} +24 -5
- data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +11 -0
- data/spec/dummy/db/schema.rb +23 -22
- data/spec/lib/config_spec.rb +2 -2
- data/spec/lib/models/revocable_spec.rb +27 -4
- data/spec/lib/oauth/authorization_code_request_spec.rb +1 -1
- data/spec/lib/oauth/client_credentials/creator_spec.rb +25 -1
- data/spec/lib/oauth/code_response_spec.rb +34 -0
- data/spec/lib/oauth/error_response_spec.rb +7 -7
- data/spec/lib/oauth/error_spec.rb +9 -5
- data/spec/lib/oauth/password_access_token_request_spec.rb +5 -5
- data/spec/lib/oauth/refresh_token_request_spec.rb +34 -3
- data/spec/lib/oauth/scopes_spec.rb +1 -2
- data/spec/lib/oauth/token_spec.rb +12 -5
- data/spec/lib/request/strategy_spec.rb +53 -0
- data/spec/lib/server_spec.rb +1 -1
- data/spec/models/doorkeeper/access_grant_spec.rb +5 -5
- data/spec/models/doorkeeper/access_token_spec.rb +49 -5
- data/spec/models/doorkeeper/application_spec.rb +2 -10
- data/spec/requests/flows/authorization_code_spec.rb +26 -0
- data/spec/requests/flows/password_spec.rb +26 -5
- data/spec/requests/flows/refresh_token_spec.rb +95 -17
- data/spec/spec_helper_integration.rb +10 -0
- data/spec/support/helpers/model_helper.rb +27 -5
- data/spec/support/http_method_shim.rb +24 -0
- data/spec/support/shared/controllers_shared_context.rb +13 -4
- data/spec/support/shared/models_shared_examples.rb +1 -1
- metadata +46 -38
- data/lib/generators/doorkeeper/application_scopes_generator.rb +0 -34
- data/lib/generators/doorkeeper/templates/add_scopes_to_oauth_applications.rb +0 -5
- data/spec/dummy/db/migrate/20141209001746_add_scopes_to_oauth_applications.rb +0 -5
- /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
|
-
|
15
|
-
|
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(:
|
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 {
|
41
|
-
it {
|
42
|
-
it {
|
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 {
|
51
|
-
it {
|
52
|
-
it {
|
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 {
|
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 {
|
10
|
-
it {
|
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(
|
15
|
-
|
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,
|
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.
|
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.
|
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
|
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 {
|
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, ->(
|
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
|
-
|
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
|
data/spec/lib/server_spec.rb
CHANGED
@@ -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(:
|
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 {
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 {
|
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.
|
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.
|
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
|
-
|
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
|