doorkeeper 5.3.3 → 5.4.0.rc1
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/Appraisals +0 -14
- data/CHANGELOG.md +35 -10
- data/Dangerfile +7 -7
- data/Dockerfile +2 -2
- data/Gemfile +9 -9
- data/README.md +6 -4
- data/app/controllers/doorkeeper/applications_controller.rb +7 -7
- data/app/controllers/doorkeeper/authorizations_controller.rb +31 -12
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +3 -3
- data/app/controllers/doorkeeper/tokens_controller.rb +57 -20
- data/app/views/doorkeeper/applications/show.html.erb +19 -2
- data/bin/console +14 -0
- data/config/locales/en.yml +3 -1
- data/doorkeeper.gemspec +1 -1
- data/gemfiles/rails_5_0.gemfile +8 -7
- data/gemfiles/rails_5_1.gemfile +8 -7
- data/gemfiles/rails_5_2.gemfile +8 -7
- data/gemfiles/rails_6_0.gemfile +8 -7
- data/gemfiles/rails_master.gemfile +8 -7
- data/lib/doorkeeper.rb +106 -79
- data/lib/doorkeeper/config.rb +40 -17
- data/lib/doorkeeper/config/abstract_builder.rb +28 -0
- data/lib/doorkeeper/config/option.rb +28 -14
- data/lib/doorkeeper/grape/helpers.rb +1 -1
- data/lib/doorkeeper/models/access_grant_mixin.rb +9 -11
- data/lib/doorkeeper/models/access_token_mixin.rb +100 -41
- data/lib/doorkeeper/models/concerns/resource_ownerable.rb +47 -0
- data/lib/doorkeeper/models/concerns/revocable.rb +1 -1
- data/lib/doorkeeper/models/concerns/scopes.rb +5 -1
- data/lib/doorkeeper/models/concerns/secret_storable.rb +1 -3
- data/lib/doorkeeper/oauth/authorization/code.rb +14 -5
- data/lib/doorkeeper/oauth/authorization/context.rb +2 -2
- data/lib/doorkeeper/oauth/authorization/token.rb +7 -11
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +4 -4
- data/lib/doorkeeper/oauth/authorization_code_request.rb +18 -8
- data/lib/doorkeeper/oauth/base_request.rb +11 -19
- data/lib/doorkeeper/oauth/client.rb +1 -1
- data/lib/doorkeeper/oauth/client/credentials.rb +2 -4
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +25 -7
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +3 -2
- data/lib/doorkeeper/oauth/client_credentials/validator.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials_request.rb +8 -7
- data/lib/doorkeeper/oauth/code_request.rb +1 -1
- data/lib/doorkeeper/oauth/code_response.rb +6 -2
- data/lib/doorkeeper/oauth/error_response.rb +2 -4
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +1 -5
- data/lib/doorkeeper/oauth/hooks/context.rb +21 -0
- data/lib/doorkeeper/oauth/invalid_token_response.rb +2 -2
- data/lib/doorkeeper/oauth/password_access_token_request.rb +3 -5
- data/lib/doorkeeper/oauth/pre_authorization.rb +32 -27
- data/lib/doorkeeper/oauth/refresh_token_request.rb +18 -22
- data/lib/doorkeeper/oauth/token.rb +1 -1
- data/lib/doorkeeper/oauth/token_introspection.rb +3 -3
- data/lib/doorkeeper/oauth/token_request.rb +2 -2
- data/lib/doorkeeper/oauth/token_response.rb +1 -1
- data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +7 -2
- data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +6 -2
- data/lib/doorkeeper/orm/active_record/mixins/application.rb +9 -64
- data/lib/doorkeeper/rails/routes.rb +13 -17
- data/lib/doorkeeper/rails/routes/abstract_router.rb +35 -0
- data/lib/doorkeeper/rails/routes/mapper.rb +2 -2
- data/lib/doorkeeper/rails/routes/registry.rb +45 -0
- data/lib/doorkeeper/request/strategy.rb +2 -2
- data/lib/doorkeeper/server.rb +3 -3
- data/lib/doorkeeper/version.rb +3 -3
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +1 -1
- data/lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb +39 -0
- data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb +2 -0
- data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb +2 -0
- data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +2 -0
- data/lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb +17 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +39 -3
- data/lib/generators/doorkeeper/templates/migration.rb.erb +2 -0
- data/spec/controllers/applications_controller_spec.rb +2 -2
- data/spec/controllers/authorizations_controller_spec.rb +165 -30
- data/spec/controllers/tokens_controller_spec.rb +6 -5
- data/spec/dummy/app/helpers/application_helper.rb +1 -1
- data/spec/dummy/app/models/user.rb +5 -1
- data/spec/dummy/config/application.rb +6 -4
- data/spec/dummy/config/boot.rb +4 -4
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/dummy/config/routes.rb +4 -4
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +2 -2
- data/spec/dummy/db/schema.rb +3 -1
- data/spec/factories.rb +1 -1
- data/spec/generators/enable_polymorphic_resource_owner_generator_spec.rb +47 -0
- data/spec/lib/config_spec.rb +15 -11
- data/spec/lib/models/revocable_spec.rb +2 -3
- data/spec/lib/models/scopes_spec.rb +8 -0
- data/spec/lib/oauth/authorization_code_request_spec.rb +25 -15
- data/spec/lib/oauth/base_request_spec.rb +6 -20
- data/spec/lib/oauth/client_credentials/creator_spec.rb +90 -89
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +84 -86
- data/spec/lib/oauth/client_credentials/validation_spec.rb +38 -40
- data/spec/lib/oauth/client_credentials_request_spec.rb +5 -4
- data/spec/lib/oauth/code_request_spec.rb +1 -1
- data/spec/lib/oauth/code_response_spec.rb +5 -1
- data/spec/lib/oauth/error_response_spec.rb +1 -1
- data/spec/lib/oauth/password_access_token_request_spec.rb +24 -13
- data/spec/lib/oauth/pre_authorization_spec.rb +13 -18
- data/spec/lib/oauth/refresh_token_request_spec.rb +19 -30
- data/spec/lib/oauth/token_request_spec.rb +14 -7
- data/spec/lib/option_spec.rb +51 -0
- data/spec/lib/stale_records_cleaner_spec.rb +18 -5
- data/spec/models/doorkeeper/access_grant_spec.rb +18 -4
- data/spec/models/doorkeeper/access_token_spec.rb +507 -479
- data/spec/models/doorkeeper/application_spec.rb +22 -62
- data/spec/requests/endpoints/token_spec.rb +5 -1
- data/spec/requests/flows/authorization_code_errors_spec.rb +4 -1
- data/spec/requests/flows/authorization_code_spec.rb +6 -1
- data/spec/requests/flows/client_credentials_spec.rb +41 -0
- data/spec/requests/flows/refresh_token_spec.rb +16 -8
- data/spec/requests/flows/revoke_token_spec.rb +143 -104
- data/spec/support/helpers/access_token_request_helper.rb +1 -0
- data/spec/support/helpers/authorization_request_helper.rb +4 -4
- data/spec/support/helpers/config_helper.rb +1 -1
- data/spec/support/shared/controllers_shared_context.rb +2 -2
- data/spec/support/shared/models_shared_examples.rb +6 -4
- metadata +16 -5
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Doorkeeper, "configuration option DSL" do
|
6
|
+
class Extension
|
7
|
+
def self.configure(&block)
|
8
|
+
@config = Config::Builder.new(Config.new, &block).build
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.configuration
|
12
|
+
@config || (raise Errors::MissingConfiguration)
|
13
|
+
end
|
14
|
+
|
15
|
+
class Config
|
16
|
+
class Builder < Doorkeeper::Config::AbstractBuilder
|
17
|
+
def enforce_something
|
18
|
+
@config.instance_variable_set(:@enforce_something, true)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def enforce_something?
|
23
|
+
if defined?(@enforce_something)
|
24
|
+
@enforce_something
|
25
|
+
else
|
26
|
+
false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.builder_class
|
31
|
+
Config::Builder
|
32
|
+
end
|
33
|
+
|
34
|
+
extend Doorkeeper::Config::Option
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "allows to define custom options in extensions" do
|
39
|
+
expect do
|
40
|
+
Extension::Config.option(:some_option, default: 1)
|
41
|
+
end.not_to raise_error
|
42
|
+
|
43
|
+
Extension.configure do
|
44
|
+
some_option 20
|
45
|
+
enforce_something
|
46
|
+
end
|
47
|
+
|
48
|
+
expect(Extension.configuration.some_option).to eq(20)
|
49
|
+
expect(Extension.configuration.enforce_something?).to be(true)
|
50
|
+
end
|
51
|
+
end
|
@@ -10,6 +10,7 @@ describe Doorkeeper::StaleRecordsCleaner do
|
|
10
10
|
access_grant: Doorkeeper::AccessGrant,
|
11
11
|
}
|
12
12
|
end
|
13
|
+
let(:resource_owner) { FactoryBot.create(:resource_owner) }
|
13
14
|
|
14
15
|
context "when ORM has no cleaner class" do
|
15
16
|
it "raises an error" do
|
@@ -30,7 +31,10 @@ describe Doorkeeper::StaleRecordsCleaner do
|
|
30
31
|
|
31
32
|
context "with revoked record" do
|
32
33
|
before do
|
33
|
-
FactoryBot.create model_name,
|
34
|
+
FactoryBot.create model_name,
|
35
|
+
revoked_at: Time.current - 1.minute,
|
36
|
+
resource_owner_id: resource_owner.id,
|
37
|
+
resource_owner_type: resource_owner.class.name
|
34
38
|
end
|
35
39
|
|
36
40
|
it "removes the record" do
|
@@ -40,7 +44,9 @@ describe Doorkeeper::StaleRecordsCleaner do
|
|
40
44
|
|
41
45
|
context "with record revoked in the future" do
|
42
46
|
before do
|
43
|
-
FactoryBot.create model_name, revoked_at: Time.current + 1.minute
|
47
|
+
FactoryBot.create model_name, revoked_at: Time.current + 1.minute,
|
48
|
+
resource_owner_id: resource_owner.id,
|
49
|
+
resource_owner_type: resource_owner.class.name
|
44
50
|
end
|
45
51
|
|
46
52
|
it "keeps the record" do
|
@@ -50,7 +56,9 @@ describe Doorkeeper::StaleRecordsCleaner do
|
|
50
56
|
|
51
57
|
context "with unrevoked record" do
|
52
58
|
before do
|
53
|
-
FactoryBot.create model_name, revoked_at: nil
|
59
|
+
FactoryBot.create model_name, revoked_at: nil,
|
60
|
+
resource_owner_id: resource_owner.id,
|
61
|
+
resource_owner_type: resource_owner.class.name
|
54
62
|
end
|
55
63
|
|
56
64
|
it "keeps the record" do
|
@@ -66,7 +74,10 @@ describe Doorkeeper::StaleRecordsCleaner do
|
|
66
74
|
|
67
75
|
context "with record that is expired" do
|
68
76
|
before do
|
69
|
-
FactoryBot.create model_name,
|
77
|
+
FactoryBot.create model_name,
|
78
|
+
created_at: expiry_border - 1.minute,
|
79
|
+
resource_owner_id: resource_owner.id,
|
80
|
+
resource_owner_type: resource_owner.class.name
|
70
81
|
end
|
71
82
|
|
72
83
|
it "removes the record" do
|
@@ -76,7 +87,9 @@ describe Doorkeeper::StaleRecordsCleaner do
|
|
76
87
|
|
77
88
|
context "with record that is not expired" do
|
78
89
|
before do
|
79
|
-
FactoryBot.create model_name, created_at: expiry_border + 1.minute
|
90
|
+
FactoryBot.create model_name, created_at: expiry_border + 1.minute,
|
91
|
+
resource_owner_id: resource_owner.id,
|
92
|
+
resource_owner_type: resource_owner.class.name
|
80
93
|
end
|
81
94
|
|
82
95
|
it "keeps the record" do
|
@@ -3,10 +3,18 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
5
|
describe Doorkeeper::AccessGrant do
|
6
|
+
let(:resource_owner) { FactoryBot.create(:resource_owner) }
|
6
7
|
let(:client) { FactoryBot.build_stubbed(:application) }
|
7
8
|
let(:clazz) { Doorkeeper::AccessGrant }
|
8
9
|
|
9
|
-
subject
|
10
|
+
subject do
|
11
|
+
FactoryBot.build(
|
12
|
+
:access_grant,
|
13
|
+
application: client,
|
14
|
+
resource_owner_id: resource_owner.id,
|
15
|
+
resource_owner_type: resource_owner.class.name,
|
16
|
+
)
|
17
|
+
end
|
10
18
|
|
11
19
|
it { expect(subject).to be_valid }
|
12
20
|
|
@@ -17,7 +25,12 @@ describe Doorkeeper::AccessGrant do
|
|
17
25
|
end
|
18
26
|
|
19
27
|
context "with hashing enabled" do
|
20
|
-
let(:
|
28
|
+
let(:resource_owner) { FactoryBot.create(:resource_owner) }
|
29
|
+
let(:grant) do
|
30
|
+
FactoryBot.create :access_grant,
|
31
|
+
resource_owner_id: resource_owner.id,
|
32
|
+
resource_owner_type: resource_owner.class.name
|
33
|
+
end
|
21
34
|
include_context "with token hashing enabled"
|
22
35
|
|
23
36
|
it "holds a volatile plaintext token when created" do
|
@@ -117,12 +130,12 @@ describe Doorkeeper::AccessGrant do
|
|
117
130
|
end
|
118
131
|
|
119
132
|
describe ".revoke_all_for" do
|
120
|
-
let(:resource_owner) { double(id: 100) }
|
121
133
|
let(:application) { FactoryBot.create :application }
|
122
134
|
let(:default_attributes) do
|
123
135
|
{
|
124
136
|
application: application,
|
125
137
|
resource_owner_id: resource_owner.id,
|
138
|
+
resource_owner_type: resource_owner.class.name,
|
126
139
|
}
|
127
140
|
end
|
128
141
|
|
@@ -148,9 +161,10 @@ describe Doorkeeper::AccessGrant do
|
|
148
161
|
end
|
149
162
|
|
150
163
|
it "matches resource owner" do
|
164
|
+
other_resource_owner = FactoryBot.create(:resource_owner)
|
151
165
|
access_grant_for_different_owner = FactoryBot.create(
|
152
166
|
:access_grant,
|
153
|
-
default_attributes.merge(resource_owner_id:
|
167
|
+
default_attributes.merge(resource_owner_id: other_resource_owner.id),
|
154
168
|
)
|
155
169
|
|
156
170
|
described_class.revoke_all_for application.id, resource_owner
|
@@ -2,621 +2,649 @@
|
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
let(:clazz) { Doorkeeper::AccessToken }
|
8
|
-
subject { FactoryBot.build(:access_token) }
|
5
|
+
RSpec.describe Doorkeeper::AccessToken do
|
6
|
+
subject { FactoryBot.build(:access_token) }
|
9
7
|
|
10
|
-
|
8
|
+
it { expect(subject).to be_valid }
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
it_behaves_like "an accessible token"
|
11
|
+
it_behaves_like "a revocable token"
|
12
|
+
it_behaves_like "a unique token" do
|
13
|
+
let(:factory_name) { :access_token }
|
14
|
+
end
|
15
|
+
|
16
|
+
module CustomGeneratorArgs
|
17
|
+
def self.generate; end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#generate_token" do
|
21
|
+
it "generates a token using the default method" do
|
22
|
+
FactoryBot.create :access_token
|
17
23
|
|
18
|
-
|
19
|
-
|
24
|
+
token = FactoryBot.create :access_token
|
25
|
+
expect(token.token).to be_a(String)
|
20
26
|
end
|
21
27
|
|
22
|
-
|
23
|
-
|
24
|
-
|
28
|
+
context "with hashing enabled" do
|
29
|
+
let(:token) { FactoryBot.create :access_token }
|
30
|
+
include_context "with token hashing enabled"
|
25
31
|
|
26
|
-
|
27
|
-
expect(token.
|
28
|
-
|
32
|
+
it "holds a volatile plaintext token when created" do
|
33
|
+
expect(token.plaintext_token).to be_a(String)
|
34
|
+
expect(token.token)
|
35
|
+
.to eq(hashed_or_plain_token_func.call(token.plaintext_token))
|
29
36
|
|
30
|
-
|
31
|
-
|
32
|
-
|
37
|
+
# Finder method only finds the hashed token
|
38
|
+
loaded = described_class.find_by(token: token.token)
|
39
|
+
expect(loaded).to eq(token)
|
40
|
+
expect(loaded.plaintext_token).to be_nil
|
41
|
+
expect(loaded.token).to eq(token.token)
|
42
|
+
end
|
33
43
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
.to eq(hashed_or_plain_token_func.call(token.plaintext_token))
|
44
|
+
it "does not find_by plain text tokens" do
|
45
|
+
expect(described_class.find_by(token: token.plaintext_token)).to be_nil
|
46
|
+
end
|
38
47
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
expect(loaded.plaintext_token).to be_nil
|
43
|
-
expect(loaded.token).to eq(token.token)
|
44
|
-
end
|
48
|
+
describe "with having a plain text token" do
|
49
|
+
let(:plain_text_token) { "plain text token" }
|
50
|
+
let(:access_token) { FactoryBot.create :access_token }
|
45
51
|
|
46
|
-
|
47
|
-
|
52
|
+
before do
|
53
|
+
# Assume we have a plain text token from before activating the option
|
54
|
+
access_token.update_column(:token, plain_text_token)
|
48
55
|
end
|
49
56
|
|
50
|
-
|
51
|
-
|
52
|
-
|
57
|
+
context "without fallback lookup" do
|
58
|
+
it "does not provide lookups with either through by_token" do
|
59
|
+
expect(described_class.by_token(plain_text_token)).to eq(nil)
|
60
|
+
expect(described_class.by_token(access_token.token)).to eq(nil)
|
53
61
|
|
54
|
-
|
55
|
-
|
56
|
-
access_token.
|
62
|
+
# And it does not touch the token
|
63
|
+
access_token.reload
|
64
|
+
expect(access_token.token).to eq(plain_text_token)
|
57
65
|
end
|
66
|
+
end
|
58
67
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
68
|
+
context "with fallback lookup" do
|
69
|
+
include_context "with token hashing and fallback lookup enabled"
|
70
|
+
|
71
|
+
it "upgrades a plain token when falling back to it" do
|
72
|
+
# Side-effect: This will automatically upgrade the token
|
73
|
+
expect(described_class).to receive(:upgrade_fallback_value).and_call_original
|
74
|
+
expect(described_class.by_token(plain_text_token))
|
75
|
+
.to have_attributes(
|
76
|
+
resource_owner_id: access_token.resource_owner_id,
|
77
|
+
application_id: access_token.application_id,
|
78
|
+
scopes: access_token.scopes,
|
79
|
+
)
|
80
|
+
|
81
|
+
# Will find subsequently by hashing the token
|
82
|
+
expect(described_class.by_token(plain_text_token))
|
83
|
+
.to have_attributes(
|
84
|
+
resource_owner_id: access_token.resource_owner_id,
|
85
|
+
application_id: access_token.application_id,
|
86
|
+
scopes: access_token.scopes,
|
87
|
+
)
|
88
|
+
|
89
|
+
# Not all the ORM support :id PK
|
90
|
+
if access_token.respond_to?(:id)
|
91
|
+
expect(described_class.by_token(plain_text_token).id).to eq(access_token.id)
|
67
92
|
end
|
68
|
-
end
|
69
93
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
expect(clazz).to receive(:upgrade_fallback_value).and_call_original
|
76
|
-
expect(clazz.by_token(plain_text_token))
|
77
|
-
.to have_attributes(
|
78
|
-
resource_owner_id: access_token.resource_owner_id,
|
79
|
-
application_id: access_token.application_id,
|
80
|
-
scopes: access_token.scopes,
|
81
|
-
)
|
82
|
-
|
83
|
-
# Will find subsequently by hashing the token
|
84
|
-
expect(clazz.by_token(plain_text_token))
|
85
|
-
.to have_attributes(
|
86
|
-
resource_owner_id: access_token.resource_owner_id,
|
87
|
-
application_id: access_token.application_id,
|
88
|
-
scopes: access_token.scopes,
|
89
|
-
)
|
90
|
-
|
91
|
-
# Not all the ORM support :id PK
|
92
|
-
if access_token.respond_to?(:id)
|
93
|
-
expect(clazz.by_token(plain_text_token).id).to eq(access_token.id)
|
94
|
-
end
|
95
|
-
|
96
|
-
# And it modifies the token value
|
97
|
-
access_token.reload
|
98
|
-
expect(access_token.token).not_to eq(plain_text_token)
|
99
|
-
expect(clazz.find_by(token: plain_text_token)).to eq(nil)
|
100
|
-
expect(clazz.find_by(token: access_token.token)).not_to be_nil
|
101
|
-
end
|
94
|
+
# And it modifies the token value
|
95
|
+
access_token.reload
|
96
|
+
expect(access_token.token).not_to eq(plain_text_token)
|
97
|
+
expect(described_class.find_by(token: plain_text_token)).to eq(nil)
|
98
|
+
expect(described_class.find_by(token: access_token.token)).not_to be_nil
|
102
99
|
end
|
103
100
|
end
|
104
101
|
end
|
102
|
+
end
|
105
103
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
116
|
-
|
117
|
-
Doorkeeper.configure do
|
118
|
-
orm DOORKEEPER_ORM
|
119
|
-
access_token_generator "Doorkeeper::CustomGeneratorArgs"
|
104
|
+
it "generates a token using a custom object" do
|
105
|
+
eigenclass = class << CustomGeneratorArgs; self; end
|
106
|
+
eigenclass.class_eval do
|
107
|
+
remove_method :generate
|
108
|
+
end
|
109
|
+
module CustomGeneratorArgs
|
110
|
+
def self.generate(opts = {})
|
111
|
+
id = opts[:resource_owner_id] || opts[:resource_owner]&.id
|
112
|
+
"custom_generator_token_#{id}"
|
120
113
|
end
|
114
|
+
end
|
121
115
|
|
122
|
-
|
123
|
-
|
116
|
+
Doorkeeper.configure do
|
117
|
+
orm DOORKEEPER_ORM
|
118
|
+
access_token_generator "CustomGeneratorArgs"
|
124
119
|
end
|
125
120
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
end
|
121
|
+
owner = FactoryBot.create :resource_owner
|
122
|
+
token = FactoryBot.create :access_token,
|
123
|
+
resource_owner_id: owner.id,
|
124
|
+
resource_owner_type: owner.class.name
|
131
125
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
126
|
+
expect(token.token).to match(/custom_generator_token_\d+/)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "allows the custom generator to access the application details" do
|
130
|
+
eigenclass = class << CustomGeneratorArgs; self; end
|
131
|
+
eigenclass.class_eval do
|
132
|
+
remove_method :generate
|
133
|
+
end
|
137
134
|
|
138
|
-
|
139
|
-
|
140
|
-
|
135
|
+
module CustomGeneratorArgs
|
136
|
+
def self.generate(opts = {})
|
137
|
+
"custom_generator_token_#{opts[:application].name}"
|
141
138
|
end
|
139
|
+
end
|
142
140
|
|
143
|
-
|
144
|
-
|
141
|
+
Doorkeeper.configure do
|
142
|
+
orm DOORKEEPER_ORM
|
143
|
+
access_token_generator "CustomGeneratorArgs"
|
145
144
|
end
|
146
145
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
remove_method :generate
|
151
|
-
end
|
152
|
-
module CustomGeneratorArgs
|
153
|
-
def self.generate(opts = {})
|
154
|
-
"custom_generator_token_#{opts[:scopes].count}_#{opts[:scopes]}"
|
155
|
-
end
|
156
|
-
end
|
146
|
+
token = FactoryBot.create :access_token
|
147
|
+
expect(token.token).to match(/custom_generator_token_Application \d+/)
|
148
|
+
end
|
157
149
|
|
158
|
-
|
159
|
-
|
160
|
-
|
150
|
+
it "allows the custom generator to access the scopes" do
|
151
|
+
eigenclass = class << CustomGeneratorArgs; self; end
|
152
|
+
eigenclass.class_eval do
|
153
|
+
remove_method :generate
|
154
|
+
end
|
155
|
+
module CustomGeneratorArgs
|
156
|
+
def self.generate(opts = {})
|
157
|
+
"custom_generator_token_#{opts[:scopes].count}_#{opts[:scopes]}"
|
161
158
|
end
|
159
|
+
end
|
162
160
|
|
163
|
-
|
164
|
-
|
165
|
-
|
161
|
+
Doorkeeper.configure do
|
162
|
+
orm DOORKEEPER_ORM
|
163
|
+
access_token_generator "CustomGeneratorArgs"
|
166
164
|
end
|
167
165
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
end
|
173
|
-
module CustomGeneratorArgs
|
174
|
-
def self.generate(opts = {})
|
175
|
-
"custom_generator_token_#{opts[:expires_in]}"
|
176
|
-
end
|
177
|
-
end
|
166
|
+
token = FactoryBot.create :access_token, scopes: "public write"
|
167
|
+
|
168
|
+
expect(token.token).to eq "custom_generator_token_2_public write"
|
169
|
+
end
|
178
170
|
|
179
|
-
|
180
|
-
|
181
|
-
|
171
|
+
it "allows the custom generator to access the expiry length" do
|
172
|
+
eigenclass = class << CustomGeneratorArgs; self; end
|
173
|
+
eigenclass.class_eval do
|
174
|
+
remove_method :generate
|
175
|
+
end
|
176
|
+
module CustomGeneratorArgs
|
177
|
+
def self.generate(opts = {})
|
178
|
+
"custom_generator_token_#{opts[:expires_in]}"
|
182
179
|
end
|
180
|
+
end
|
183
181
|
|
184
|
-
|
185
|
-
|
182
|
+
Doorkeeper.configure do
|
183
|
+
orm DOORKEEPER_ORM
|
184
|
+
access_token_generator "CustomGeneratorArgs"
|
186
185
|
end
|
187
186
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
"custom_generator_token_#{opts[:created_at].to_i}"
|
192
|
-
end
|
193
|
-
end
|
187
|
+
token = FactoryBot.create :access_token
|
188
|
+
expect(token.token).to eq "custom_generator_token_7200"
|
189
|
+
end
|
194
190
|
|
195
|
-
|
196
|
-
|
197
|
-
|
191
|
+
it "allows the custom generator to access the created time" do
|
192
|
+
module CustomGeneratorArgs
|
193
|
+
def self.generate(opts = {})
|
194
|
+
"custom_generator_token_#{opts[:created_at].to_i}"
|
198
195
|
end
|
196
|
+
end
|
199
197
|
|
200
|
-
|
201
|
-
|
202
|
-
|
198
|
+
Doorkeeper.configure do
|
199
|
+
orm DOORKEEPER_ORM
|
200
|
+
access_token_generator "CustomGeneratorArgs"
|
203
201
|
end
|
204
202
|
|
205
|
-
|
206
|
-
|
207
|
-
|
203
|
+
token = FactoryBot.create :access_token
|
204
|
+
created_at = token.created_at
|
205
|
+
expect(token.token).to eq "custom_generator_token_#{created_at.to_i}"
|
206
|
+
end
|
208
207
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
end
|
208
|
+
it "raises an error if the custom object does not support generate" do
|
209
|
+
module NoGenerate
|
210
|
+
end
|
213
211
|
|
214
|
-
|
215
|
-
|
216
|
-
|
212
|
+
Doorkeeper.configure do
|
213
|
+
orm DOORKEEPER_ORM
|
214
|
+
access_token_generator "NoGenerate"
|
217
215
|
end
|
218
216
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
end
|
217
|
+
expect { FactoryBot.create :access_token }.to(
|
218
|
+
raise_error(Doorkeeper::Errors::UnableToGenerateToken),
|
219
|
+
)
|
220
|
+
end
|
224
221
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
222
|
+
it "raises original error if something went wrong in custom generator" do
|
223
|
+
eigenclass = class << CustomGeneratorArgs; self; end
|
224
|
+
eigenclass.class_eval do
|
225
|
+
remove_method :generate
|
226
|
+
end
|
230
227
|
|
231
|
-
|
232
|
-
|
233
|
-
|
228
|
+
module CustomGeneratorArgs
|
229
|
+
def self.generate(_opts = {})
|
230
|
+
raise LoadError, "custom behaviour"
|
234
231
|
end
|
232
|
+
end
|
235
233
|
|
236
|
-
|
237
|
-
|
238
|
-
|
234
|
+
Doorkeeper.configure do
|
235
|
+
orm DOORKEEPER_ORM
|
236
|
+
access_token_generator "CustomGeneratorArgs"
|
239
237
|
end
|
240
238
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
end
|
239
|
+
expect { FactoryBot.create :access_token }.to(
|
240
|
+
raise_error(LoadError),
|
241
|
+
)
|
242
|
+
end
|
246
243
|
|
247
|
-
|
248
|
-
|
249
|
-
|
244
|
+
it "raises an error if the custom object does not exist" do
|
245
|
+
Doorkeeper.configure do
|
246
|
+
orm DOORKEEPER_ORM
|
247
|
+
access_token_generator "Doorkeeper::NotReal"
|
250
248
|
end
|
249
|
+
|
250
|
+
expect { FactoryBot.create :access_token }.to(
|
251
|
+
raise_error(Doorkeeper::Errors::TokenGeneratorNotFound, /NotReal/),
|
252
|
+
)
|
251
253
|
end
|
254
|
+
end
|
252
255
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
256
|
+
describe "refresh_token" do
|
257
|
+
it "has empty refresh token if it was not required" do
|
258
|
+
token = FactoryBot.create :access_token
|
259
|
+
expect(token.refresh_token).to be_nil
|
260
|
+
end
|
258
261
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
262
|
+
it "generates a refresh token if it was requested" do
|
263
|
+
token = FactoryBot.create :access_token, use_refresh_token: true
|
264
|
+
expect(token.refresh_token).not_to be_nil
|
265
|
+
end
|
266
|
+
|
267
|
+
it "is not valid if token exists" do
|
268
|
+
token1 = FactoryBot.create :access_token, use_refresh_token: true
|
269
|
+
token2 = FactoryBot.create :access_token, use_refresh_token: true
|
270
|
+
token2.refresh_token = token1.refresh_token
|
271
|
+
expect(token2).not_to be_valid
|
272
|
+
end
|
263
273
|
|
264
|
-
|
265
|
-
|
266
|
-
|
274
|
+
it "expects database to raise an error if refresh tokens are the same" do
|
275
|
+
token1 = FactoryBot.create :access_token, use_refresh_token: true
|
276
|
+
token2 = FactoryBot.create :access_token, use_refresh_token: true
|
277
|
+
expect do
|
267
278
|
token2.refresh_token = token1.refresh_token
|
268
|
-
|
269
|
-
end
|
279
|
+
token2.save(validate: false)
|
280
|
+
end.to raise_error(uniqueness_error)
|
281
|
+
end
|
270
282
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
expect do
|
275
|
-
token2.refresh_token = token1.refresh_token
|
276
|
-
token2.save(validate: false)
|
277
|
-
end.to raise_error(uniqueness_error)
|
278
|
-
end
|
283
|
+
context "with hashing enabled" do
|
284
|
+
include_context "with token hashing enabled"
|
285
|
+
let(:token) { FactoryBot.create :access_token, use_refresh_token: true }
|
279
286
|
|
280
|
-
|
281
|
-
|
282
|
-
|
287
|
+
it "holds a volatile refresh token when created" do
|
288
|
+
expect(token.plaintext_refresh_token).to be_a(String)
|
289
|
+
expect(token.refresh_token)
|
290
|
+
.to eq(hashed_or_plain_token_func.call(token.plaintext_refresh_token))
|
283
291
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
292
|
+
# Finder method only finds the hashed token
|
293
|
+
loaded = described_class.find_by(refresh_token: token.refresh_token)
|
294
|
+
expect(loaded).to eq(token)
|
295
|
+
expect(loaded.plaintext_refresh_token).to be_nil
|
296
|
+
expect(loaded.refresh_token).to eq(token.refresh_token)
|
297
|
+
end
|
288
298
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
299
|
+
it "does not find_by plain text refresh tokens" do
|
300
|
+
expect(described_class.find_by(refresh_token: token.plaintext_refresh_token)).to be_nil
|
301
|
+
end
|
302
|
+
|
303
|
+
describe "with having a plain text token" do
|
304
|
+
let(:plain_refresh_token) { "plain refresh token" }
|
305
|
+
let(:access_token) { FactoryBot.create :access_token }
|
295
306
|
|
296
|
-
|
297
|
-
|
307
|
+
before do
|
308
|
+
# Assume we have a plain text token from before activating the option
|
309
|
+
access_token.update_column(:refresh_token, plain_refresh_token)
|
298
310
|
end
|
299
311
|
|
300
|
-
|
301
|
-
|
302
|
-
|
312
|
+
context "without fallback lookup" do
|
313
|
+
it "does not provide lookups with either through by_token" do
|
314
|
+
expect(described_class.by_refresh_token(plain_refresh_token)).to eq(nil)
|
315
|
+
expect(described_class.by_refresh_token(access_token.refresh_token)).to eq(nil)
|
303
316
|
|
304
|
-
|
305
|
-
|
306
|
-
access_token.
|
317
|
+
# And it does not touch the token
|
318
|
+
access_token.reload
|
319
|
+
expect(access_token.refresh_token).to eq(plain_refresh_token)
|
307
320
|
end
|
321
|
+
end
|
308
322
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
323
|
+
context "with fallback lookup" do
|
324
|
+
include_context "with token hashing and fallback lookup enabled"
|
325
|
+
|
326
|
+
it "upgrades a plain token when falling back to it" do
|
327
|
+
# Side-effect: This will automatically upgrade the token
|
328
|
+
expect(described_class).to receive(:upgrade_fallback_value).and_call_original
|
329
|
+
expect(described_class.by_refresh_token(plain_refresh_token))
|
330
|
+
.to have_attributes(
|
331
|
+
token: access_token.token,
|
332
|
+
resource_owner_id: access_token.resource_owner_id,
|
333
|
+
application_id: access_token.application_id,
|
334
|
+
)
|
335
|
+
|
336
|
+
# Will find subsequently by hashing the token
|
337
|
+
expect(described_class.by_refresh_token(plain_refresh_token))
|
338
|
+
.to have_attributes(
|
339
|
+
token: access_token.token,
|
340
|
+
resource_owner_id: access_token.resource_owner_id,
|
341
|
+
application_id: access_token.application_id,
|
342
|
+
)
|
343
|
+
|
344
|
+
# Not all the ORM support :id PK
|
345
|
+
if access_token.respond_to?(:id)
|
346
|
+
expect(described_class.by_refresh_token(plain_refresh_token).id).to eq(access_token.id)
|
317
347
|
end
|
318
|
-
end
|
319
348
|
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
expect(clazz).to receive(:upgrade_fallback_value).and_call_original
|
326
|
-
expect(clazz.by_refresh_token(plain_refresh_token))
|
327
|
-
.to have_attributes(
|
328
|
-
token: access_token.token,
|
329
|
-
resource_owner_id: access_token.resource_owner_id,
|
330
|
-
application_id: access_token.application_id,
|
331
|
-
)
|
332
|
-
|
333
|
-
# Will find subsequently by hashing the token
|
334
|
-
expect(clazz.by_refresh_token(plain_refresh_token))
|
335
|
-
.to have_attributes(
|
336
|
-
token: access_token.token,
|
337
|
-
resource_owner_id: access_token.resource_owner_id,
|
338
|
-
application_id: access_token.application_id,
|
339
|
-
)
|
340
|
-
|
341
|
-
# Not all the ORM support :id PK
|
342
|
-
if access_token.respond_to?(:id)
|
343
|
-
expect(clazz.by_refresh_token(plain_refresh_token).id).to eq(access_token.id)
|
344
|
-
end
|
345
|
-
|
346
|
-
# And it modifies the token value
|
347
|
-
access_token.reload
|
348
|
-
expect(access_token.refresh_token).not_to eq(plain_refresh_token)
|
349
|
-
expect(clazz.find_by(refresh_token: plain_refresh_token)).to eq(nil)
|
350
|
-
expect(clazz.find_by(refresh_token: access_token.refresh_token)).not_to be_nil
|
351
|
-
end
|
349
|
+
# And it modifies the token value
|
350
|
+
access_token.reload
|
351
|
+
expect(access_token.refresh_token).not_to eq(plain_refresh_token)
|
352
|
+
expect(described_class.find_by(refresh_token: plain_refresh_token)).to eq(nil)
|
353
|
+
expect(described_class.find_by(refresh_token: access_token.refresh_token)).not_to be_nil
|
352
354
|
end
|
353
355
|
end
|
354
356
|
end
|
355
357
|
end
|
358
|
+
end
|
356
359
|
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
360
|
+
describe "validations" do
|
361
|
+
it "is valid without resource_owner_id" do
|
362
|
+
# For client credentials flow
|
363
|
+
subject.resource_owner_id = nil
|
364
|
+
expect(subject).to be_valid
|
365
|
+
end
|
363
366
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
end
|
367
|
+
it "is valid without application_id" do
|
368
|
+
# For resource owner credentials flow
|
369
|
+
subject.application_id = nil
|
370
|
+
expect(subject).to be_valid
|
369
371
|
end
|
372
|
+
end
|
370
373
|
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
374
|
+
describe "#same_credential?" do
|
375
|
+
context "with default parameters" do
|
376
|
+
let(:resource_owner) { FactoryBot.create(:resource_owner) }
|
377
|
+
let(:resource_owner_id) { resource_owner.id }
|
378
|
+
let(:application) { FactoryBot.create :application }
|
379
|
+
let(:default_attributes) do
|
380
|
+
{
|
381
|
+
application: application,
|
382
|
+
resource_owner_id: resource_owner_id,
|
383
|
+
resource_owner_type: resource_owner.class.name,
|
384
|
+
}
|
385
|
+
end
|
386
|
+
let(:access_token1) { FactoryBot.create :access_token, default_attributes }
|
379
387
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
end
|
388
|
+
context "the second token has the same owner and same app" do
|
389
|
+
let(:access_token2) { FactoryBot.create :access_token, default_attributes }
|
390
|
+
it "success" do
|
391
|
+
expect(access_token1.same_credential?(access_token2)).to be_truthy
|
385
392
|
end
|
393
|
+
end
|
386
394
|
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
395
|
+
context "the second token has same owner and different app" do
|
396
|
+
let(:other_application) { FactoryBot.create :application }
|
397
|
+
let(:access_token2) do
|
398
|
+
FactoryBot.create :access_token,
|
399
|
+
application: other_application,
|
400
|
+
resource_owner_id: resource_owner_id,
|
401
|
+
resource_owner_type: resource_owner.class.name
|
402
|
+
end
|
394
403
|
|
395
|
-
|
396
|
-
|
397
|
-
end
|
404
|
+
it "fail" do
|
405
|
+
expect(access_token1.same_credential?(access_token2)).to be_falsey
|
398
406
|
end
|
407
|
+
end
|
399
408
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
409
|
+
context "the second token has different owner and different app" do
|
410
|
+
let(:other_application) { FactoryBot.create :application }
|
411
|
+
let(:access_token2) do
|
412
|
+
FactoryBot.create :access_token,
|
413
|
+
application: other_application,
|
414
|
+
resource_owner_id: resource_owner.id + 1
|
415
|
+
end
|
405
416
|
|
406
|
-
|
407
|
-
|
408
|
-
end
|
417
|
+
it "fail" do
|
418
|
+
expect(access_token1.same_credential?(access_token2)).to be_falsey
|
409
419
|
end
|
420
|
+
end
|
410
421
|
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
422
|
+
context "the second token has different owner and same app" do
|
423
|
+
let(:access_token2) do
|
424
|
+
FactoryBot.create :access_token,
|
425
|
+
application: application,
|
426
|
+
resource_owner_id: resource_owner.id + 1
|
427
|
+
end
|
415
428
|
|
416
|
-
|
417
|
-
|
418
|
-
end
|
429
|
+
it "fail" do
|
430
|
+
expect(access_token1.same_credential?(access_token2)).to be_falsey
|
419
431
|
end
|
420
432
|
end
|
421
433
|
end
|
434
|
+
end
|
422
435
|
|
423
|
-
|
424
|
-
|
425
|
-
|
436
|
+
describe "#acceptable?" do
|
437
|
+
context "a token that is not accessible" do
|
438
|
+
let(:token) { FactoryBot.create(:access_token, created_at: 6.hours.ago) }
|
426
439
|
|
427
|
-
|
428
|
-
|
429
|
-
end
|
440
|
+
it "should return false" do
|
441
|
+
expect(token.acceptable?(nil)).to be false
|
430
442
|
end
|
443
|
+
end
|
431
444
|
|
432
|
-
|
433
|
-
|
445
|
+
context "a token that has the incorrect scopes" do
|
446
|
+
let(:token) { FactoryBot.create(:access_token) }
|
434
447
|
|
435
|
-
|
436
|
-
|
437
|
-
end
|
448
|
+
it "should return false" do
|
449
|
+
expect(token.acceptable?(["public"])).to be false
|
438
450
|
end
|
451
|
+
end
|
439
452
|
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
453
|
+
context "a token is acceptable with the correct scopes" do
|
454
|
+
let(:token) do
|
455
|
+
token = FactoryBot.create(:access_token)
|
456
|
+
token[:scopes] = "public"
|
457
|
+
token
|
458
|
+
end
|
446
459
|
|
447
|
-
|
448
|
-
|
449
|
-
end
|
460
|
+
it "should return true" do
|
461
|
+
expect(token.acceptable?(["public"])).to be true
|
450
462
|
end
|
451
463
|
end
|
464
|
+
end
|
452
465
|
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
466
|
+
describe ".revoke_all_for" do
|
467
|
+
let(:resource_owner) { FactoryBot.create :resource_owner }
|
468
|
+
let(:application) { FactoryBot.create :application }
|
469
|
+
let(:default_attributes) do
|
470
|
+
{
|
471
|
+
application: application,
|
472
|
+
resource_owner_id: resource_owner.id,
|
473
|
+
resource_owner_type: resource_owner.class.name,
|
474
|
+
}
|
475
|
+
end
|
459
476
|
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
end
|
477
|
+
it "revokes all tokens for given application and resource owner" do
|
478
|
+
FactoryBot.create :access_token, default_attributes
|
479
|
+
described_class.revoke_all_for application.id, resource_owner
|
480
|
+
described_class.all.each do |token|
|
481
|
+
expect(token).to be_revoked
|
466
482
|
end
|
483
|
+
end
|
467
484
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
485
|
+
it "matches application" do
|
486
|
+
access_token_for_different_app = FactoryBot.create(
|
487
|
+
:access_token,
|
488
|
+
default_attributes.merge(application: FactoryBot.create(:application)),
|
489
|
+
)
|
473
490
|
|
474
|
-
|
491
|
+
described_class.revoke_all_for application.id, resource_owner
|
475
492
|
|
476
|
-
|
477
|
-
|
493
|
+
expect(access_token_for_different_app.reload).not_to be_revoked
|
494
|
+
end
|
478
495
|
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
496
|
+
it "matches resource owner" do
|
497
|
+
access_token_for_different_owner = FactoryBot.create(
|
498
|
+
:access_token,
|
499
|
+
default_attributes.merge(resource_owner_id: resource_owner.id + 1),
|
500
|
+
)
|
484
501
|
|
485
|
-
|
502
|
+
described_class.revoke_all_for application.id, resource_owner
|
486
503
|
|
487
|
-
|
488
|
-
end
|
504
|
+
expect(access_token_for_different_owner.reload).not_to be_revoked
|
489
505
|
end
|
506
|
+
end
|
490
507
|
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
508
|
+
describe ".matching_token_for" do
|
509
|
+
let(:resource_owner) { FactoryBot.create :resource_owner }
|
510
|
+
let(:resource_owner_id) { resource_owner.id }
|
511
|
+
let(:application) { FactoryBot.create :application }
|
512
|
+
let(:scopes) { Doorkeeper::OAuth::Scopes.from_string("public write") }
|
513
|
+
let(:default_attributes) do
|
514
|
+
{
|
515
|
+
application: application,
|
516
|
+
resource_owner_id: resource_owner_id,
|
517
|
+
resource_owner_type: resource_owner.class.name,
|
518
|
+
scopes: scopes.to_s,
|
519
|
+
}
|
520
|
+
end
|
502
521
|
|
503
|
-
|
504
|
-
|
505
|
-
|
522
|
+
before do
|
523
|
+
default_scopes_exist(*scopes.all)
|
524
|
+
end
|
506
525
|
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
526
|
+
it "returns only one token" do
|
527
|
+
token = FactoryBot.create :access_token, default_attributes
|
528
|
+
last_token = described_class.matching_token_for(application, resource_owner, scopes)
|
529
|
+
expect(last_token).to eq(token)
|
530
|
+
end
|
512
531
|
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
end
|
532
|
+
it "accepts resource owner as object" do
|
533
|
+
token = FactoryBot.create :access_token, default_attributes
|
534
|
+
last_token = described_class.matching_token_for(application, resource_owner, scopes)
|
535
|
+
expect(last_token).to eq(token)
|
536
|
+
end
|
519
537
|
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
538
|
+
it "accepts nil as resource owner" do
|
539
|
+
token = FactoryBot.create :access_token,
|
540
|
+
default_attributes.merge(resource_owner_id: nil, resource_owner_type: nil)
|
541
|
+
last_token = described_class.matching_token_for(application, nil, scopes)
|
542
|
+
expect(last_token).to eq(token)
|
543
|
+
end
|
525
544
|
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
545
|
+
it "excludes revoked tokens" do
|
546
|
+
FactoryBot.create :access_token, default_attributes.merge(revoked_at: 1.day.ago)
|
547
|
+
last_token = described_class.matching_token_for(application, resource_owner_id, scopes)
|
548
|
+
expect(last_token).to be_nil
|
549
|
+
end
|
531
550
|
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
551
|
+
it "excludes tokens with a different application" do
|
552
|
+
FactoryBot.create :access_token, default_attributes.merge(application: FactoryBot.create(:application))
|
553
|
+
last_token = described_class.matching_token_for(application, resource_owner_id, scopes)
|
554
|
+
expect(last_token).to be_nil
|
555
|
+
end
|
537
556
|
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
557
|
+
it "excludes tokens with a different resource owner" do
|
558
|
+
FactoryBot.create :access_token, default_attributes.merge(resource_owner_id: resource_owner.id + 1)
|
559
|
+
last_token = described_class.matching_token_for(application, resource_owner_id, scopes)
|
560
|
+
expect(last_token).to be_nil
|
561
|
+
end
|
543
562
|
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
563
|
+
it "excludes tokens with fewer scopes" do
|
564
|
+
FactoryBot.create :access_token, default_attributes.merge(scopes: "public")
|
565
|
+
last_token = described_class.matching_token_for(application, resource_owner_id, scopes)
|
566
|
+
expect(last_token).to be_nil
|
567
|
+
end
|
549
568
|
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
569
|
+
it "excludes tokens with different scopes" do
|
570
|
+
FactoryBot.create :access_token, default_attributes.merge(scopes: "public email")
|
571
|
+
last_token = described_class.matching_token_for(application, resource_owner, scopes)
|
572
|
+
expect(last_token).to be_nil
|
573
|
+
end
|
555
574
|
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
575
|
+
it "excludes tokens with additional scopes" do
|
576
|
+
FactoryBot.create :access_token, default_attributes.merge(scopes: "public write email")
|
577
|
+
last_token = described_class.matching_token_for(application, resource_owner, scopes)
|
578
|
+
expect(last_token).to be_nil
|
579
|
+
end
|
561
580
|
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
581
|
+
it "excludes tokens with scopes that are not present in server scopes" do
|
582
|
+
FactoryBot.create :access_token, default_attributes.merge(
|
583
|
+
application: application, scopes: "public read",
|
584
|
+
)
|
585
|
+
last_token = described_class.matching_token_for(application, resource_owner, scopes)
|
586
|
+
expect(last_token).to be_nil
|
587
|
+
end
|
569
588
|
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
589
|
+
it "excludes tokens with scopes that are not present in application scopes" do
|
590
|
+
application = FactoryBot.create :application, scopes: "private read"
|
591
|
+
FactoryBot.create :access_token, default_attributes.merge(
|
592
|
+
application: application,
|
593
|
+
)
|
594
|
+
last_token = described_class.matching_token_for(application, resource_owner, scopes)
|
595
|
+
expect(last_token).to be_nil
|
596
|
+
end
|
578
597
|
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
598
|
+
it "does not match token if empty scope requested and token/app scopes present" do
|
599
|
+
application = FactoryBot.create :application, scopes: "sample:scope"
|
600
|
+
app_params = {
|
601
|
+
application_id: application.id, scopes: "sample:scope",
|
602
|
+
resource_owner_id: resource_owner.id,
|
603
|
+
resource_owner_type: resource_owner.class.name,
|
604
|
+
}
|
605
|
+
FactoryBot.create :access_token, app_params
|
606
|
+
empty_scopes = Doorkeeper::OAuth::Scopes.from_string("")
|
607
|
+
last_token = described_class.matching_token_for(application, resource_owner.id, empty_scopes)
|
608
|
+
expect(last_token).to be_nil
|
609
|
+
end
|
590
610
|
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
611
|
+
it "matches token if empty scope requested and no token scopes present" do
|
612
|
+
empty_scopes = Doorkeeper::OAuth::Scopes.from_string("")
|
613
|
+
token = FactoryBot.create :access_token, default_attributes.merge(scopes: empty_scopes)
|
614
|
+
last_token = described_class.matching_token_for(application, resource_owner.id, empty_scopes)
|
615
|
+
expect(last_token).to eq(token)
|
616
|
+
end
|
597
617
|
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
618
|
+
it "returns the last matching token" do
|
619
|
+
FactoryBot.create :access_token, default_attributes.merge(created_at: 1.day.ago)
|
620
|
+
matching_token = FactoryBot.create :access_token, default_attributes
|
621
|
+
FactoryBot.create :access_token, default_attributes.merge(scopes: "public")
|
602
622
|
|
603
|
-
|
604
|
-
|
605
|
-
|
623
|
+
last_token = described_class.matching_token_for(application, resource_owner_id, scopes)
|
624
|
+
expect(last_token).to eq(matching_token)
|
625
|
+
end
|
626
|
+
end
|
627
|
+
|
628
|
+
describe "#as_json" do
|
629
|
+
let(:token) { FactoryBot.create(:access_token) }
|
630
|
+
let(:token_hash) do
|
631
|
+
{
|
632
|
+
resource_owner_id: token.resource_owner_id,
|
633
|
+
scope: token.scopes,
|
634
|
+
expires_in: token.expires_in_seconds,
|
635
|
+
application: { uid: token.application.uid },
|
636
|
+
created_at: token.created_at.to_i,
|
637
|
+
}
|
606
638
|
end
|
607
639
|
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
scope: token.scopes,
|
614
|
-
expires_in: token.expires_in_seconds,
|
615
|
-
application: { uid: token.application.uid },
|
616
|
-
created_at: token.created_at.to_i,
|
617
|
-
}
|
618
|
-
expect(token.as_json).to eq token_hash
|
640
|
+
it "returns as_json hash" do
|
641
|
+
hash = token_hash
|
642
|
+
|
643
|
+
if Doorkeeper.configuration.polymorphic_resource_owner?
|
644
|
+
hash[:resource_owner_type] = token.resource_owner_type
|
619
645
|
end
|
646
|
+
|
647
|
+
expect(token.as_json).to match(hash)
|
620
648
|
end
|
621
649
|
end
|
622
650
|
end
|