doorkeeper 5.1.0 → 5.3.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/Appraisals +3 -3
- data/CHANGELOG.md +881 -0
- data/CONTRIBUTING.md +11 -9
- data/Dangerfile +2 -2
- data/Dockerfile +29 -0
- data/Gemfile +4 -3
- data/NEWS.md +1 -814
- data/README.md +11 -3
- data/RELEASING.md +6 -5
- data/app/controllers/doorkeeper/application_controller.rb +2 -2
- data/app/controllers/doorkeeper/application_metal_controller.rb +3 -2
- data/app/controllers/doorkeeper/applications_controller.rb +1 -0
- data/app/controllers/doorkeeper/authorizations_controller.rb +16 -9
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +2 -2
- data/app/controllers/doorkeeper/tokens_controller.rb +32 -9
- data/app/views/doorkeeper/applications/_form.html.erb +0 -6
- data/app/views/doorkeeper/applications/show.html.erb +1 -1
- data/config/locales/en.yml +8 -2
- data/doorkeeper.gemspec +9 -1
- data/gemfiles/rails_5_0.gemfile +3 -2
- data/gemfiles/rails_5_1.gemfile +3 -2
- data/gemfiles/rails_5_2.gemfile +3 -2
- data/gemfiles/rails_6_0.gemfile +4 -3
- data/gemfiles/rails_master.gemfile +3 -2
- data/lib/doorkeeper/config/option.rb +13 -7
- data/lib/doorkeeper/config.rb +157 -42
- data/lib/doorkeeper/errors.rb +13 -18
- data/lib/doorkeeper/grape/helpers.rb +6 -2
- data/lib/doorkeeper/helpers/controller.rb +28 -7
- data/lib/doorkeeper/models/access_grant_mixin.rb +7 -6
- data/lib/doorkeeper/models/access_token_mixin.rb +99 -20
- data/lib/doorkeeper/models/application_mixin.rb +3 -3
- data/lib/doorkeeper/models/concerns/ownership.rb +1 -1
- data/lib/doorkeeper/models/concerns/reusable.rb +1 -1
- data/lib/doorkeeper/models/concerns/revocable.rb +0 -27
- data/lib/doorkeeper/oauth/authorization/code.rb +12 -14
- data/lib/doorkeeper/oauth/authorization/token.rb +10 -7
- data/lib/doorkeeper/oauth/authorization_code_request.rb +31 -15
- data/lib/doorkeeper/oauth/base_request.rb +10 -4
- data/lib/doorkeeper/oauth/client.rb +7 -8
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +23 -2
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +7 -7
- data/lib/doorkeeper/oauth/client_credentials/{validation.rb → validator.rb} +10 -2
- data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -1
- data/lib/doorkeeper/oauth/code_request.rb +5 -11
- data/lib/doorkeeper/oauth/code_response.rb +4 -4
- data/lib/doorkeeper/oauth/error.rb +1 -1
- data/lib/doorkeeper/oauth/error_response.rb +6 -6
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +7 -5
- data/lib/doorkeeper/oauth/helpers/unique_token.rb +8 -5
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +19 -5
- data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
- data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -2
- data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -4
- data/lib/doorkeeper/oauth/pre_authorization.rb +76 -38
- data/lib/doorkeeper/oauth/refresh_token_request.rb +17 -14
- data/lib/doorkeeper/oauth/token.rb +2 -2
- data/lib/doorkeeper/oauth/token_introspection.rb +26 -16
- data/lib/doorkeeper/oauth/token_request.rb +4 -18
- data/lib/doorkeeper/orm/active_record/access_grant.rb +4 -43
- data/lib/doorkeeper/orm/active_record/access_token.rb +4 -35
- data/lib/doorkeeper/orm/active_record/application.rb +5 -83
- data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +53 -0
- data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +47 -0
- data/lib/doorkeeper/orm/active_record/mixins/application.rb +128 -0
- data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +61 -0
- data/lib/doorkeeper/orm/active_record.rb +21 -6
- data/lib/doorkeeper/rails/helpers.rb +4 -4
- data/lib/doorkeeper/rails/routes.rb +5 -7
- data/lib/doorkeeper/rake/db.rake +3 -3
- data/lib/doorkeeper/request/authorization_code.rb +5 -3
- data/lib/doorkeeper/request/client_credentials.rb +2 -2
- data/lib/doorkeeper/request/password.rb +2 -2
- data/lib/doorkeeper/request/refresh_token.rb +3 -3
- data/lib/doorkeeper/request.rb +7 -12
- data/lib/doorkeeper/server.rb +3 -7
- data/lib/doorkeeper/stale_records_cleaner.rb +6 -2
- data/lib/doorkeeper/version.rb +1 -1
- data/lib/doorkeeper.rb +6 -3
- data/lib/generators/doorkeeper/application_owner_generator.rb +1 -1
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +1 -1
- data/lib/generators/doorkeeper/migration_generator.rb +1 -1
- data/lib/generators/doorkeeper/pkce_generator.rb +1 -1
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +7 -7
- data/lib/generators/doorkeeper/templates/initializer.rb +149 -33
- data/lib/generators/doorkeeper/templates/migration.rb.erb +4 -1
- data/spec/controllers/application_metal_controller_spec.rb +1 -1
- data/spec/controllers/applications_controller_spec.rb +95 -1
- data/spec/controllers/authorizations_controller_spec.rb +156 -75
- data/spec/controllers/protected_resources_controller_spec.rb +28 -20
- data/spec/controllers/token_info_controller_spec.rb +1 -1
- data/spec/controllers/tokens_controller_spec.rb +206 -38
- data/spec/dummy/config/application.rb +3 -1
- data/spec/dummy/config/initializers/doorkeeper.rb +54 -9
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -3
- data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +1 -1
- data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +1 -1
- data/spec/generators/install_generator_spec.rb +1 -1
- data/spec/generators/previous_refresh_token_generator_spec.rb +2 -2
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
- data/spec/lib/config_spec.rb +104 -7
- data/spec/lib/doorkeeper_spec.rb +1 -1
- data/spec/lib/models/revocable_spec.rb +3 -3
- data/spec/lib/oauth/authorization_code_request_spec.rb +129 -115
- data/spec/lib/oauth/base_request_spec.rb +154 -135
- data/spec/lib/oauth/base_response_spec.rb +27 -29
- data/spec/lib/oauth/client/credentials_spec.rb +1 -1
- data/spec/lib/oauth/client_credentials/creator_spec.rb +43 -3
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +12 -12
- data/spec/lib/oauth/client_credentials/validation_spec.rb +4 -4
- data/spec/lib/oauth/client_credentials_integration_spec.rb +16 -18
- data/spec/lib/oauth/client_credentials_request_spec.rb +78 -80
- data/spec/lib/oauth/client_spec.rb +26 -26
- data/spec/lib/oauth/code_request_spec.rb +34 -35
- data/spec/lib/oauth/code_response_spec.rb +21 -25
- data/spec/lib/oauth/error_response_spec.rb +42 -44
- data/spec/lib/oauth/error_spec.rb +12 -14
- data/spec/lib/oauth/forbidden_token_response_spec.rb +11 -13
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +30 -18
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +17 -2
- data/spec/lib/oauth/invalid_request_response_spec.rb +73 -0
- data/spec/lib/oauth/invalid_token_response_spec.rb +32 -34
- data/spec/lib/oauth/password_access_token_request_spec.rb +145 -147
- data/spec/lib/oauth/pre_authorization_spec.rb +161 -153
- data/spec/lib/oauth/refresh_token_request_spec.rb +138 -138
- data/spec/lib/oauth/scopes_spec.rb +104 -106
- data/spec/lib/oauth/token_request_spec.rb +117 -110
- data/spec/lib/oauth/token_response_spec.rb +71 -73
- data/spec/lib/oauth/token_spec.rb +121 -123
- data/spec/lib/server_spec.rb +0 -12
- data/spec/models/doorkeeper/access_grant_spec.rb +21 -4
- data/spec/models/doorkeeper/access_token_spec.rb +42 -11
- data/spec/models/doorkeeper/application_spec.rb +114 -77
- data/spec/requests/applications/applications_request_spec.rb +1 -1
- data/spec/requests/endpoints/authorization_spec.rb +26 -8
- data/spec/requests/endpoints/token_spec.rb +1 -1
- data/spec/requests/flows/authorization_code_errors_spec.rb +1 -0
- data/spec/requests/flows/authorization_code_spec.rb +123 -45
- data/spec/requests/flows/client_credentials_spec.rb +38 -0
- data/spec/requests/flows/implicit_grant_errors_spec.rb +22 -10
- data/spec/requests/flows/implicit_grant_spec.rb +9 -8
- data/spec/requests/flows/password_spec.rb +67 -10
- data/spec/requests/flows/refresh_token_spec.rb +20 -20
- data/spec/requests/flows/revoke_token_spec.rb +37 -23
- data/spec/spec_helper.rb +1 -4
- data/spec/support/doorkeeper_rspec.rb +1 -1
- data/spec/support/helpers/request_spec_helper.rb +14 -2
- data/spec/support/shared/controllers_shared_context.rb +33 -23
- data/spec/validators/redirect_uri_validator_spec.rb +41 -16
- metadata +19 -16
- data/.coveralls.yml +0 -1
- data/.github/ISSUE_TEMPLATE.md +0 -25
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -17
- data/.gitignore +0 -20
- data/.gitlab-ci.yml +0 -16
- data/.hound.yml +0 -3
- data/.rspec +0 -1
- data/.rubocop.yml +0 -50
- data/.travis.yml +0 -35
- data/app/validators/redirect_uri_validator.rb +0 -50
- data/spec/support/http_method_shim.rb +0 -29
|
@@ -7,7 +7,7 @@ module Doorkeeper
|
|
|
7
7
|
# @see https://tools.ietf.org/html/rfc7662
|
|
8
8
|
class TokenIntrospection
|
|
9
9
|
attr_reader :server, :token
|
|
10
|
-
attr_reader :error
|
|
10
|
+
attr_reader :error, :invalid_request_reason
|
|
11
11
|
|
|
12
12
|
def initialize(server, token)
|
|
13
13
|
@server = server
|
|
@@ -25,6 +25,8 @@ module Doorkeeper
|
|
|
25
25
|
|
|
26
26
|
if @error == :invalid_token
|
|
27
27
|
OAuth::InvalidTokenResponse.from_access_token(authorized_token)
|
|
28
|
+
elsif @error == :invalid_request
|
|
29
|
+
OAuth::InvalidRequestResponse.from_request(self)
|
|
28
30
|
else
|
|
29
31
|
OAuth::ErrorResponse.new(name: @error)
|
|
30
32
|
end
|
|
@@ -67,9 +69,10 @@ module Doorkeeper
|
|
|
67
69
|
# HTTP 401 code as described in Section 3 of OAuth 2.0 Bearer Token
|
|
68
70
|
# Usage [RFC6750].
|
|
69
71
|
#
|
|
70
|
-
@error = :invalid_token
|
|
72
|
+
@error = :invalid_token unless valid_authorized_token?
|
|
71
73
|
else
|
|
72
74
|
@error = :invalid_request
|
|
75
|
+
@invalid_request_reason = :request_not_authorized
|
|
73
76
|
end
|
|
74
77
|
end
|
|
75
78
|
|
|
@@ -80,8 +83,7 @@ module Doorkeeper
|
|
|
80
83
|
|
|
81
84
|
# Bearer Token Authentication
|
|
82
85
|
def authorized_token
|
|
83
|
-
@authorized_token ||=
|
|
84
|
-
OAuth::Token.authenticate(server.context.request, :from_bearer_authorization)
|
|
86
|
+
@authorized_token ||= Doorkeeper.authenticate(server.context.request)
|
|
85
87
|
end
|
|
86
88
|
|
|
87
89
|
# 2.2. Introspection Response
|
|
@@ -92,7 +94,7 @@ module Doorkeeper
|
|
|
92
94
|
client_id: @token.try(:application).try(:uid),
|
|
93
95
|
token_type: @token.token_type,
|
|
94
96
|
exp: @token.expires_at.to_i,
|
|
95
|
-
iat: @token.created_at.to_i
|
|
97
|
+
iat: @token.created_at.to_i,
|
|
96
98
|
)
|
|
97
99
|
end
|
|
98
100
|
|
|
@@ -150,7 +152,7 @@ module Doorkeeper
|
|
|
150
152
|
#
|
|
151
153
|
def active?
|
|
152
154
|
if authorized_client
|
|
153
|
-
valid_token? &&
|
|
155
|
+
valid_token? && token_introspection_allowed?(auth_client: authorized_client.application)
|
|
154
156
|
else
|
|
155
157
|
valid_token?
|
|
156
158
|
end
|
|
@@ -161,19 +163,27 @@ module Doorkeeper
|
|
|
161
163
|
@token&.accessible?
|
|
162
164
|
end
|
|
163
165
|
|
|
166
|
+
def valid_authorized_token?
|
|
167
|
+
!authorized_token_matches_introspected? &&
|
|
168
|
+
authorized_token.accessible? &&
|
|
169
|
+
token_introspection_allowed?(auth_token: authorized_token)
|
|
170
|
+
end
|
|
171
|
+
|
|
164
172
|
# RFC7662 Section 2.1
|
|
165
173
|
def authorized_token_matches_introspected?
|
|
166
174
|
authorized_token.token == @token&.token
|
|
167
175
|
end
|
|
168
176
|
|
|
169
|
-
#
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
# Config constraints for introspection in Doorkeeper.config.allow_token_introspection
|
|
178
|
+
def token_introspection_allowed?(auth_client: nil, auth_token: nil)
|
|
179
|
+
allow_introspection = Doorkeeper.config.allow_token_introspection
|
|
180
|
+
return allow_introspection unless allow_introspection.respond_to?(:call)
|
|
181
|
+
|
|
182
|
+
allow_introspection.call(
|
|
183
|
+
@token,
|
|
184
|
+
auth_client,
|
|
185
|
+
auth_token,
|
|
186
|
+
)
|
|
177
187
|
end
|
|
178
188
|
|
|
179
189
|
# Allows to customize introspection response.
|
|
@@ -183,9 +193,9 @@ module Doorkeeper
|
|
|
183
193
|
# @see https://tools.ietf.org/html/rfc7662#section-2.2
|
|
184
194
|
#
|
|
185
195
|
def customize_response(response)
|
|
186
|
-
customized_response = Doorkeeper.
|
|
196
|
+
customized_response = Doorkeeper.config.custom_introspection_response.call(
|
|
187
197
|
token,
|
|
188
|
-
server.context
|
|
198
|
+
server.context,
|
|
189
199
|
)
|
|
190
200
|
return response if customized_response.blank?
|
|
191
201
|
|
|
@@ -11,28 +11,14 @@ module Doorkeeper
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def authorize
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@response = CodeResponse.new pre_auth,
|
|
18
|
-
auth,
|
|
19
|
-
response_on_fragment: true
|
|
20
|
-
else
|
|
21
|
-
@response = error_response
|
|
22
|
-
end
|
|
14
|
+
auth = Authorization::Token.new(pre_auth, resource_owner)
|
|
15
|
+
auth.issue_token
|
|
16
|
+
CodeResponse.new(pre_auth, auth, response_on_fragment: true)
|
|
23
17
|
end
|
|
24
18
|
|
|
25
19
|
def deny
|
|
26
20
|
pre_auth.error = :access_denied
|
|
27
|
-
error_response
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
def error_response
|
|
33
|
-
ErrorResponse.from_request pre_auth,
|
|
34
|
-
redirect_uri: pre_auth.redirect_uri,
|
|
35
|
-
response_on_fragment: true
|
|
21
|
+
pre_auth.error_response
|
|
36
22
|
end
|
|
37
23
|
end
|
|
38
24
|
end
|
|
@@ -1,48 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
class AccessGrant < ActiveRecord::Base
|
|
5
|
-
self.table_name = "#{table_name_prefix}oauth_access_grants#{table_name_suffix}"
|
|
6
|
-
|
|
7
|
-
include AccessGrantMixin
|
|
8
|
-
|
|
9
|
-
belongs_to :application, class_name: "Doorkeeper::Application",
|
|
10
|
-
optional: true, inverse_of: :access_grants
|
|
11
|
-
|
|
12
|
-
validates :resource_owner_id,
|
|
13
|
-
:application_id,
|
|
14
|
-
:token,
|
|
15
|
-
:expires_in,
|
|
16
|
-
:redirect_uri,
|
|
17
|
-
presence: true
|
|
18
|
-
|
|
19
|
-
validates :token, uniqueness: true
|
|
3
|
+
require "doorkeeper/orm/active_record/mixins/access_grant"
|
|
20
4
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
# The stored refresh_token may be mapped and not available in cleartext.
|
|
25
|
-
#
|
|
26
|
-
# Some strategies allow restoring stored secrets (e.g. symmetric encryption)
|
|
27
|
-
# while hashing strategies do not, so you cannot rely on this value
|
|
28
|
-
# returning a present value for persisted tokens.
|
|
29
|
-
def plaintext_token
|
|
30
|
-
if secret_strategy.allows_restoring_secrets?
|
|
31
|
-
secret_strategy.restore_secret(self, :token)
|
|
32
|
-
else
|
|
33
|
-
@raw_token
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
private
|
|
38
|
-
|
|
39
|
-
# Generates token value with UniqueToken class.
|
|
40
|
-
#
|
|
41
|
-
# @return [String] token value
|
|
42
|
-
#
|
|
43
|
-
def generate_token
|
|
44
|
-
@raw_token = UniqueToken.generate
|
|
45
|
-
secret_strategy.store_secret(self, :token, @raw_token)
|
|
46
|
-
end
|
|
5
|
+
module Doorkeeper
|
|
6
|
+
class AccessGrant < ::ActiveRecord::Base
|
|
7
|
+
include Doorkeeper::Orm::ActiveRecord::Mixins::AccessGrant
|
|
47
8
|
end
|
|
48
9
|
end
|
|
@@ -1,40 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
class AccessToken < ActiveRecord::Base
|
|
5
|
-
self.table_name = "#{table_name_prefix}oauth_access_tokens#{table_name_suffix}"
|
|
6
|
-
|
|
7
|
-
include AccessTokenMixin
|
|
8
|
-
|
|
9
|
-
belongs_to :application, class_name: "Doorkeeper::Application",
|
|
10
|
-
inverse_of: :access_tokens, optional: true
|
|
11
|
-
|
|
12
|
-
validates :token, presence: true, uniqueness: true
|
|
13
|
-
validates :refresh_token, uniqueness: true, if: :use_refresh_token?
|
|
3
|
+
require "doorkeeper/orm/active_record/mixins/access_token"
|
|
14
4
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
before_validation :generate_token, on: :create
|
|
20
|
-
before_validation :generate_refresh_token,
|
|
21
|
-
on: :create, if: :use_refresh_token?
|
|
22
|
-
|
|
23
|
-
# Searches for not revoked Access Tokens associated with the
|
|
24
|
-
# specific Resource Owner.
|
|
25
|
-
#
|
|
26
|
-
# @param resource_owner [ActiveRecord::Base]
|
|
27
|
-
# Resource Owner model instance
|
|
28
|
-
#
|
|
29
|
-
# @return [ActiveRecord::Relation]
|
|
30
|
-
# active Access Tokens for Resource Owner
|
|
31
|
-
#
|
|
32
|
-
def self.active_for(resource_owner)
|
|
33
|
-
where(resource_owner_id: resource_owner.id, revoked_at: nil)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def self.refresh_token_revoked_on_use?
|
|
37
|
-
column_names.include?("previous_refresh_token")
|
|
38
|
-
end
|
|
5
|
+
module Doorkeeper
|
|
6
|
+
class AccessToken < ::ActiveRecord::Base
|
|
7
|
+
include Doorkeeper::Orm::ActiveRecord::Mixins::AccessToken
|
|
39
8
|
end
|
|
40
9
|
end
|
|
@@ -1,88 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
self.table_name = "#{table_name_prefix}oauth_applications#{table_name_suffix}"
|
|
6
|
-
|
|
7
|
-
include ApplicationMixin
|
|
8
|
-
|
|
9
|
-
has_many :access_grants, dependent: :delete_all, class_name: "Doorkeeper::AccessGrant"
|
|
10
|
-
has_many :access_tokens, dependent: :delete_all, class_name: "Doorkeeper::AccessToken"
|
|
11
|
-
|
|
12
|
-
validates :name, :secret, :uid, presence: true
|
|
13
|
-
validates :uid, uniqueness: true
|
|
14
|
-
validates :redirect_uri, redirect_uri: true
|
|
15
|
-
validates :confidential, inclusion: { in: [true, false] }
|
|
16
|
-
|
|
17
|
-
validate :scopes_match_configured, if: :enforce_scopes?
|
|
18
|
-
|
|
19
|
-
before_validation :generate_uid, :generate_secret, on: :create
|
|
20
|
-
|
|
21
|
-
has_many :authorized_tokens, -> { where(revoked_at: nil) }, class_name: "AccessToken"
|
|
22
|
-
has_many :authorized_applications, through: :authorized_tokens, source: :application
|
|
23
|
-
|
|
24
|
-
# Returns Applications associated with active (not revoked) Access Tokens
|
|
25
|
-
# that are owned by the specific Resource Owner.
|
|
26
|
-
#
|
|
27
|
-
# @param resource_owner [ActiveRecord::Base]
|
|
28
|
-
# Resource Owner model instance
|
|
29
|
-
#
|
|
30
|
-
# @return [ActiveRecord::Relation]
|
|
31
|
-
# Applications authorized for the Resource Owner
|
|
32
|
-
#
|
|
33
|
-
def self.authorized_for(resource_owner)
|
|
34
|
-
resource_access_tokens = AccessToken.active_for(resource_owner)
|
|
35
|
-
where(id: resource_access_tokens.select(:application_id).distinct)
|
|
36
|
-
end
|
|
3
|
+
require "doorkeeper/orm/active_record/redirect_uri_validator"
|
|
4
|
+
require "doorkeeper/orm/active_record/mixins/application"
|
|
37
5
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# @param resource_owner [ActiveRecord::Base]
|
|
42
|
-
# instance of the Resource Owner model
|
|
43
|
-
#
|
|
44
|
-
def self.revoke_tokens_and_grants_for(id, resource_owner)
|
|
45
|
-
AccessToken.revoke_all_for(id, resource_owner)
|
|
46
|
-
AccessGrant.revoke_all_for(id, resource_owner)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# We keep a volatile copy of the raw secret for initial communication
|
|
50
|
-
# The stored refresh_token may be mapped and not available in cleartext.
|
|
51
|
-
#
|
|
52
|
-
# Some strategies allow restoring stored secrets (e.g. symmetric encryption)
|
|
53
|
-
# while hashing strategies do not, so you cannot rely on this value
|
|
54
|
-
# returning a present value for persisted tokens.
|
|
55
|
-
def plaintext_secret
|
|
56
|
-
if secret_strategy.allows_restoring_secrets?
|
|
57
|
-
secret_strategy.restore_secret(self, :secret)
|
|
58
|
-
else
|
|
59
|
-
@raw_secret
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
private
|
|
64
|
-
|
|
65
|
-
def generate_uid
|
|
66
|
-
self.uid = UniqueToken.generate if uid.blank?
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def generate_secret
|
|
70
|
-
return unless secret.blank?
|
|
71
|
-
|
|
72
|
-
@raw_secret = UniqueToken.generate
|
|
73
|
-
secret_strategy.store_secret(self, :secret, @raw_secret)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def scopes_match_configured
|
|
77
|
-
if scopes.present? &&
|
|
78
|
-
!ScopeChecker.valid?(scope_str: scopes.to_s,
|
|
79
|
-
server_scopes: Doorkeeper.configuration.scopes)
|
|
80
|
-
errors.add(:scopes, :not_match_configured)
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def enforce_scopes?
|
|
85
|
-
Doorkeeper.configuration.enforce_configured_scopes?
|
|
86
|
-
end
|
|
6
|
+
module Doorkeeper
|
|
7
|
+
class Application < ::ActiveRecord::Base
|
|
8
|
+
include ::Doorkeeper::Orm::ActiveRecord::Mixins::Application
|
|
87
9
|
end
|
|
88
10
|
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doorkeeper::Orm::ActiveRecord::Mixins
|
|
4
|
+
module AccessGrant
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
self.table_name = "#{table_name_prefix}oauth_access_grants#{table_name_suffix}"
|
|
9
|
+
|
|
10
|
+
include ::Doorkeeper::AccessGrantMixin
|
|
11
|
+
|
|
12
|
+
belongs_to :application, class_name: Doorkeeper.config.application_class,
|
|
13
|
+
optional: true,
|
|
14
|
+
inverse_of: :access_grants
|
|
15
|
+
|
|
16
|
+
validates :resource_owner_id,
|
|
17
|
+
:application_id,
|
|
18
|
+
:token,
|
|
19
|
+
:expires_in,
|
|
20
|
+
:redirect_uri,
|
|
21
|
+
presence: true
|
|
22
|
+
|
|
23
|
+
validates :token, uniqueness: { case_sensitive: true }
|
|
24
|
+
|
|
25
|
+
before_validation :generate_token, on: :create
|
|
26
|
+
|
|
27
|
+
# We keep a volatile copy of the raw token for initial communication
|
|
28
|
+
# The stored refresh_token may be mapped and not available in cleartext.
|
|
29
|
+
#
|
|
30
|
+
# Some strategies allow restoring stored secrets (e.g. symmetric encryption)
|
|
31
|
+
# while hashing strategies do not, so you cannot rely on this value
|
|
32
|
+
# returning a present value for persisted tokens.
|
|
33
|
+
def plaintext_token
|
|
34
|
+
if secret_strategy.allows_restoring_secrets?
|
|
35
|
+
secret_strategy.restore_secret(self, :token)
|
|
36
|
+
else
|
|
37
|
+
@raw_token
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
# Generates token value with UniqueToken class.
|
|
44
|
+
#
|
|
45
|
+
# @return [String] token value
|
|
46
|
+
#
|
|
47
|
+
def generate_token
|
|
48
|
+
@raw_token = Doorkeeper::OAuth::Helpers::UniqueToken.generate
|
|
49
|
+
secret_strategy.store_secret(self, :token, @raw_token)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doorkeeper::Orm::ActiveRecord::Mixins
|
|
4
|
+
module AccessToken
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
self.table_name = "#{table_name_prefix}oauth_access_tokens#{table_name_suffix}"
|
|
9
|
+
|
|
10
|
+
include ::Doorkeeper::AccessTokenMixin
|
|
11
|
+
|
|
12
|
+
belongs_to :application, class_name: Doorkeeper.config.application_class,
|
|
13
|
+
inverse_of: :access_tokens,
|
|
14
|
+
optional: true
|
|
15
|
+
|
|
16
|
+
validates :token, presence: true, uniqueness: { case_sensitive: true }
|
|
17
|
+
validates :refresh_token, uniqueness: { case_sensitive: true }, if: :use_refresh_token?
|
|
18
|
+
|
|
19
|
+
# @attr_writer [Boolean, nil] use_refresh_token
|
|
20
|
+
# indicates the possibility of using refresh token
|
|
21
|
+
attr_writer :use_refresh_token
|
|
22
|
+
|
|
23
|
+
before_validation :generate_token, on: :create
|
|
24
|
+
before_validation :generate_refresh_token,
|
|
25
|
+
on: :create, if: :use_refresh_token?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class_methods do
|
|
29
|
+
# Searches for not revoked Access Tokens associated with the
|
|
30
|
+
# specific Resource Owner.
|
|
31
|
+
#
|
|
32
|
+
# @param resource_owner [ActiveRecord::Base]
|
|
33
|
+
# Resource Owner model instance
|
|
34
|
+
#
|
|
35
|
+
# @return [ActiveRecord::Relation]
|
|
36
|
+
# active Access Tokens for Resource Owner
|
|
37
|
+
#
|
|
38
|
+
def active_for(resource_owner)
|
|
39
|
+
where(resource_owner_id: resource_owner.id, revoked_at: nil)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def refresh_token_revoked_on_use?
|
|
43
|
+
column_names.include?("previous_refresh_token")
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doorkeeper::Orm::ActiveRecord::Mixins
|
|
4
|
+
module Application
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
self.table_name = "#{table_name_prefix}oauth_applications#{table_name_suffix}"
|
|
9
|
+
|
|
10
|
+
include ::Doorkeeper::ApplicationMixin
|
|
11
|
+
|
|
12
|
+
has_many :access_grants,
|
|
13
|
+
foreign_key: :application_id,
|
|
14
|
+
dependent: :delete_all,
|
|
15
|
+
class_name: Doorkeeper.config.access_grant_class
|
|
16
|
+
|
|
17
|
+
has_many :access_tokens,
|
|
18
|
+
foreign_key: :application_id,
|
|
19
|
+
dependent: :delete_all,
|
|
20
|
+
class_name: Doorkeeper.config.access_token_class
|
|
21
|
+
|
|
22
|
+
validates :name, :secret, :uid, presence: true
|
|
23
|
+
validates :uid, uniqueness: { case_sensitive: true }
|
|
24
|
+
validates :redirect_uri, "doorkeeper/redirect_uri": true
|
|
25
|
+
validates :confidential, inclusion: { in: [true, false] }
|
|
26
|
+
|
|
27
|
+
validate :scopes_match_configured, if: :enforce_scopes?
|
|
28
|
+
|
|
29
|
+
before_validation :generate_uid, :generate_secret, on: :create
|
|
30
|
+
|
|
31
|
+
has_many :authorized_tokens,
|
|
32
|
+
-> { where(revoked_at: nil) },
|
|
33
|
+
foreign_key: :application_id,
|
|
34
|
+
class_name: Doorkeeper.config.access_token_class
|
|
35
|
+
|
|
36
|
+
has_many :authorized_applications,
|
|
37
|
+
through: :authorized_tokens,
|
|
38
|
+
source: :application
|
|
39
|
+
|
|
40
|
+
# Generates a new secret for this application, intended to be used
|
|
41
|
+
# for rotating the secret or in case of compromise.
|
|
42
|
+
#
|
|
43
|
+
# @return [String] new transformed secret value
|
|
44
|
+
#
|
|
45
|
+
def renew_secret
|
|
46
|
+
@raw_secret = Doorkeeper::OAuth::Helpers::UniqueToken.generate
|
|
47
|
+
secret_strategy.store_secret(self, :secret, @raw_secret)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# We keep a volatile copy of the raw secret for initial communication
|
|
51
|
+
# The stored refresh_token may be mapped and not available in cleartext.
|
|
52
|
+
#
|
|
53
|
+
# Some strategies allow restoring stored secrets (e.g. symmetric encryption)
|
|
54
|
+
# while hashing strategies do not, so you cannot rely on this value
|
|
55
|
+
# returning a present value for persisted tokens.
|
|
56
|
+
def plaintext_secret
|
|
57
|
+
if secret_strategy.allows_restoring_secrets?
|
|
58
|
+
secret_strategy.restore_secret(self, :secret)
|
|
59
|
+
else
|
|
60
|
+
@raw_secret
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# This is the right way how we want to override ActiveRecord #to_json
|
|
65
|
+
#
|
|
66
|
+
# @return [String] entity attributes as JSON
|
|
67
|
+
#
|
|
68
|
+
def as_json(options = {})
|
|
69
|
+
hash = super
|
|
70
|
+
|
|
71
|
+
hash["secret"] = plaintext_secret if hash.key?("secret")
|
|
72
|
+
hash
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def generate_uid
|
|
78
|
+
self.uid = Doorkeeper::OAuth::Helpers::UniqueToken.generate if uid.blank?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def generate_secret
|
|
82
|
+
return unless secret.blank?
|
|
83
|
+
|
|
84
|
+
renew_secret
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def scopes_match_configured
|
|
88
|
+
if scopes.present? && !Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
|
|
89
|
+
scope_str: scopes.to_s,
|
|
90
|
+
server_scopes: Doorkeeper.config.scopes,
|
|
91
|
+
)
|
|
92
|
+
errors.add(:scopes, :not_match_configured)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def enforce_scopes?
|
|
97
|
+
Doorkeeper.config.enforce_configured_scopes?
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
class_methods do
|
|
102
|
+
# Returns Applications associated with active (not revoked) Access Tokens
|
|
103
|
+
# that are owned by the specific Resource Owner.
|
|
104
|
+
#
|
|
105
|
+
# @param resource_owner [ActiveRecord::Base]
|
|
106
|
+
# Resource Owner model instance
|
|
107
|
+
#
|
|
108
|
+
# @return [ActiveRecord::Relation]
|
|
109
|
+
# Applications authorized for the Resource Owner
|
|
110
|
+
#
|
|
111
|
+
def authorized_for(resource_owner)
|
|
112
|
+
resource_access_tokens = Doorkeeper.config.access_token_model.active_for(resource_owner)
|
|
113
|
+
where(id: resource_access_tokens.select(:application_id).distinct)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Revokes AccessToken and AccessGrant records that have not been revoked and
|
|
117
|
+
# associated with the specific Application and Resource Owner.
|
|
118
|
+
#
|
|
119
|
+
# @param resource_owner [ActiveRecord::Base]
|
|
120
|
+
# instance of the Resource Owner model
|
|
121
|
+
#
|
|
122
|
+
def revoke_tokens_and_grants_for(id, resource_owner)
|
|
123
|
+
Doorkeeper.config.access_token_model.revoke_all_for(id, resource_owner)
|
|
124
|
+
Doorkeeper.config.access_grant_model.revoke_all_for(id, resource_owner)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module Doorkeeper
|
|
6
|
+
# ActiveModel validator for redirect URI validation in according
|
|
7
|
+
# to OAuth standards and Doorkeeper configuration.
|
|
8
|
+
class RedirectUriValidator < ActiveModel::EachValidator
|
|
9
|
+
def validate_each(record, attribute, value)
|
|
10
|
+
if value.blank?
|
|
11
|
+
return if Doorkeeper.config.allow_blank_redirect_uri?(record)
|
|
12
|
+
|
|
13
|
+
record.errors.add(attribute, :blank)
|
|
14
|
+
else
|
|
15
|
+
value.split.each do |val|
|
|
16
|
+
next if oob_redirect_uri?(val)
|
|
17
|
+
|
|
18
|
+
uri = ::URI.parse(val)
|
|
19
|
+
record.errors.add(attribute, :forbidden_uri) if forbidden_uri?(uri)
|
|
20
|
+
record.errors.add(attribute, :fragment_present) unless uri.fragment.nil?
|
|
21
|
+
record.errors.add(attribute, :unspecified_scheme) if unspecified_scheme?(uri)
|
|
22
|
+
record.errors.add(attribute, :relative_uri) if relative_uri?(uri)
|
|
23
|
+
record.errors.add(attribute, :secured_uri) if invalid_ssl_uri?(uri)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
rescue URI::InvalidURIError
|
|
27
|
+
record.errors.add(attribute, :invalid_uri)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def oob_redirect_uri?(uri)
|
|
33
|
+
Doorkeeper::OAuth::NonStandard::IETF_WG_OAUTH2_OOB_METHODS.include?(uri)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def forbidden_uri?(uri)
|
|
37
|
+
Doorkeeper.config.forbid_redirect_uri.call(uri)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def unspecified_scheme?(uri)
|
|
41
|
+
return true if uri.opaque.present?
|
|
42
|
+
|
|
43
|
+
%w[localhost].include?(uri.try(:scheme))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def relative_uri?(uri)
|
|
47
|
+
uri.scheme.nil? && uri.host.nil?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def invalid_ssl_uri?(uri)
|
|
51
|
+
forces_ssl = Doorkeeper.config.force_ssl_in_redirect_uri
|
|
52
|
+
non_https = uri.try(:scheme) == "http"
|
|
53
|
+
|
|
54
|
+
if forces_ssl.respond_to?(:call)
|
|
55
|
+
forces_ssl.call(uri) && non_https
|
|
56
|
+
else
|
|
57
|
+
forces_ssl && non_https
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -2,20 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
require "active_support/lazy_load_hooks"
|
|
4
4
|
|
|
5
|
-
require "doorkeeper/orm/active_record/stale_records_cleaner"
|
|
6
|
-
|
|
7
5
|
module Doorkeeper
|
|
8
6
|
module Orm
|
|
7
|
+
# ActiveRecord ORM for Doorkeeper entity models.
|
|
8
|
+
# Consists of three main OAuth entities:
|
|
9
|
+
# * Access Token
|
|
10
|
+
# * Access Grant
|
|
11
|
+
# * Application (client)
|
|
12
|
+
#
|
|
13
|
+
# Do a lazy loading of all the required and configured stuff.
|
|
14
|
+
#
|
|
9
15
|
module ActiveRecord
|
|
10
16
|
def self.initialize_models!
|
|
11
17
|
lazy_load do
|
|
18
|
+
require "doorkeeper/orm/active_record/stale_records_cleaner"
|
|
12
19
|
require "doorkeeper/orm/active_record/access_grant"
|
|
13
20
|
require "doorkeeper/orm/active_record/access_token"
|
|
14
21
|
require "doorkeeper/orm/active_record/application"
|
|
15
22
|
|
|
16
|
-
if Doorkeeper.
|
|
17
|
-
|
|
18
|
-
options = Doorkeeper.
|
|
23
|
+
if Doorkeeper.config.active_record_options[:establish_connection]
|
|
24
|
+
Doorkeeper::Orm::ActiveRecord.models.each do |model|
|
|
25
|
+
options = Doorkeeper.config.active_record_options[:establish_connection]
|
|
19
26
|
model.establish_connection(options)
|
|
20
27
|
end
|
|
21
28
|
end
|
|
@@ -26,13 +33,21 @@ module Doorkeeper
|
|
|
26
33
|
lazy_load do
|
|
27
34
|
require "doorkeeper/models/concerns/ownership"
|
|
28
35
|
|
|
29
|
-
Doorkeeper
|
|
36
|
+
Doorkeeper.config.application_model.send :include, Doorkeeper::Models::Ownership
|
|
30
37
|
end
|
|
31
38
|
end
|
|
32
39
|
|
|
33
40
|
def self.lazy_load(&block)
|
|
34
41
|
ActiveSupport.on_load(:active_record, {}, &block)
|
|
35
42
|
end
|
|
43
|
+
|
|
44
|
+
def self.models
|
|
45
|
+
[
|
|
46
|
+
Doorkeeper::AccessGrant,
|
|
47
|
+
Doorkeeper::AccessToken,
|
|
48
|
+
Doorkeeper::Application,
|
|
49
|
+
]
|
|
50
|
+
end
|
|
36
51
|
end
|
|
37
52
|
end
|
|
38
53
|
end
|