doorkeeper 4.2.5 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE.md +25 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +17 -0
- data/.gitignore +2 -1
- data/.gitlab-ci.yml +16 -0
- data/.hound.yml +2 -13
- data/.rubocop.yml +17 -0
- data/.travis.yml +19 -4
- data/Appraisals +8 -4
- data/CODE_OF_CONDUCT.md +46 -0
- data/Gemfile +1 -1
- data/NEWS.md +115 -0
- data/README.md +175 -39
- data/RELEASING.md +5 -12
- data/Rakefile +6 -0
- data/SECURITY.md +15 -0
- data/UPGRADE.md +2 -0
- data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
- data/app/controllers/doorkeeper/application_controller.rb +2 -5
- data/app/controllers/doorkeeper/application_metal_controller.rb +4 -0
- data/app/controllers/doorkeeper/applications_controller.rb +47 -13
- data/app/controllers/doorkeeper/authorizations_controller.rb +55 -12
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +19 -2
- data/app/controllers/doorkeeper/tokens_controller.rb +24 -17
- data/app/helpers/doorkeeper/dashboard_helper.rb +8 -6
- data/app/validators/redirect_uri_validator.rb +13 -2
- data/app/views/doorkeeper/applications/_delete_form.html.erb +3 -1
- data/app/views/doorkeeper/applications/_form.html.erb +31 -19
- data/app/views/doorkeeper/applications/edit.html.erb +1 -1
- data/app/views/doorkeeper/applications/index.html.erb +18 -6
- data/app/views/doorkeeper/applications/new.html.erb +1 -1
- data/app/views/doorkeeper/applications/show.html.erb +8 -5
- data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
- data/app/views/doorkeeper/authorizations/new.html.erb +5 -1
- data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
- data/app/views/layouts/doorkeeper/admin.html.erb +15 -15
- data/config/locales/en.yml +19 -6
- data/doorkeeper.gemspec +20 -20
- data/gemfiles/rails_4_2.gemfile +6 -4
- data/gemfiles/rails_5_0.gemfile +4 -4
- data/gemfiles/rails_5_1.gemfile +6 -7
- data/gemfiles/rails_5_2.gemfile +12 -0
- data/gemfiles/rails_master.gemfile +17 -0
- data/lib/doorkeeper/config.rb +123 -71
- data/lib/doorkeeper/engine.rb +7 -3
- data/lib/doorkeeper/errors.rb +20 -5
- data/lib/doorkeeper/grape/helpers.rb +14 -9
- data/lib/doorkeeper/helpers/controller.rb +16 -22
- data/lib/doorkeeper/models/access_grant_mixin.rb +67 -23
- data/lib/doorkeeper/models/access_token_mixin.rb +54 -55
- data/lib/doorkeeper/models/application_mixin.rb +16 -30
- data/lib/doorkeeper/models/concerns/expirable.rb +7 -5
- data/lib/doorkeeper/models/concerns/orderable.rb +13 -0
- data/lib/doorkeeper/models/concerns/scopes.rb +1 -1
- data/lib/doorkeeper/oauth/authorization/code.rb +31 -8
- data/lib/doorkeeper/oauth/authorization/context.rb +15 -0
- data/lib/doorkeeper/oauth/authorization/token.rb +46 -20
- data/lib/doorkeeper/oauth/authorization_code_request.rb +33 -3
- data/lib/doorkeeper/oauth/base_request.rb +23 -7
- data/lib/doorkeeper/oauth/client/credentials.rb +6 -4
- data/lib/doorkeeper/oauth/client.rb +2 -4
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +2 -1
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +6 -3
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +4 -6
- data/lib/doorkeeper/oauth/client_credentials_request.rb +0 -4
- data/lib/doorkeeper/oauth/error.rb +2 -2
- data/lib/doorkeeper/oauth/error_response.rb +11 -4
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +0 -8
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +15 -0
- data/lib/doorkeeper/oauth/invalid_token_response.rb +3 -4
- data/lib/doorkeeper/oauth/password_access_token_request.rb +8 -4
- data/lib/doorkeeper/oauth/pre_authorization.rb +45 -13
- data/lib/doorkeeper/oauth/refresh_token_request.rb +7 -1
- data/lib/doorkeeper/oauth/scopes.rb +19 -9
- data/lib/doorkeeper/oauth/token.rb +6 -3
- data/lib/doorkeeper/oauth/token_introspection.rb +128 -0
- data/lib/doorkeeper/oauth/token_response.rb +4 -2
- data/lib/doorkeeper/oauth.rb +13 -0
- data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -0
- data/lib/doorkeeper/orm/active_record/access_token.rb +21 -20
- data/lib/doorkeeper/orm/active_record/application.rb +45 -0
- data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +26 -0
- data/lib/doorkeeper/orm/active_record.rb +21 -8
- data/lib/doorkeeper/rails/helpers.rb +7 -10
- data/lib/doorkeeper/rails/routes.rb +21 -7
- data/lib/doorkeeper/rake/db.rake +40 -0
- data/lib/doorkeeper/rake/setup.rake +6 -0
- data/lib/doorkeeper/rake.rb +14 -0
- data/lib/doorkeeper/request/authorization_code.rb +0 -2
- data/lib/doorkeeper/request/client_credentials.rb +0 -2
- data/lib/doorkeeper/request/code.rb +0 -2
- data/lib/doorkeeper/request/password.rb +1 -13
- data/lib/doorkeeper/request/refresh_token.rb +0 -2
- data/lib/doorkeeper/request/token.rb +0 -2
- data/lib/doorkeeper/request.rb +29 -30
- data/lib/doorkeeper/validations.rb +3 -2
- data/lib/doorkeeper/version.rb +14 -1
- data/lib/doorkeeper.rb +21 -17
- data/lib/generators/doorkeeper/application_owner_generator.rb +26 -12
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +32 -0
- data/lib/generators/doorkeeper/install_generator.rb +17 -9
- data/lib/generators/doorkeeper/migration_generator.rb +26 -9
- data/lib/generators/doorkeeper/pkce_generator.rb +32 -0
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +31 -20
- data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
- data/lib/generators/doorkeeper/templates/{add_owner_to_application_migration.rb → add_owner_to_application_migration.rb.erb} +1 -1
- data/lib/generators/doorkeeper/templates/{add_previous_refresh_token_to_access_tokens.rb → add_previous_refresh_token_to_access_tokens.rb.erb} +1 -1
- data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +6 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +104 -12
- data/lib/generators/doorkeeper/templates/{migration.rb → migration.rb.erb} +2 -1
- data/lib/generators/doorkeeper/views_generator.rb +3 -1
- data/spec/controllers/application_metal_controller_spec.rb +50 -0
- data/spec/controllers/applications_controller_spec.rb +141 -17
- data/spec/controllers/authorizations_controller_spec.rb +304 -27
- data/spec/controllers/protected_resources_controller_spec.rb +44 -35
- data/spec/controllers/token_info_controller_spec.rb +17 -21
- data/spec/controllers/tokens_controller_spec.rb +201 -17
- data/spec/dummy/app/assets/config/manifest.js +2 -0
- data/spec/dummy/config/environments/test.rb +4 -5
- data/spec/dummy/config/initializers/doorkeeper.rb +23 -2
- data/spec/dummy/config/initializers/{active_record_belongs_to_required_by_default.rb → new_framework_defaults.rb} +5 -1
- data/spec/dummy/config/initializers/secret_token.rb +0 -1
- data/spec/dummy/config/routes.rb +3 -42
- data/spec/dummy/db/migrate/20111122132257_create_users.rb +3 -1
- data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +3 -1
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -1
- data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +3 -1
- data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +3 -1
- data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +6 -0
- data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +13 -0
- data/spec/dummy/db/schema.rb +38 -37
- data/spec/factories.rb +1 -1
- data/spec/generators/application_owner_generator_spec.rb +25 -6
- data/spec/generators/confidential_applications_generator_spec.rb +45 -0
- data/spec/generators/install_generator_spec.rb +1 -1
- data/spec/generators/migration_generator_spec.rb +25 -4
- data/spec/generators/pkce_generator_spec.rb +43 -0
- data/spec/generators/previous_refresh_token_generator_spec.rb +57 -0
- data/spec/generators/views_generator_spec.rb +1 -1
- data/spec/grape/grape_integration_spec.rb +135 -0
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +2 -2
- data/spec/lib/config_spec.rb +199 -22
- data/spec/lib/doorkeeper_spec.rb +1 -126
- data/spec/lib/models/expirable_spec.rb +0 -4
- data/spec/lib/models/revocable_spec.rb +2 -4
- data/spec/lib/models/scopes_spec.rb +0 -4
- data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -4
- data/spec/lib/oauth/authorization_code_request_spec.rb +63 -13
- data/spec/lib/oauth/base_request_spec.rb +43 -10
- data/spec/lib/oauth/base_response_spec.rb +1 -1
- data/spec/lib/oauth/client/credentials_spec.rb +5 -5
- data/spec/lib/oauth/client_credentials/creator_spec.rb +6 -2
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +26 -7
- data/spec/lib/oauth/client_credentials/validation_spec.rb +2 -3
- data/spec/lib/oauth/client_credentials_integration_spec.rb +2 -2
- data/spec/lib/oauth/client_credentials_request_spec.rb +4 -5
- data/spec/lib/oauth/client_spec.rb +0 -3
- data/spec/lib/oauth/code_request_spec.rb +5 -5
- data/spec/lib/oauth/error_response_spec.rb +0 -3
- data/spec/lib/oauth/error_spec.rb +1 -3
- data/spec/lib/oauth/forbidden_token_response_spec.rb +1 -4
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -3
- data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -1
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +115 -3
- data/spec/lib/oauth/invalid_token_response_spec.rb +2 -5
- data/spec/lib/oauth/password_access_token_request_spec.rb +46 -5
- data/spec/lib/oauth/pre_authorization_spec.rb +40 -6
- data/spec/lib/oauth/refresh_token_request_spec.rb +30 -14
- data/spec/lib/oauth/scopes_spec.rb +28 -4
- data/spec/lib/oauth/token_request_spec.rb +10 -13
- data/spec/lib/oauth/token_response_spec.rb +0 -1
- data/spec/lib/oauth/token_spec.rb +37 -14
- data/spec/lib/orm/active_record/stale_records_cleaner_spec.rb +79 -0
- data/spec/lib/request/strategy_spec.rb +0 -1
- data/spec/lib/server_spec.rb +10 -0
- data/spec/models/doorkeeper/access_grant_spec.rb +45 -2
- data/spec/models/doorkeeper/access_token_spec.rb +132 -64
- data/spec/models/doorkeeper/application_spec.rb +114 -23
- data/spec/requests/applications/applications_request_spec.rb +139 -6
- data/spec/requests/applications/authorized_applications_spec.rb +1 -1
- data/spec/requests/endpoints/authorization_spec.rb +1 -1
- data/spec/requests/endpoints/token_spec.rb +15 -6
- data/spec/requests/flows/authorization_code_errors_spec.rb +12 -2
- data/spec/requests/flows/authorization_code_spec.rb +199 -2
- data/spec/requests/flows/client_credentials_spec.rb +73 -5
- data/spec/requests/flows/implicit_grant_errors_spec.rb +3 -3
- data/spec/requests/flows/implicit_grant_spec.rb +38 -11
- data/spec/requests/flows/password_spec.rb +160 -24
- data/spec/requests/flows/refresh_token_spec.rb +6 -6
- data/spec/requests/flows/revoke_token_spec.rb +26 -26
- data/spec/requests/flows/skip_authorization_spec.rb +16 -11
- data/spec/requests/protected_resources/metal_spec.rb +2 -2
- data/spec/requests/protected_resources/private_api_spec.rb +2 -2
- data/spec/routing/custom_controller_routes_spec.rb +63 -7
- data/spec/routing/default_routes_spec.rb +6 -2
- data/spec/routing/scoped_routes_spec.rb +16 -2
- data/spec/spec_helper.rb +54 -3
- data/spec/spec_helper_integration.rb +2 -64
- data/spec/support/dependencies/factory_bot.rb +2 -0
- data/spec/support/doorkeeper_rspec.rb +19 -0
- data/spec/support/helpers/access_token_request_helper.rb +1 -1
- data/spec/support/helpers/authorization_request_helper.rb +4 -4
- data/spec/support/helpers/model_helper.rb +9 -4
- data/spec/support/helpers/request_spec_helper.rb +18 -6
- data/spec/support/helpers/url_helper.rb +15 -10
- data/spec/support/http_method_shim.rb +12 -16
- data/spec/support/shared/controllers_shared_context.rb +2 -6
- data/spec/support/shared/models_shared_examples.rb +4 -4
- data/spec/validators/redirect_uri_validator_spec.rb +58 -7
- data/spec/version/version_spec.rb +15 -0
- data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
- metadata +75 -33
- data/spec/controllers/application_metal_controller.rb +0 -10
- data/spec/support/dependencies/factory_girl.rb +0 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
module Doorkeeper::OAuth
|
|
4
4
|
describe PasswordAccessTokenRequest do
|
|
@@ -8,10 +8,12 @@ module Doorkeeper::OAuth
|
|
|
8
8
|
default_scopes: Doorkeeper::OAuth::Scopes.new,
|
|
9
9
|
access_token_expires_in: 2.hours,
|
|
10
10
|
refresh_token_enabled?: false,
|
|
11
|
-
custom_access_token_expires_in:
|
|
11
|
+
custom_access_token_expires_in: lambda { |context|
|
|
12
|
+
context.grant_type == Doorkeeper::OAuth::PASSWORD ? 1234 : nil
|
|
13
|
+
}
|
|
12
14
|
)
|
|
13
15
|
end
|
|
14
|
-
let(:client) {
|
|
16
|
+
let(:client) { FactoryBot.create(:application) }
|
|
15
17
|
let(:owner) { double :owner, id: 99 }
|
|
16
18
|
|
|
17
19
|
subject do
|
|
@@ -22,6 +24,7 @@ module Doorkeeper::OAuth
|
|
|
22
24
|
expect do
|
|
23
25
|
subject.authorize
|
|
24
26
|
end.to change { client.reload.access_tokens.count }.by(1)
|
|
27
|
+
expect(client.reload.access_tokens.sort_by(&:created_at).last.expires_in).to eq(1234)
|
|
25
28
|
end
|
|
26
29
|
|
|
27
30
|
it 'issues a new token without a client' do
|
|
@@ -53,7 +56,7 @@ module Doorkeeper::OAuth
|
|
|
53
56
|
end
|
|
54
57
|
|
|
55
58
|
it 'creates token even when there is already one (default)' do
|
|
56
|
-
|
|
59
|
+
FactoryBot.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
|
|
57
60
|
expect do
|
|
58
61
|
subject.authorize
|
|
59
62
|
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
|
@@ -61,12 +64,18 @@ module Doorkeeper::OAuth
|
|
|
61
64
|
|
|
62
65
|
it 'skips token creation if there is already one' do
|
|
63
66
|
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
|
64
|
-
|
|
67
|
+
FactoryBot.create(:access_token, application_id: client.id, resource_owner_id: owner.id)
|
|
65
68
|
expect do
|
|
66
69
|
subject.authorize
|
|
67
70
|
end.to_not change { Doorkeeper::AccessToken.count }
|
|
68
71
|
end
|
|
69
72
|
|
|
73
|
+
it "calls configured request callback methods" do
|
|
74
|
+
expect(Doorkeeper.configuration.before_successful_strategy_response).to receive(:call).with(subject).once
|
|
75
|
+
expect(Doorkeeper.configuration.after_successful_strategy_response).to receive(:call).with(subject, instance_of(Doorkeeper::OAuth::TokenResponse)).once
|
|
76
|
+
subject.authorize
|
|
77
|
+
end
|
|
78
|
+
|
|
70
79
|
describe 'with scopes' do
|
|
71
80
|
subject do
|
|
72
81
|
PasswordAccessTokenRequest.new(server, client, owner, scope: 'public')
|
|
@@ -86,5 +95,37 @@ module Doorkeeper::OAuth
|
|
|
86
95
|
expect(Doorkeeper::AccessToken.last.scopes).to include('public')
|
|
87
96
|
end
|
|
88
97
|
end
|
|
98
|
+
|
|
99
|
+
describe 'with custom expiry' do
|
|
100
|
+
let(:server) do
|
|
101
|
+
double(
|
|
102
|
+
:server,
|
|
103
|
+
default_scopes: Doorkeeper::OAuth::Scopes.new,
|
|
104
|
+
access_token_expires_in: 2.hours,
|
|
105
|
+
refresh_token_enabled?: false,
|
|
106
|
+
custom_access_token_expires_in: lambda { |context|
|
|
107
|
+
context.scopes.exists?('public') ? 222 : nil
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
it 'checks scopes' do
|
|
113
|
+
subject = PasswordAccessTokenRequest.new(server, client, owner, scope: 'public')
|
|
114
|
+
allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string('public'))
|
|
115
|
+
expect do
|
|
116
|
+
subject.authorize
|
|
117
|
+
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
|
118
|
+
expect(Doorkeeper::AccessToken.last.expires_in).to eq(222)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it 'falls back to the default otherwise' do
|
|
122
|
+
subject = PasswordAccessTokenRequest.new(server, client, owner, scope: 'private')
|
|
123
|
+
allow(server).to receive(:scopes).and_return(Doorkeeper::OAuth::Scopes.from_string('private'))
|
|
124
|
+
expect do
|
|
125
|
+
subject.authorize
|
|
126
|
+
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
|
127
|
+
expect(Doorkeeper::AccessToken.last.expires_in).to eq(2.hours)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
89
130
|
end
|
|
90
131
|
end
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
module Doorkeeper::OAuth
|
|
4
4
|
describe PreAuthorization do
|
|
5
|
-
let(:server)
|
|
5
|
+
let(:server) do
|
|
6
6
|
server = Doorkeeper.configuration
|
|
7
7
|
allow(server).to receive(:default_scopes).and_return(Scopes.new)
|
|
8
8
|
allow(server).to receive(:scopes).and_return(Scopes.from_string('public profile'))
|
|
9
9
|
server
|
|
10
|
-
|
|
10
|
+
end
|
|
11
11
|
|
|
12
12
|
let(:application) do
|
|
13
13
|
application = double :application
|
|
@@ -123,9 +123,19 @@ module Doorkeeper::OAuth
|
|
|
123
123
|
expect(subject.scopes).to eq(Scopes.from_string('default'))
|
|
124
124
|
end
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
126
|
+
context 'with native redirect uri' do
|
|
127
|
+
let(:native_redirect_uri) { 'urn:ietf:wg:oauth:2.0:oob' }
|
|
128
|
+
|
|
129
|
+
it 'accepts redirect_uri when it matches with the client' do
|
|
130
|
+
subject.redirect_uri = native_redirect_uri
|
|
131
|
+
allow(subject.client).to receive(:redirect_uri) { native_redirect_uri }
|
|
132
|
+
expect(subject).to be_authorizable
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it 'invalidates redirect_uri when it does\'n match with the client' do
|
|
136
|
+
subject.redirect_uri = 'urn:ietf:wg:oauth:2.0:oob'
|
|
137
|
+
expect(subject).not_to be_authorizable
|
|
138
|
+
end
|
|
129
139
|
end
|
|
130
140
|
|
|
131
141
|
it 'matches the redirect uri against client\'s one' do
|
|
@@ -151,5 +161,29 @@ module Doorkeeper::OAuth
|
|
|
151
161
|
subject.redirect_uri = nil
|
|
152
162
|
expect(subject).not_to be_authorizable
|
|
153
163
|
end
|
|
164
|
+
|
|
165
|
+
describe "as_json" do
|
|
166
|
+
let(:client_id) { "client_uid_123" }
|
|
167
|
+
let(:client_name) { "Acme Co." }
|
|
168
|
+
|
|
169
|
+
before do
|
|
170
|
+
allow(client).to receive(:uid).and_return client_id
|
|
171
|
+
allow(client).to receive(:name).and_return client_name
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
let(:json) { subject.as_json({}) }
|
|
175
|
+
|
|
176
|
+
it { is_expected.to respond_to :as_json }
|
|
177
|
+
|
|
178
|
+
it "returns correct values" do
|
|
179
|
+
expect(json[:client_id]).to eq client_id
|
|
180
|
+
expect(json[:redirect_uri]).to eq subject.redirect_uri
|
|
181
|
+
expect(json[:state]).to eq subject.state
|
|
182
|
+
expect(json[:response_type]).to eq subject.response_type
|
|
183
|
+
expect(json[:scope]).to eq subject.scope
|
|
184
|
+
expect(json[:client_name]).to eq client_name
|
|
185
|
+
expect(json[:status]).to eq I18n.t('doorkeeper.pre_authorization.status')
|
|
186
|
+
end
|
|
187
|
+
end
|
|
154
188
|
end
|
|
155
189
|
end
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
module Doorkeeper::OAuth
|
|
4
4
|
describe RefreshTokenRequest do
|
|
5
5
|
before do
|
|
6
6
|
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
|
|
7
7
|
end
|
|
8
|
+
|
|
8
9
|
let(:server) do
|
|
9
10
|
double :server,
|
|
10
11
|
access_token_expires_in: 2.minutes,
|
|
11
|
-
custom_access_token_expires_in: ->
|
|
12
|
+
custom_access_token_expires_in: ->(_context) { nil }
|
|
12
13
|
end
|
|
14
|
+
|
|
13
15
|
let(:refresh_token) do
|
|
14
|
-
|
|
16
|
+
FactoryBot.create(:access_token, use_refresh_token: true)
|
|
15
17
|
end
|
|
18
|
+
|
|
16
19
|
let(:client) { refresh_token.application }
|
|
17
20
|
let(:credentials) { Client::Credentials.new(client.uid, client.secret) }
|
|
18
21
|
|
|
@@ -20,25 +23,35 @@ module Doorkeeper::OAuth
|
|
|
20
23
|
|
|
21
24
|
it 'issues a new token for the client' do
|
|
22
25
|
expect { subject.authorize }.to change { client.reload.access_tokens.count }.by(1)
|
|
23
|
-
|
|
26
|
+
# #sort_by used for MongoDB ORM extensions for valid ordering
|
|
27
|
+
expect(client.reload.access_tokens.sort_by(&:created_at).last.expires_in).to eq(120)
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
it 'issues a new token for the client with custom expires_in' do
|
|
27
31
|
server = double :server,
|
|
28
32
|
access_token_expires_in: 2.minutes,
|
|
29
|
-
custom_access_token_expires_in:
|
|
33
|
+
custom_access_token_expires_in: lambda { |context|
|
|
34
|
+
context.grant_type == Doorkeeper::OAuth::REFRESH_TOKEN ? 1234 : nil
|
|
35
|
+
}
|
|
30
36
|
|
|
31
37
|
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
|
|
32
38
|
|
|
33
39
|
RefreshTokenRequest.new(server, refresh_token, credentials).authorize
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
# #sort_by used for MongoDB ORM extensions for valid ordering
|
|
42
|
+
expect(client.reload.access_tokens.sort_by(&:created_at).last.expires_in).to eq(1234)
|
|
36
43
|
end
|
|
37
44
|
|
|
38
45
|
it 'revokes the previous token' do
|
|
39
46
|
expect { subject.authorize }.to change { refresh_token.revoked? }.from(false).to(true)
|
|
40
47
|
end
|
|
41
48
|
|
|
49
|
+
it "calls configured request callback methods" do
|
|
50
|
+
expect(Doorkeeper.configuration.before_successful_strategy_response).to receive(:call).with(subject).once
|
|
51
|
+
expect(Doorkeeper.configuration.after_successful_strategy_response).to receive(:call).with(subject, instance_of(Doorkeeper::OAuth::TokenResponse)).once
|
|
52
|
+
subject.authorize
|
|
53
|
+
end
|
|
54
|
+
|
|
42
55
|
it 'requires the refresh token' do
|
|
43
56
|
subject.refresh_token = nil
|
|
44
57
|
subject.validate
|
|
@@ -52,7 +65,7 @@ module Doorkeeper::OAuth
|
|
|
52
65
|
end
|
|
53
66
|
|
|
54
67
|
it "requires the token's client and current client to match" do
|
|
55
|
-
subject.client =
|
|
68
|
+
subject.client = FactoryBot.create(:application)
|
|
56
69
|
subject.validate
|
|
57
70
|
expect(subject.error).to eq(:invalid_grant)
|
|
58
71
|
end
|
|
@@ -74,7 +87,9 @@ module Doorkeeper::OAuth
|
|
|
74
87
|
let(:server) do
|
|
75
88
|
double :server,
|
|
76
89
|
access_token_expires_in: 2.minutes,
|
|
77
|
-
custom_access_token_expires_in:
|
|
90
|
+
custom_access_token_expires_in: lambda { |context|
|
|
91
|
+
context.grant_type == Doorkeeper::OAuth::REFRESH_TOKEN ? 1234 : nil
|
|
92
|
+
}
|
|
78
93
|
end
|
|
79
94
|
|
|
80
95
|
before do
|
|
@@ -93,13 +108,14 @@ module Doorkeeper::OAuth
|
|
|
93
108
|
it 'sets the previous refresh token in the new access token' do
|
|
94
109
|
subject.authorize
|
|
95
110
|
expect(
|
|
96
|
-
|
|
111
|
+
# #sort_by used for MongoDB ORM extensions for valid ordering
|
|
112
|
+
client.access_tokens.sort_by(&:created_at).last.previous_refresh_token
|
|
97
113
|
).to eq(refresh_token.refresh_token)
|
|
98
114
|
end
|
|
99
115
|
end
|
|
100
116
|
|
|
101
117
|
context 'clientless access tokens' do
|
|
102
|
-
let!(:refresh_token) {
|
|
118
|
+
let!(:refresh_token) { FactoryBot.create(:clientless_access_token, use_refresh_token: true) }
|
|
103
119
|
|
|
104
120
|
subject { RefreshTokenRequest.new server, refresh_token, nil }
|
|
105
121
|
|
|
@@ -110,7 +126,7 @@ module Doorkeeper::OAuth
|
|
|
110
126
|
|
|
111
127
|
context 'with scopes' do
|
|
112
128
|
let(:refresh_token) do
|
|
113
|
-
|
|
129
|
+
FactoryBot.create :access_token,
|
|
114
130
|
use_refresh_token: true,
|
|
115
131
|
scopes: 'public write'
|
|
116
132
|
end
|
|
@@ -119,13 +135,13 @@ module Doorkeeper::OAuth
|
|
|
119
135
|
|
|
120
136
|
it 'transfers scopes from the old token to the new token' do
|
|
121
137
|
subject.authorize
|
|
122
|
-
expect(Doorkeeper::AccessToken.last.scopes).to eq([
|
|
138
|
+
expect(Doorkeeper::AccessToken.last.scopes).to eq(%i[public write])
|
|
123
139
|
end
|
|
124
140
|
|
|
125
141
|
it 'reduces scopes to the provided scopes' do
|
|
126
142
|
parameters[:scopes] = 'public'
|
|
127
143
|
subject.authorize
|
|
128
|
-
expect(Doorkeeper::AccessToken.last.scopes).to eq([
|
|
144
|
+
expect(Doorkeeper::AccessToken.last.scopes).to eq(%i[public])
|
|
129
145
|
end
|
|
130
146
|
|
|
131
147
|
it 'validates that scopes are included in the original access token' do
|
|
@@ -139,7 +155,7 @@ module Doorkeeper::OAuth
|
|
|
139
155
|
parameters[:scopes] = 'public update'
|
|
140
156
|
parameters[:scope] = 'public'
|
|
141
157
|
subject.authorize
|
|
142
|
-
expect(Doorkeeper::AccessToken.last.scopes).to eq([
|
|
158
|
+
expect(Doorkeeper::AccessToken.last.scopes).to eq(%i[public])
|
|
143
159
|
end
|
|
144
160
|
|
|
145
161
|
it 'uses params[:scope] in favor of scopes if present (invalid)' do
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
require 'active_support/core_ext/module/delegation'
|
|
3
|
-
require 'active_support/core_ext/string'
|
|
4
|
-
require 'doorkeeper/oauth/scopes'
|
|
5
2
|
|
|
6
3
|
module Doorkeeper::OAuth
|
|
7
4
|
describe Scopes do
|
|
@@ -62,7 +59,7 @@ module Doorkeeper::OAuth
|
|
|
62
59
|
describe '#+' do
|
|
63
60
|
it 'can add to another scope object' do
|
|
64
61
|
scopes = Scopes.from_string('public') + Scopes.from_string('admin')
|
|
65
|
-
expect(scopes.all).to eq(%w
|
|
62
|
+
expect(scopes.all).to eq(%w[public admin])
|
|
66
63
|
end
|
|
67
64
|
|
|
68
65
|
it 'does not change the existing object' do
|
|
@@ -70,6 +67,11 @@ module Doorkeeper::OAuth
|
|
|
70
67
|
expect(origin.to_s).to eq('public')
|
|
71
68
|
end
|
|
72
69
|
|
|
70
|
+
it 'can add an array to a scope object' do
|
|
71
|
+
scopes = Scopes.from_string('public') + ['admin']
|
|
72
|
+
expect(scopes.all).to eq(%w[public admin])
|
|
73
|
+
end
|
|
74
|
+
|
|
73
75
|
it 'raises an error if cannot handle addition' do
|
|
74
76
|
expect do
|
|
75
77
|
Scopes.from_string('public') + 'admin'
|
|
@@ -77,6 +79,24 @@ module Doorkeeper::OAuth
|
|
|
77
79
|
end
|
|
78
80
|
end
|
|
79
81
|
|
|
82
|
+
describe '#&' do
|
|
83
|
+
it 'can get intersection with another scope object' do
|
|
84
|
+
scopes = Scopes.from_string('public admin') & Scopes.from_string('write admin')
|
|
85
|
+
expect(scopes.all).to eq(%w[admin])
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'does not change the existing object' do
|
|
89
|
+
origin = Scopes.from_string('public admin')
|
|
90
|
+
origin & Scopes.from_string('write admin')
|
|
91
|
+
expect(origin.to_s).to eq('public admin')
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it 'can get intersection with an array' do
|
|
95
|
+
scopes = Scopes.from_string('public admin') & %w[write admin]
|
|
96
|
+
expect(scopes.all).to eq(%w[admin])
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
80
100
|
describe '#==' do
|
|
81
101
|
it 'is equal to another set of scopes' do
|
|
82
102
|
expect(Scopes.from_string('public')).to eq(Scopes.from_string('public'))
|
|
@@ -89,6 +109,10 @@ module Doorkeeper::OAuth
|
|
|
89
109
|
it 'differs from another set of scopes when scopes are not the same' do
|
|
90
110
|
expect(Scopes.from_string('public write')).not_to eq(Scopes.from_string('write'))
|
|
91
111
|
end
|
|
112
|
+
|
|
113
|
+
it "does not raise an error when compared to a non-enumerable object" do
|
|
114
|
+
expect { Scopes.from_string("public") == false }.not_to raise_error
|
|
115
|
+
end
|
|
92
116
|
end
|
|
93
117
|
|
|
94
118
|
describe '#has_scopes?' do
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
module Doorkeeper::OAuth
|
|
4
4
|
describe TokenRequest do
|
|
5
5
|
let :application do
|
|
6
|
-
|
|
7
|
-
double(:application, id: 9990, scopes: scopes)
|
|
6
|
+
FactoryBot.create(:application, scopes: "public")
|
|
8
7
|
end
|
|
8
|
+
|
|
9
9
|
let :pre_auth do
|
|
10
10
|
double(
|
|
11
11
|
:pre_auth,
|
|
@@ -38,9 +38,7 @@ module Doorkeeper::OAuth
|
|
|
38
38
|
|
|
39
39
|
it 'does not create token when not authorizable' do
|
|
40
40
|
allow(pre_auth).to receive(:authorizable?).and_return(false)
|
|
41
|
-
expect
|
|
42
|
-
subject.authorize
|
|
43
|
-
end.to_not change { Doorkeeper::AccessToken.count }
|
|
41
|
+
expect { subject.authorize }.not_to change { Doorkeeper::AccessToken.count }
|
|
44
42
|
end
|
|
45
43
|
|
|
46
44
|
it 'returns a error response' do
|
|
@@ -52,8 +50,8 @@ module Doorkeeper::OAuth
|
|
|
52
50
|
before do
|
|
53
51
|
Doorkeeper.configure do
|
|
54
52
|
orm DOORKEEPER_ORM
|
|
55
|
-
custom_access_token_expires_in do |
|
|
56
|
-
1234
|
|
53
|
+
custom_access_token_expires_in do |context|
|
|
54
|
+
context.grant_type == Doorkeeper::OAuth::IMPLICIT ? 1234 : nil
|
|
57
55
|
end
|
|
58
56
|
end
|
|
59
57
|
end
|
|
@@ -75,7 +73,7 @@ module Doorkeeper::OAuth
|
|
|
75
73
|
|
|
76
74
|
it 'creates a new token if scopes do not match' do
|
|
77
75
|
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
|
78
|
-
|
|
76
|
+
FactoryBot.create(:access_token, application_id: pre_auth.client.id,
|
|
79
77
|
resource_owner_id: owner.id, scopes: '')
|
|
80
78
|
expect do
|
|
81
79
|
subject.authorize
|
|
@@ -86,12 +84,11 @@ module Doorkeeper::OAuth
|
|
|
86
84
|
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
|
87
85
|
allow(application.scopes).to receive(:has_scopes?).and_return(true)
|
|
88
86
|
allow(application.scopes).to receive(:all?).and_return(true)
|
|
89
|
-
|
|
87
|
+
|
|
88
|
+
FactoryBot.create(:access_token, application_id: pre_auth.client.id,
|
|
90
89
|
resource_owner_id: owner.id, scopes: 'public')
|
|
91
90
|
|
|
92
|
-
expect
|
|
93
|
-
subject.authorize
|
|
94
|
-
end.to_not change { Doorkeeper::AccessToken.count }
|
|
91
|
+
expect { subject.authorize }.not_to change { Doorkeeper::AccessToken.count }
|
|
95
92
|
end
|
|
96
93
|
end
|
|
97
94
|
end
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
require 'active_support/core_ext/string'
|
|
3
|
-
require 'doorkeeper/oauth/token'
|
|
4
2
|
|
|
5
3
|
module Doorkeeper
|
|
6
4
|
unless defined?(AccessToken)
|
|
@@ -14,7 +12,7 @@ module Doorkeeper
|
|
|
14
12
|
let(:request) { double.as_null_object }
|
|
15
13
|
|
|
16
14
|
let(:method) do
|
|
17
|
-
->(
|
|
15
|
+
->(*) { 'token-value' }
|
|
18
16
|
end
|
|
19
17
|
|
|
20
18
|
it 'accepts anything that responds to #call' do
|
|
@@ -96,19 +94,44 @@ module Doorkeeper
|
|
|
96
94
|
end
|
|
97
95
|
|
|
98
96
|
describe :authenticate do
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
context 'refresh tokens are disabled (default)' do
|
|
98
|
+
context 'refresh tokens are enabled' do
|
|
99
|
+
it 'does not revoke previous refresh_token if token was found' do
|
|
100
|
+
token = ->(_r) { 'token' }
|
|
101
|
+
expect(
|
|
102
|
+
AccessToken
|
|
103
|
+
).to receive(:by_token).with('token').and_return(token)
|
|
104
|
+
expect(token).not_to receive(:revoke_previous_refresh_token!)
|
|
105
|
+
Token.authenticate double, token
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'calls the finder if token was returned' do
|
|
110
|
+
token = ->(_r) { 'token' }
|
|
111
|
+
expect(AccessToken).to receive(:by_token).with('token')
|
|
112
|
+
Token.authenticate double, token
|
|
113
|
+
end
|
|
103
114
|
end
|
|
104
115
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
context 'refresh tokens are enabled' do
|
|
117
|
+
before do
|
|
118
|
+
Doorkeeper.configure { use_refresh_token }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it 'revokes previous refresh_token if token was found' do
|
|
122
|
+
token = ->(_r) { 'token' }
|
|
123
|
+
expect(
|
|
124
|
+
AccessToken
|
|
125
|
+
).to receive(:by_token).with('token').and_return(token)
|
|
126
|
+
expect(token).to receive(:revoke_previous_refresh_token!)
|
|
127
|
+
Token.authenticate double, token
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'calls the finder if token was returned' do
|
|
131
|
+
token = ->(_r) { 'token' }
|
|
132
|
+
expect(AccessToken).to receive(:by_token).with('token')
|
|
133
|
+
Token.authenticate double, token
|
|
134
|
+
end
|
|
112
135
|
end
|
|
113
136
|
end
|
|
114
137
|
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Doorkeeper::Orm::ActiveRecord::StaleRecordsCleaner do
|
|
6
|
+
let(:cleaner) { described_class.new(model) }
|
|
7
|
+
let(:models_by_name) do
|
|
8
|
+
{
|
|
9
|
+
access_token: Doorkeeper::AccessToken,
|
|
10
|
+
access_grant: Doorkeeper::AccessGrant
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
%i[access_token access_grant].each do |model_name|
|
|
15
|
+
context "(#{model_name})" do
|
|
16
|
+
let(:model) { models_by_name.fetch(model_name) }
|
|
17
|
+
|
|
18
|
+
describe '#clean_revoked' do
|
|
19
|
+
subject { cleaner.clean_revoked }
|
|
20
|
+
|
|
21
|
+
context 'with revoked record' do
|
|
22
|
+
before do
|
|
23
|
+
FactoryBot.create model_name, revoked_at: Time.current - 1.minute
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'removes the record' do
|
|
27
|
+
expect { subject }.to change { model.count }.to(0)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
context 'with record revoked in the future' do
|
|
32
|
+
before do
|
|
33
|
+
FactoryBot.create model_name, revoked_at: Time.current + 1.minute
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'keeps the record' do
|
|
37
|
+
expect { subject }.not_to change { model.count }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
context 'with unrevoked record' do
|
|
42
|
+
before do
|
|
43
|
+
FactoryBot.create model_name, revoked_at: nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'keeps the record' do
|
|
47
|
+
expect { subject }.not_to change { model.count }
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe '#clean_expired' do
|
|
53
|
+
subject { cleaner.clean_expired(ttl) }
|
|
54
|
+
let(:ttl) { 500 }
|
|
55
|
+
let(:expiry_border) { ttl.seconds.ago }
|
|
56
|
+
|
|
57
|
+
context 'with record that is expired' do
|
|
58
|
+
before do
|
|
59
|
+
FactoryBot.create model_name, created_at: expiry_border - 1.minute
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'removes the record' do
|
|
63
|
+
expect { subject }.to change { model.count }.to(0)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
context 'with record that is not expired' do
|
|
68
|
+
before do
|
|
69
|
+
FactoryBot.create model_name, created_at: expiry_border + 1.minute
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'keeps the record' do
|
|
73
|
+
expect { subject }.not_to change { model.count }
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
data/spec/lib/server_spec.rb
CHANGED
|
@@ -45,5 +45,15 @@ describe Doorkeeper::Server do
|
|
|
45
45
|
expect(fake_class).to receive(:new).with(subject)
|
|
46
46
|
subject.authorization_request :code
|
|
47
47
|
end
|
|
48
|
+
|
|
49
|
+
it 'builds the request with composite strategy name' do
|
|
50
|
+
allow(Doorkeeper.configuration).
|
|
51
|
+
to receive(:authorization_response_types).
|
|
52
|
+
and_return(['id_token token'])
|
|
53
|
+
|
|
54
|
+
stub_const 'Doorkeeper::Request::IdTokenToken', fake_class
|
|
55
|
+
expect(fake_class).to receive(:new).with(subject)
|
|
56
|
+
subject.authorization_request 'id_token token'
|
|
57
|
+
end
|
|
48
58
|
end
|
|
49
59
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe Doorkeeper::AccessGrant do
|
|
4
|
-
subject {
|
|
4
|
+
subject { FactoryBot.build(:access_grant) }
|
|
5
5
|
|
|
6
6
|
it { expect(subject).to be_valid }
|
|
7
7
|
|
|
@@ -33,4 +33,47 @@ describe Doorkeeper::AccessGrant do
|
|
|
33
33
|
expect(subject).not_to be_valid
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
|
+
|
|
37
|
+
describe '.revoke_all_for' do
|
|
38
|
+
let(:resource_owner) { double(id: 100) }
|
|
39
|
+
let(:application) { FactoryBot.create :application }
|
|
40
|
+
let(:default_attributes) do
|
|
41
|
+
{
|
|
42
|
+
application: application,
|
|
43
|
+
resource_owner_id: resource_owner.id
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'revokes all tokens for given application and resource owner' do
|
|
48
|
+
FactoryBot.create :access_grant, default_attributes
|
|
49
|
+
|
|
50
|
+
described_class.revoke_all_for(application.id, resource_owner)
|
|
51
|
+
|
|
52
|
+
described_class.all.each do |token|
|
|
53
|
+
expect(token).to be_revoked
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'matches application' do
|
|
58
|
+
access_grant_for_different_app = FactoryBot.create(
|
|
59
|
+
:access_grant,
|
|
60
|
+
default_attributes.merge(application: FactoryBot.create(:application))
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
described_class.revoke_all_for(application.id, resource_owner)
|
|
64
|
+
|
|
65
|
+
expect(access_grant_for_different_app.reload).not_to be_revoked
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'matches resource owner' do
|
|
69
|
+
access_grant_for_different_owner = FactoryBot.create(
|
|
70
|
+
:access_grant,
|
|
71
|
+
default_attributes.merge(resource_owner_id: 90)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
described_class.revoke_all_for application.id, resource_owner
|
|
75
|
+
|
|
76
|
+
expect(access_grant_for_different_owner.reload).not_to be_revoked
|
|
77
|
+
end
|
|
78
|
+
end
|
|
36
79
|
end
|