doorkeeper 5.0.0 → 5.1.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 +5 -5
- data/.hound.yml +2 -1
- data/.rubocop.yml +37 -4
- data/.travis.yml +16 -21
- data/Appraisals +29 -7
- data/Dangerfile +67 -0
- data/Gemfile +19 -5
- data/NEWS.md +107 -10
- data/README.md +75 -470
- data/Rakefile +10 -8
- data/app/controllers/doorkeeper/application_controller.rb +3 -2
- data/app/controllers/doorkeeper/application_metal_controller.rb +4 -13
- data/app/controllers/doorkeeper/applications_controller.rb +21 -7
- data/app/controllers/doorkeeper/authorizations_controller.rb +3 -3
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +2 -0
- data/app/controllers/doorkeeper/token_info_controller.rb +4 -2
- data/app/controllers/doorkeeper/tokens_controller.rb +13 -11
- data/app/helpers/doorkeeper/dashboard_helper.rb +3 -1
- data/app/validators/redirect_uri_validator.rb +7 -2
- data/app/views/doorkeeper/applications/_form.html.erb +6 -0
- data/app/views/doorkeeper/applications/show.html.erb +1 -1
- data/app/views/layouts/doorkeeper/admin.html.erb +5 -3
- data/bin/console +16 -0
- data/config/locales/en.yml +1 -0
- data/doorkeeper.gemspec +28 -24
- data/gemfiles/rails_5_0.gemfile +9 -4
- data/gemfiles/rails_5_1.gemfile +9 -4
- data/gemfiles/rails_5_2.gemfile +10 -5
- data/gemfiles/rails_6_0.gemfile +17 -0
- data/gemfiles/rails_master.gemfile +9 -9
- data/lib/doorkeeper/config/option.rb +64 -0
- data/lib/doorkeeper/config.rb +197 -76
- data/lib/doorkeeper/engine.rb +3 -1
- data/lib/doorkeeper/errors.rb +17 -0
- data/lib/doorkeeper/grape/authorization_decorator.rb +6 -4
- data/lib/doorkeeper/grape/helpers.rb +5 -3
- data/lib/doorkeeper/helpers/controller.rb +5 -2
- data/lib/doorkeeper/models/access_grant_mixin.rb +25 -6
- data/lib/doorkeeper/models/access_token_mixin.rb +68 -22
- data/lib/doorkeeper/models/application_mixin.rb +45 -1
- data/lib/doorkeeper/models/concerns/accessible.rb +2 -0
- data/lib/doorkeeper/models/concerns/expirable.rb +6 -2
- data/lib/doorkeeper/models/concerns/orderable.rb +2 -0
- data/lib/doorkeeper/models/concerns/ownership.rb +3 -6
- data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
- data/lib/doorkeeper/models/concerns/revocable.rb +4 -1
- data/lib/doorkeeper/models/concerns/scopes.rb +7 -1
- data/lib/doorkeeper/models/concerns/secret_storable.rb +108 -0
- data/lib/doorkeeper/oauth/authorization/code.rb +4 -2
- data/lib/doorkeeper/oauth/authorization/context.rb +2 -0
- data/lib/doorkeeper/oauth/authorization/token.rb +14 -7
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +3 -1
- data/lib/doorkeeper/oauth/authorization_code_request.rb +8 -4
- data/lib/doorkeeper/oauth/base_request.rb +2 -0
- data/lib/doorkeeper/oauth/base_response.rb +2 -0
- data/lib/doorkeeper/oauth/client/credentials.rb +2 -0
- data/lib/doorkeeper/oauth/client.rb +3 -1
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +2 -0
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +2 -0
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +7 -4
- data/lib/doorkeeper/oauth/client_credentials_request.rb +3 -1
- data/lib/doorkeeper/oauth/code_request.rb +2 -0
- data/lib/doorkeeper/oauth/code_response.rb +4 -2
- data/lib/doorkeeper/oauth/error.rb +2 -0
- data/lib/doorkeeper/oauth/error_response.rb +20 -6
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +10 -3
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +25 -8
- data/lib/doorkeeper/oauth/helpers/unique_token.rb +15 -1
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +42 -3
- data/lib/doorkeeper/oauth/invalid_token_response.rb +22 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -2
- data/lib/doorkeeper/oauth/pre_authorization.rb +13 -5
- data/lib/doorkeeper/oauth/refresh_token_request.rb +15 -4
- data/lib/doorkeeper/oauth/scopes.rb +7 -3
- data/lib/doorkeeper/oauth/token.rb +4 -2
- data/lib/doorkeeper/oauth/token_introspection.rb +76 -8
- data/lib/doorkeeper/oauth/token_request.rb +2 -0
- data/lib/doorkeeper/oauth/token_response.rb +11 -9
- data/lib/doorkeeper/oauth.rb +5 -5
- data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -13
- data/lib/doorkeeper/orm/active_record/access_token.rb +6 -13
- data/lib/doorkeeper/orm/active_record/application.rb +26 -7
- data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +10 -3
- data/lib/doorkeeper/orm/active_record.rb +8 -6
- data/lib/doorkeeper/rails/helpers.rb +5 -1
- data/lib/doorkeeper/rails/routes/mapper.rb +2 -0
- data/lib/doorkeeper/rails/routes/mapping.rb +9 -7
- data/lib/doorkeeper/rails/routes.rb +19 -12
- data/lib/doorkeeper/rake/db.rake +17 -17
- data/lib/doorkeeper/rake.rb +1 -1
- data/lib/doorkeeper/request/authorization_code.rb +2 -0
- data/lib/doorkeeper/request/client_credentials.rb +2 -0
- data/lib/doorkeeper/request/code.rb +2 -0
- data/lib/doorkeeper/request/password.rb +2 -0
- data/lib/doorkeeper/request/refresh_token.rb +2 -0
- data/lib/doorkeeper/request/strategy.rb +2 -0
- data/lib/doorkeeper/request/token.rb +2 -0
- data/lib/doorkeeper/request.rb +3 -1
- data/lib/doorkeeper/secret_storing/base.rb +64 -0
- data/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
- data/lib/doorkeeper/secret_storing/plain.rb +33 -0
- data/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
- data/lib/doorkeeper/server.rb +2 -0
- data/lib/doorkeeper/stale_records_cleaner.rb +20 -0
- data/lib/doorkeeper/validations.rb +2 -0
- data/lib/doorkeeper/version.rb +4 -2
- data/lib/doorkeeper.rb +69 -59
- data/lib/generators/doorkeeper/application_owner_generator.rb +10 -9
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +10 -9
- data/lib/generators/doorkeeper/install_generator.rb +11 -9
- data/lib/generators/doorkeeper/migration_generator.rb +9 -9
- data/lib/generators/doorkeeper/pkce_generator.rb +10 -9
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +10 -9
- data/lib/generators/doorkeeper/templates/initializer.rb +138 -13
- data/lib/generators/doorkeeper/templates/migration.rb.erb +17 -10
- data/lib/generators/doorkeeper/views_generator.rb +6 -4
- data/spec/controllers/application_metal_controller_spec.rb +24 -10
- data/spec/controllers/applications_controller_spec.rb +76 -78
- data/spec/controllers/authorizations_controller_spec.rb +189 -142
- data/spec/controllers/protected_resources_controller_spec.rb +111 -67
- data/spec/controllers/token_info_controller_spec.rb +14 -12
- data/spec/controllers/tokens_controller_spec.rb +170 -112
- data/spec/dummy/Rakefile +3 -1
- data/spec/dummy/app/controllers/application_controller.rb +3 -1
- data/spec/dummy/app/controllers/custom_authorizations_controller.rb +3 -1
- data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -2
- data/spec/dummy/app/controllers/home_controller.rb +6 -5
- data/spec/dummy/app/controllers/metal_controller.rb +2 -0
- data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +4 -2
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/user.rb +2 -0
- data/spec/dummy/config/application.rb +26 -2
- data/spec/dummy/config/boot.rb +2 -4
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/dummy/config/environments/development.rb +2 -0
- data/spec/dummy/config/environments/production.rb +2 -0
- data/spec/dummy/config/environments/test.rb +4 -2
- data/spec/dummy/config/initializers/backtrace_silencers.rb +2 -0
- data/spec/dummy/config/initializers/doorkeeper.rb +7 -3
- data/spec/dummy/config/initializers/secret_token.rb +3 -1
- data/spec/dummy/config/initializers/session_store.rb +3 -1
- data/spec/dummy/config/initializers/wrap_parameters.rb +2 -0
- data/spec/dummy/config.ru +3 -1
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +20 -13
- data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +1 -1
- data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +2 -0
- data/spec/dummy/db/schema.rb +1 -1
- data/spec/dummy/script/rails +7 -4
- data/spec/factories.rb +11 -9
- data/spec/generators/application_owner_generator_spec.rb +13 -26
- data/spec/generators/confidential_applications_generator_spec.rb +12 -28
- data/spec/generators/install_generator_spec.rb +19 -14
- data/spec/generators/migration_generator_spec.rb +13 -26
- data/spec/generators/pkce_generator_spec.rb +11 -26
- data/spec/generators/previous_refresh_token_generator_spec.rb +16 -29
- data/spec/generators/templates/routes.rb +2 -1
- data/spec/generators/views_generator_spec.rb +14 -12
- data/spec/grape/grape_integration_spec.rb +35 -33
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +9 -7
- data/spec/lib/config_spec.rb +290 -104
- data/spec/lib/doorkeeper_spec.rb +8 -6
- data/spec/lib/models/expirable_spec.rb +22 -8
- data/spec/lib/models/reusable_spec.rb +40 -0
- data/spec/lib/models/revocable_spec.rb +8 -6
- data/spec/lib/models/scopes_spec.rb +27 -13
- data/spec/lib/models/secret_storable_spec.rb +135 -0
- data/spec/lib/oauth/authorization/uri_builder_spec.rb +17 -15
- data/spec/lib/oauth/authorization_code_request_spec.rb +43 -17
- data/spec/lib/oauth/base_request_spec.rb +24 -12
- data/spec/lib/oauth/base_response_spec.rb +3 -1
- data/spec/lib/oauth/client/credentials_spec.rb +26 -24
- data/spec/lib/oauth/client_credentials/creator_spec.rb +56 -10
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +27 -20
- data/spec/lib/oauth/client_credentials/validation_spec.rb +21 -15
- data/spec/lib/oauth/client_credentials_integration_spec.rb +8 -6
- data/spec/lib/oauth/client_credentials_request_spec.rb +27 -21
- data/spec/lib/oauth/client_spec.rb +15 -13
- data/spec/lib/oauth/code_request_spec.rb +10 -8
- data/spec/lib/oauth/code_response_spec.rb +9 -7
- data/spec/lib/oauth/error_response_spec.rb +19 -11
- data/spec/lib/oauth/error_spec.rb +4 -2
- data/spec/lib/oauth/forbidden_token_response_spec.rb +7 -5
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +68 -31
- data/spec/lib/oauth/helpers/unique_token_spec.rb +8 -6
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +124 -93
- data/spec/lib/oauth/invalid_token_response_spec.rb +3 -1
- data/spec/lib/oauth/password_access_token_request_spec.rb +95 -34
- data/spec/lib/oauth/pre_authorization_spec.rb +80 -54
- data/spec/lib/oauth/refresh_token_request_spec.rb +46 -39
- data/spec/lib/oauth/scopes_spec.rb +63 -61
- data/spec/lib/oauth/token_request_spec.rb +80 -25
- data/spec/lib/oauth/token_response_spec.rb +40 -38
- data/spec/lib/oauth/token_spec.rb +64 -45
- data/spec/lib/request/strategy_spec.rb +3 -1
- data/spec/lib/secret_storing/base_spec.rb +60 -0
- data/spec/lib/secret_storing/bcrypt_spec.rb +49 -0
- data/spec/lib/secret_storing/plain_spec.rb +44 -0
- data/spec/lib/secret_storing/sha256_hash_spec.rb +48 -0
- data/spec/lib/server_spec.rb +20 -18
- data/spec/lib/{orm/active_record/stale_records_cleaner_spec.rb → stale_records_cleaner_spec.rb} +28 -18
- data/spec/models/doorkeeper/access_grant_spec.rb +80 -15
- data/spec/models/doorkeeper/access_token_spec.rb +212 -83
- data/spec/models/doorkeeper/application_spec.rb +146 -49
- data/spec/requests/applications/applications_request_spec.rb +100 -68
- data/spec/requests/applications/authorized_applications_spec.rb +20 -18
- data/spec/requests/endpoints/authorization_spec.rb +27 -25
- data/spec/requests/endpoints/token_spec.rb +38 -36
- data/spec/requests/flows/authorization_code_errors_spec.rb +27 -25
- data/spec/requests/flows/authorization_code_spec.rb +260 -158
- data/spec/requests/flows/client_credentials_spec.rb +54 -52
- data/spec/requests/flows/implicit_grant_errors_spec.rb +11 -9
- data/spec/requests/flows/implicit_grant_spec.rb +27 -25
- data/spec/requests/flows/password_spec.rb +64 -56
- data/spec/requests/flows/refresh_token_spec.rb +91 -32
- data/spec/requests/flows/revoke_token_spec.rb +49 -63
- data/spec/requests/flows/skip_authorization_spec.rb +23 -21
- data/spec/requests/protected_resources/metal_spec.rb +7 -5
- data/spec/requests/protected_resources/private_api_spec.rb +37 -35
- data/spec/routing/custom_controller_routes_spec.rb +71 -65
- data/spec/routing/default_routes_spec.rb +22 -20
- data/spec/routing/scoped_routes_spec.rb +20 -18
- data/spec/spec_helper.rb +15 -13
- data/spec/spec_helper_integration.rb +3 -1
- data/spec/support/dependencies/factory_bot.rb +3 -1
- data/spec/support/doorkeeper_rspec.rb +5 -2
- data/spec/support/helpers/access_token_request_helper.rb +3 -1
- data/spec/support/helpers/authorization_request_helper.rb +4 -2
- data/spec/support/helpers/config_helper.rb +2 -0
- data/spec/support/helpers/model_helper.rb +11 -5
- data/spec/support/helpers/request_spec_helper.rb +5 -3
- data/spec/support/helpers/url_helper.rb +20 -18
- data/spec/support/http_method_shim.rb +4 -9
- data/spec/support/orm/active_record.rb +3 -1
- data/spec/support/shared/controllers_shared_context.rb +68 -10
- data/spec/support/shared/hashing_shared_context.rb +36 -0
- data/spec/support/shared/models_shared_examples.rb +12 -10
- data/spec/validators/redirect_uri_validator_spec.rb +76 -47
- data/spec/version/version_spec.rb +7 -5
- metadata +70 -21
- data/gemfiles/rails_4_2.gemfile +0 -13
- data/spec/dummy/config/initializers/new_framework_defaults.rb +0 -10
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
2
4
|
|
|
3
5
|
module Doorkeeper::OAuth
|
|
4
6
|
describe RefreshTokenRequest do
|
|
5
|
-
before do
|
|
6
|
-
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
|
|
7
|
-
end
|
|
8
|
-
|
|
9
7
|
let(:server) do
|
|
10
8
|
double :server,
|
|
11
|
-
access_token_expires_in: 2.minutes
|
|
12
|
-
custom_access_token_expires_in: ->(_context) { nil }
|
|
9
|
+
access_token_expires_in: 2.minutes
|
|
13
10
|
end
|
|
14
11
|
|
|
15
12
|
let(:refresh_token) do
|
|
@@ -19,46 +16,56 @@ module Doorkeeper::OAuth
|
|
|
19
16
|
let(:client) { refresh_token.application }
|
|
20
17
|
let(:credentials) { Client::Credentials.new(client.uid, client.secret) }
|
|
21
18
|
|
|
19
|
+
before do
|
|
20
|
+
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
|
|
21
|
+
allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(false)
|
|
22
|
+
end
|
|
23
|
+
|
|
22
24
|
subject { RefreshTokenRequest.new server, refresh_token, credentials }
|
|
23
25
|
|
|
24
|
-
it
|
|
26
|
+
it "issues a new token for the client" do
|
|
25
27
|
expect { subject.authorize }.to change { client.reload.access_tokens.count }.by(1)
|
|
26
28
|
# #sort_by used for MongoDB ORM extensions for valid ordering
|
|
27
|
-
expect(client.reload.access_tokens.
|
|
29
|
+
expect(client.reload.access_tokens.max_by(&:created_at).expires_in).to eq(120)
|
|
28
30
|
end
|
|
29
31
|
|
|
30
|
-
it
|
|
32
|
+
it "issues a new token for the client with custom expires_in" do
|
|
31
33
|
server = double :server,
|
|
32
34
|
access_token_expires_in: 2.minutes,
|
|
33
35
|
custom_access_token_expires_in: lambda { |context|
|
|
34
36
|
context.grant_type == Doorkeeper::OAuth::REFRESH_TOKEN ? 1234 : nil
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
allow(server).to receive(:option_defined?).with(:custom_access_token_expires_in).and_return(true)
|
|
37
40
|
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false)
|
|
38
41
|
|
|
39
42
|
RefreshTokenRequest.new(server, refresh_token, credentials).authorize
|
|
40
43
|
|
|
41
44
|
# #sort_by used for MongoDB ORM extensions for valid ordering
|
|
42
|
-
expect(client.reload.access_tokens.
|
|
45
|
+
expect(client.reload.access_tokens.max_by(&:created_at).expires_in).to eq(1234)
|
|
43
46
|
end
|
|
44
47
|
|
|
45
|
-
it
|
|
48
|
+
it "revokes the previous token" do
|
|
46
49
|
expect { subject.authorize }.to change { refresh_token.revoked? }.from(false).to(true)
|
|
47
50
|
end
|
|
48
51
|
|
|
49
52
|
it "calls configured request callback methods" do
|
|
50
|
-
expect(Doorkeeper.configuration.before_successful_strategy_response)
|
|
51
|
-
|
|
53
|
+
expect(Doorkeeper.configuration.before_successful_strategy_response)
|
|
54
|
+
.to receive(:call).with(subject).once
|
|
55
|
+
|
|
56
|
+
expect(Doorkeeper.configuration.after_successful_strategy_response)
|
|
57
|
+
.to receive(:call).with(subject, instance_of(Doorkeeper::OAuth::TokenResponse)).once
|
|
58
|
+
|
|
52
59
|
subject.authorize
|
|
53
60
|
end
|
|
54
61
|
|
|
55
|
-
it
|
|
62
|
+
it "requires the refresh token" do
|
|
56
63
|
subject.refresh_token = nil
|
|
57
64
|
subject.validate
|
|
58
65
|
expect(subject.error).to eq(:invalid_request)
|
|
59
66
|
end
|
|
60
67
|
|
|
61
|
-
it
|
|
68
|
+
it "requires credentials to be valid if provided" do
|
|
62
69
|
subject.client = nil
|
|
63
70
|
subject.validate
|
|
64
71
|
expect(subject.error).to eq(:invalid_client)
|
|
@@ -70,20 +77,20 @@ module Doorkeeper::OAuth
|
|
|
70
77
|
expect(subject.error).to eq(:invalid_grant)
|
|
71
78
|
end
|
|
72
79
|
|
|
73
|
-
it
|
|
80
|
+
it "rejects revoked tokens" do
|
|
74
81
|
refresh_token.revoke
|
|
75
82
|
subject.validate
|
|
76
83
|
expect(subject.error).to eq(:invalid_grant)
|
|
77
84
|
end
|
|
78
85
|
|
|
79
|
-
it
|
|
86
|
+
it "accepts expired tokens" do
|
|
80
87
|
refresh_token.expires_in = -1
|
|
81
88
|
refresh_token.save
|
|
82
89
|
subject.validate
|
|
83
90
|
expect(subject).to be_valid
|
|
84
91
|
end
|
|
85
92
|
|
|
86
|
-
context
|
|
93
|
+
context "refresh tokens expire on access token use" do
|
|
87
94
|
let(:server) do
|
|
88
95
|
double :server,
|
|
89
96
|
access_token_expires_in: 2.minutes,
|
|
@@ -96,71 +103,71 @@ module Doorkeeper::OAuth
|
|
|
96
103
|
allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(true)
|
|
97
104
|
end
|
|
98
105
|
|
|
99
|
-
it
|
|
106
|
+
it "issues a new token for the client" do
|
|
100
107
|
expect { subject.authorize }.to change { client.reload.access_tokens.count }.by(1)
|
|
101
108
|
end
|
|
102
109
|
|
|
103
|
-
it
|
|
110
|
+
it "does not revoke the previous token" do
|
|
104
111
|
subject.authorize
|
|
105
112
|
expect(refresh_token).not_to be_revoked
|
|
106
113
|
end
|
|
107
114
|
|
|
108
|
-
it
|
|
115
|
+
it "sets the previous refresh token in the new access token" do
|
|
109
116
|
subject.authorize
|
|
110
117
|
expect(
|
|
111
118
|
# #sort_by used for MongoDB ORM extensions for valid ordering
|
|
112
|
-
client.access_tokens.
|
|
119
|
+
client.access_tokens.max_by(&:created_at).previous_refresh_token
|
|
113
120
|
).to eq(refresh_token.refresh_token)
|
|
114
121
|
end
|
|
115
122
|
end
|
|
116
123
|
|
|
117
|
-
context
|
|
124
|
+
context "clientless access tokens" do
|
|
118
125
|
let!(:refresh_token) { FactoryBot.create(:clientless_access_token, use_refresh_token: true) }
|
|
119
126
|
|
|
120
127
|
subject { RefreshTokenRequest.new server, refresh_token, nil }
|
|
121
128
|
|
|
122
|
-
it
|
|
129
|
+
it "issues a new token without a client" do
|
|
123
130
|
expect { subject.authorize }.to change { Doorkeeper::AccessToken.count }.by(1)
|
|
124
131
|
end
|
|
125
132
|
end
|
|
126
133
|
|
|
127
|
-
context
|
|
134
|
+
context "with scopes" do
|
|
128
135
|
let(:refresh_token) do
|
|
129
136
|
FactoryBot.create :access_token,
|
|
130
|
-
|
|
131
|
-
|
|
137
|
+
use_refresh_token: true,
|
|
138
|
+
scopes: "public write"
|
|
132
139
|
end
|
|
133
140
|
let(:parameters) { {} }
|
|
134
141
|
subject { RefreshTokenRequest.new server, refresh_token, credentials, parameters }
|
|
135
142
|
|
|
136
|
-
it
|
|
143
|
+
it "transfers scopes from the old token to the new token" do
|
|
137
144
|
subject.authorize
|
|
138
145
|
expect(Doorkeeper::AccessToken.last.scopes).to eq(%i[public write])
|
|
139
146
|
end
|
|
140
147
|
|
|
141
|
-
it
|
|
142
|
-
parameters[:scopes] =
|
|
148
|
+
it "reduces scopes to the provided scopes" do
|
|
149
|
+
parameters[:scopes] = "public"
|
|
143
150
|
subject.authorize
|
|
144
151
|
expect(Doorkeeper::AccessToken.last.scopes).to eq(%i[public])
|
|
145
152
|
end
|
|
146
153
|
|
|
147
|
-
it
|
|
148
|
-
parameters[:scopes] =
|
|
154
|
+
it "validates that scopes are included in the original access token" do
|
|
155
|
+
parameters[:scopes] = "public update"
|
|
149
156
|
|
|
150
157
|
subject.validate
|
|
151
158
|
expect(subject.error).to eq(:invalid_scope)
|
|
152
159
|
end
|
|
153
160
|
|
|
154
|
-
it
|
|
155
|
-
parameters[:scopes] =
|
|
156
|
-
parameters[:scope] =
|
|
161
|
+
it "uses params[:scope] in favor of scopes if present (valid)" do
|
|
162
|
+
parameters[:scopes] = "public update"
|
|
163
|
+
parameters[:scope] = "public"
|
|
157
164
|
subject.authorize
|
|
158
165
|
expect(Doorkeeper::AccessToken.last.scopes).to eq(%i[public])
|
|
159
166
|
end
|
|
160
167
|
|
|
161
|
-
it
|
|
162
|
-
parameters[:scopes] =
|
|
163
|
-
parameters[:scope] =
|
|
168
|
+
it "uses params[:scope] in favor of scopes if present (invalid)" do
|
|
169
|
+
parameters[:scopes] = "public"
|
|
170
|
+
parameters[:scope] = "public update"
|
|
164
171
|
|
|
165
172
|
subject.validate
|
|
166
173
|
expect(subject.error).to eq(:invalid_scope)
|
|
@@ -1,113 +1,115 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
2
4
|
|
|
3
5
|
module Doorkeeper::OAuth
|
|
4
6
|
describe Scopes do
|
|
5
|
-
describe
|
|
6
|
-
it
|
|
7
|
+
describe "#add" do
|
|
8
|
+
it "allows you to add scopes with symbols" do
|
|
7
9
|
subject.add :public
|
|
8
|
-
expect(subject.all).to eq([
|
|
10
|
+
expect(subject.all).to eq(["public"])
|
|
9
11
|
end
|
|
10
12
|
|
|
11
|
-
it
|
|
12
|
-
subject.add
|
|
13
|
-
expect(subject.all).to eq([
|
|
13
|
+
it "allows you to add scopes with strings" do
|
|
14
|
+
subject.add "public"
|
|
15
|
+
expect(subject.all).to eq(["public"])
|
|
14
16
|
end
|
|
15
17
|
|
|
16
|
-
it
|
|
18
|
+
it "do not add already included scopes" do
|
|
17
19
|
subject.add :public
|
|
18
20
|
subject.add :public
|
|
19
|
-
expect(subject.all).to eq([
|
|
21
|
+
expect(subject.all).to eq(["public"])
|
|
20
22
|
end
|
|
21
23
|
end
|
|
22
24
|
|
|
23
|
-
describe
|
|
25
|
+
describe "#exists" do
|
|
24
26
|
before do
|
|
25
27
|
subject.add :public
|
|
26
28
|
end
|
|
27
29
|
|
|
28
|
-
it
|
|
29
|
-
expect(subject.exists?(
|
|
30
|
+
it "returns true if scope with given name is present" do
|
|
31
|
+
expect(subject.exists?("public")).to be_truthy
|
|
30
32
|
end
|
|
31
33
|
|
|
32
|
-
it
|
|
33
|
-
expect(subject.exists?(
|
|
34
|
+
it "returns false if scope with given name does not exist" do
|
|
35
|
+
expect(subject.exists?("other")).to be_falsey
|
|
34
36
|
end
|
|
35
37
|
|
|
36
|
-
it
|
|
38
|
+
it "handles symbols" do
|
|
37
39
|
expect(subject.exists?(:public)).to be_truthy
|
|
38
40
|
expect(subject.exists?(:other)).to be_falsey
|
|
39
41
|
end
|
|
40
42
|
end
|
|
41
43
|
|
|
42
|
-
describe
|
|
43
|
-
let(:string) {
|
|
44
|
+
describe ".from_string" do
|
|
45
|
+
let(:string) { "public write" }
|
|
44
46
|
|
|
45
47
|
subject { Scopes.from_string(string) }
|
|
46
48
|
|
|
47
49
|
it { expect(subject).to be_a(Scopes) }
|
|
48
50
|
|
|
49
|
-
describe
|
|
50
|
-
it
|
|
51
|
+
describe "#all" do
|
|
52
|
+
it "should be an array of the expected scopes" do
|
|
51
53
|
scopes_array = subject.all
|
|
52
54
|
expect(scopes_array.size).to eq(2)
|
|
53
|
-
expect(scopes_array).to include(
|
|
54
|
-
expect(scopes_array).to include(
|
|
55
|
+
expect(scopes_array).to include("public")
|
|
56
|
+
expect(scopes_array).to include("write")
|
|
55
57
|
end
|
|
56
58
|
end
|
|
57
59
|
end
|
|
58
60
|
|
|
59
|
-
describe
|
|
60
|
-
it
|
|
61
|
-
scopes = Scopes.from_string(
|
|
61
|
+
describe "#+" do
|
|
62
|
+
it "can add to another scope object" do
|
|
63
|
+
scopes = Scopes.from_string("public") + Scopes.from_string("admin")
|
|
62
64
|
expect(scopes.all).to eq(%w[public admin])
|
|
63
65
|
end
|
|
64
66
|
|
|
65
|
-
it
|
|
66
|
-
origin = Scopes.from_string(
|
|
67
|
-
expect(origin.to_s).to eq(
|
|
67
|
+
it "does not change the existing object" do
|
|
68
|
+
origin = Scopes.from_string("public")
|
|
69
|
+
expect(origin.to_s).to eq("public")
|
|
68
70
|
end
|
|
69
71
|
|
|
70
|
-
it
|
|
71
|
-
scopes = Scopes.from_string(
|
|
72
|
+
it "can add an array to a scope object" do
|
|
73
|
+
scopes = Scopes.from_string("public") + ["admin"]
|
|
72
74
|
expect(scopes.all).to eq(%w[public admin])
|
|
73
75
|
end
|
|
74
76
|
|
|
75
|
-
it
|
|
77
|
+
it "raises an error if cannot handle addition" do
|
|
76
78
|
expect do
|
|
77
|
-
Scopes.from_string(
|
|
79
|
+
Scopes.from_string("public") + "admin"
|
|
78
80
|
end.to raise_error(NoMethodError)
|
|
79
81
|
end
|
|
80
82
|
end
|
|
81
83
|
|
|
82
|
-
describe
|
|
83
|
-
it
|
|
84
|
-
scopes = Scopes.from_string(
|
|
84
|
+
describe "#&" do
|
|
85
|
+
it "can get intersection with another scope object" do
|
|
86
|
+
scopes = Scopes.from_string("public admin") & Scopes.from_string("write admin")
|
|
85
87
|
expect(scopes.all).to eq(%w[admin])
|
|
86
88
|
end
|
|
87
89
|
|
|
88
|
-
it
|
|
89
|
-
origin = Scopes.from_string(
|
|
90
|
-
origin & Scopes.from_string(
|
|
91
|
-
expect(origin.to_s).to eq(
|
|
90
|
+
it "does not change the existing object" do
|
|
91
|
+
origin = Scopes.from_string("public admin")
|
|
92
|
+
origin & Scopes.from_string("write admin")
|
|
93
|
+
expect(origin.to_s).to eq("public admin")
|
|
92
94
|
end
|
|
93
95
|
|
|
94
|
-
it
|
|
95
|
-
scopes = Scopes.from_string(
|
|
96
|
+
it "can get intersection with an array" do
|
|
97
|
+
scopes = Scopes.from_string("public admin") & %w[write admin]
|
|
96
98
|
expect(scopes.all).to eq(%w[admin])
|
|
97
99
|
end
|
|
98
100
|
end
|
|
99
101
|
|
|
100
|
-
describe
|
|
101
|
-
it
|
|
102
|
-
expect(Scopes.from_string(
|
|
102
|
+
describe "#==" do
|
|
103
|
+
it "is equal to another set of scopes" do
|
|
104
|
+
expect(Scopes.from_string("public")).to eq(Scopes.from_string("public"))
|
|
103
105
|
end
|
|
104
106
|
|
|
105
|
-
it
|
|
106
|
-
expect(Scopes.from_string(
|
|
107
|
+
it "is equal to another set of scopes with no particular order" do
|
|
108
|
+
expect(Scopes.from_string("public write")).to eq(Scopes.from_string("write public"))
|
|
107
109
|
end
|
|
108
110
|
|
|
109
|
-
it
|
|
110
|
-
expect(Scopes.from_string(
|
|
111
|
+
it "differs from another set of scopes when scopes are not the same" do
|
|
112
|
+
expect(Scopes.from_string("public write")).not_to eq(Scopes.from_string("write"))
|
|
111
113
|
end
|
|
112
114
|
|
|
113
115
|
it "does not raise an error when compared to a non-enumerable object" do
|
|
@@ -115,31 +117,31 @@ module Doorkeeper::OAuth
|
|
|
115
117
|
end
|
|
116
118
|
end
|
|
117
119
|
|
|
118
|
-
describe
|
|
119
|
-
subject { Scopes.from_string(
|
|
120
|
+
describe "#has_scopes?" do
|
|
121
|
+
subject { Scopes.from_string("public admin") }
|
|
120
122
|
|
|
121
|
-
it
|
|
122
|
-
expect(subject.has_scopes?(Scopes.from_string(
|
|
123
|
+
it "returns true when at least one scope is included" do
|
|
124
|
+
expect(subject.has_scopes?(Scopes.from_string("public"))).to be_truthy
|
|
123
125
|
end
|
|
124
126
|
|
|
125
|
-
it
|
|
126
|
-
expect(subject.has_scopes?(Scopes.from_string(
|
|
127
|
+
it "returns true when all scopes are included" do
|
|
128
|
+
expect(subject.has_scopes?(Scopes.from_string("public admin"))).to be_truthy
|
|
127
129
|
end
|
|
128
130
|
|
|
129
|
-
it
|
|
130
|
-
expect(subject.has_scopes?(Scopes.from_string(
|
|
131
|
+
it "is true if all scopes are included in any order" do
|
|
132
|
+
expect(subject.has_scopes?(Scopes.from_string("admin public"))).to be_truthy
|
|
131
133
|
end
|
|
132
134
|
|
|
133
|
-
it
|
|
134
|
-
expect(subject.has_scopes?(Scopes.from_string(
|
|
135
|
+
it "is false if no scopes are included" do
|
|
136
|
+
expect(subject.has_scopes?(Scopes.from_string("notexistent"))).to be_falsey
|
|
135
137
|
end
|
|
136
138
|
|
|
137
|
-
it
|
|
138
|
-
expect(subject.has_scopes?(Scopes.from_string(
|
|
139
|
+
it "returns false when any scope is not included" do
|
|
140
|
+
expect(subject.has_scopes?(Scopes.from_string("public nope"))).to be_falsey
|
|
139
141
|
end
|
|
140
142
|
|
|
141
|
-
it
|
|
142
|
-
expect(subject.has_scopes?(Scopes.from_string(
|
|
143
|
+
it "is false if no scopes are included even for existing ones" do
|
|
144
|
+
expect(subject.has_scopes?(Scopes.from_string("public admin notexistent"))).to be_falsey
|
|
143
145
|
end
|
|
144
146
|
end
|
|
145
147
|
end
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
2
4
|
|
|
3
5
|
module Doorkeeper::OAuth
|
|
4
6
|
describe TokenRequest do
|
|
@@ -10,9 +12,9 @@ module Doorkeeper::OAuth
|
|
|
10
12
|
double(
|
|
11
13
|
:pre_auth,
|
|
12
14
|
client: application,
|
|
13
|
-
redirect_uri:
|
|
15
|
+
redirect_uri: "http://tst.com/cb",
|
|
14
16
|
state: nil,
|
|
15
|
-
scopes: Scopes.from_string(
|
|
17
|
+
scopes: Scopes.from_string("public"),
|
|
16
18
|
error: nil,
|
|
17
19
|
authorizable?: true
|
|
18
20
|
)
|
|
@@ -26,69 +28,122 @@ module Doorkeeper::OAuth
|
|
|
26
28
|
TokenRequest.new(pre_auth, owner)
|
|
27
29
|
end
|
|
28
30
|
|
|
29
|
-
it
|
|
31
|
+
it "creates an access token" do
|
|
30
32
|
expect do
|
|
31
33
|
subject.authorize
|
|
32
34
|
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
|
33
35
|
end
|
|
34
36
|
|
|
35
|
-
it
|
|
37
|
+
it "returns a code response" do
|
|
36
38
|
expect(subject.authorize).to be_a(CodeResponse)
|
|
37
39
|
end
|
|
38
40
|
|
|
39
|
-
it
|
|
41
|
+
it "does not create token when not authorizable" do
|
|
40
42
|
allow(pre_auth).to receive(:authorizable?).and_return(false)
|
|
41
|
-
expect { subject.authorize }.not_to
|
|
43
|
+
expect { subject.authorize }.not_to(change { Doorkeeper::AccessToken.count })
|
|
42
44
|
end
|
|
43
45
|
|
|
44
|
-
it
|
|
46
|
+
it "returns a error response" do
|
|
45
47
|
allow(pre_auth).to receive(:authorizable?).and_return(false)
|
|
46
48
|
expect(subject.authorize).to be_a(ErrorResponse)
|
|
47
49
|
end
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
describe "with custom expiration" do
|
|
52
|
+
context "when proper TTL returned" do
|
|
53
|
+
before do
|
|
54
|
+
Doorkeeper.configure do
|
|
55
|
+
orm DOORKEEPER_ORM
|
|
56
|
+
custom_access_token_expires_in do |context|
|
|
57
|
+
context.grant_type == Doorkeeper::OAuth::IMPLICIT ? 1234 : nil
|
|
58
|
+
end
|
|
55
59
|
end
|
|
56
60
|
end
|
|
61
|
+
|
|
62
|
+
it "should use the custom ttl" do
|
|
63
|
+
subject.authorize
|
|
64
|
+
token = Doorkeeper::AccessToken.first
|
|
65
|
+
expect(token.expires_in).to eq(1234)
|
|
66
|
+
end
|
|
57
67
|
end
|
|
58
68
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
context "when nil TTL returned" do
|
|
70
|
+
before do
|
|
71
|
+
Doorkeeper.configure do
|
|
72
|
+
orm DOORKEEPER_ORM
|
|
73
|
+
access_token_expires_in 654
|
|
74
|
+
custom_access_token_expires_in do |_context|
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "should fallback to access_token_expires_in" do
|
|
81
|
+
subject.authorize
|
|
82
|
+
token = Doorkeeper::AccessToken.first
|
|
83
|
+
expect(token.expires_in).to eq(654)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
context "when infinite TTL returned" do
|
|
88
|
+
before do
|
|
89
|
+
Doorkeeper.configure do
|
|
90
|
+
orm DOORKEEPER_ORM
|
|
91
|
+
access_token_expires_in 654
|
|
92
|
+
custom_access_token_expires_in do |_context|
|
|
93
|
+
Float::INFINITY
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "should fallback to access_token_expires_in" do
|
|
99
|
+
subject.authorize
|
|
100
|
+
token = Doorkeeper::AccessToken.first
|
|
101
|
+
expect(token.expires_in).to be_nil
|
|
102
|
+
end
|
|
63
103
|
end
|
|
64
104
|
end
|
|
65
105
|
|
|
66
|
-
context
|
|
67
|
-
it
|
|
106
|
+
context "token reuse" do
|
|
107
|
+
it "creates a new token if there are no matching tokens" do
|
|
68
108
|
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
|
69
109
|
expect do
|
|
70
110
|
subject.authorize
|
|
71
111
|
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
|
72
112
|
end
|
|
73
113
|
|
|
74
|
-
it
|
|
114
|
+
it "creates a new token if scopes do not match" do
|
|
75
115
|
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
|
76
116
|
FactoryBot.create(:access_token, application_id: pre_auth.client.id,
|
|
77
|
-
|
|
117
|
+
resource_owner_id: owner.id, scopes: "")
|
|
78
118
|
expect do
|
|
79
119
|
subject.authorize
|
|
80
120
|
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
|
81
121
|
end
|
|
82
122
|
|
|
83
|
-
it
|
|
123
|
+
it "skips token creation if there is a matching one reusable" do
|
|
124
|
+
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
|
125
|
+
allow(application.scopes).to receive(:has_scopes?).and_return(true)
|
|
126
|
+
allow(application.scopes).to receive(:all?).and_return(true)
|
|
127
|
+
|
|
128
|
+
FactoryBot.create(:access_token, application_id: pre_auth.client.id,
|
|
129
|
+
resource_owner_id: owner.id, scopes: "public")
|
|
130
|
+
|
|
131
|
+
expect { subject.authorize }.not_to(change { Doorkeeper::AccessToken.count })
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "creates new token if there is a matching one but non reusable" do
|
|
84
135
|
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
|
85
136
|
allow(application.scopes).to receive(:has_scopes?).and_return(true)
|
|
86
137
|
allow(application.scopes).to receive(:all?).and_return(true)
|
|
87
138
|
|
|
88
139
|
FactoryBot.create(:access_token, application_id: pre_auth.client.id,
|
|
89
|
-
|
|
140
|
+
resource_owner_id: owner.id, scopes: "public")
|
|
141
|
+
|
|
142
|
+
allow_any_instance_of(Doorkeeper::AccessToken).to receive(:reusable?).and_return(false)
|
|
90
143
|
|
|
91
|
-
expect
|
|
144
|
+
expect do
|
|
145
|
+
subject.authorize
|
|
146
|
+
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
|
92
147
|
end
|
|
93
148
|
end
|
|
94
149
|
end
|