doorkeeper 4.4.3 → 5.0.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of doorkeeper might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.gitlab-ci.yml +16 -0
- data/.travis.yml +7 -0
- data/Appraisals +2 -2
- data/Dangerfile +64 -0
- data/Gemfile +1 -1
- data/NEWS.md +98 -8
- data/README.md +110 -12
- data/Rakefile +6 -0
- data/UPGRADE.md +2 -0
- data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
- data/app/controllers/doorkeeper/application_controller.rb +6 -3
- data/app/controllers/doorkeeper/application_metal_controller.rb +6 -0
- data/app/controllers/doorkeeper/applications_controller.rb +46 -24
- data/app/controllers/doorkeeper/authorizations_controller.rb +55 -12
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +21 -2
- data/app/controllers/doorkeeper/token_info_controller.rb +2 -0
- data/app/controllers/doorkeeper/tokens_controller.rb +4 -6
- data/app/helpers/doorkeeper/dashboard_helper.rb +9 -7
- data/app/validators/redirect_uri_validator.rb +5 -2
- data/app/views/doorkeeper/applications/_delete_form.html.erb +3 -1
- data/app/views/doorkeeper/applications/_form.html.erb +25 -24
- data/app/views/doorkeeper/applications/edit.html.erb +1 -1
- data/app/views/doorkeeper/applications/index.html.erb +17 -7
- data/app/views/doorkeeper/applications/new.html.erb +1 -1
- data/app/views/doorkeeper/applications/show.html.erb +6 -6
- data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
- data/app/views/doorkeeper/authorizations/new.html.erb +4 -0
- data/app/views/layouts/doorkeeper/admin.html.erb +15 -15
- data/config/locales/en.yml +10 -1
- data/doorkeeper.gemspec +25 -26
- data/gemfiles/rails_5_2.gemfile +1 -1
- data/gemfiles/rails_master.gemfile +4 -1
- data/lib/doorkeeper/config.rb +81 -40
- data/lib/doorkeeper/engine.rb +6 -0
- data/lib/doorkeeper/errors.rb +17 -3
- data/lib/doorkeeper/grape/authorization_decorator.rb +2 -0
- data/lib/doorkeeper/grape/helpers.rb +3 -1
- data/lib/doorkeeper/helpers/controller.rb +9 -2
- data/lib/doorkeeper/models/access_grant_mixin.rb +73 -0
- data/lib/doorkeeper/models/access_token_mixin.rb +44 -25
- data/lib/doorkeeper/models/application_mixin.rb +2 -0
- data/lib/doorkeeper/models/concerns/accessible.rb +2 -0
- data/lib/doorkeeper/models/concerns/expirable.rb +2 -0
- data/lib/doorkeeper/models/concerns/orderable.rb +2 -0
- data/lib/doorkeeper/models/concerns/ownership.rb +2 -0
- data/lib/doorkeeper/models/concerns/revocable.rb +2 -0
- data/lib/doorkeeper/models/concerns/scopes.rb +3 -1
- data/lib/doorkeeper/oauth/authorization/code.rb +33 -8
- data/lib/doorkeeper/oauth/authorization/context.rb +17 -0
- data/lib/doorkeeper/oauth/authorization/token.rb +38 -14
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +2 -0
- data/lib/doorkeeper/oauth/authorization_code_request.rb +29 -2
- data/lib/doorkeeper/oauth/base_request.rb +22 -9
- data/lib/doorkeeper/oauth/base_response.rb +2 -0
- data/lib/doorkeeper/oauth/client/credentials.rb +3 -1
- data/lib/doorkeeper/oauth/client.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +4 -1
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +7 -2
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +5 -5
- data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -3
- data/lib/doorkeeper/oauth/code_request.rb +2 -0
- data/lib/doorkeeper/oauth/code_response.rb +2 -0
- data/lib/doorkeeper/oauth/error.rb +2 -0
- data/lib/doorkeeper/oauth/error_response.rb +21 -3
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +9 -2
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +2 -8
- data/lib/doorkeeper/oauth/helpers/unique_token.rb +2 -0
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +5 -2
- data/lib/doorkeeper/oauth/invalid_token_response.rb +18 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -4
- data/lib/doorkeeper/oauth/pre_authorization.rb +43 -11
- data/lib/doorkeeper/oauth/refresh_token_request.rb +16 -3
- data/lib/doorkeeper/oauth/scopes.rb +3 -1
- data/lib/doorkeeper/oauth/token.rb +7 -2
- data/lib/doorkeeper/oauth/token_introspection.rb +4 -2
- data/lib/doorkeeper/oauth/token_request.rb +2 -0
- data/lib/doorkeeper/oauth/token_response.rb +6 -2
- data/lib/doorkeeper/oauth.rb +13 -0
- data/lib/doorkeeper/orm/active_record/application.rb +75 -12
- data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +26 -0
- data/lib/doorkeeper/orm/active_record.rb +4 -0
- data/lib/doorkeeper/rails/helpers.rb +6 -4
- data/lib/doorkeeper/rails/routes/mapper.rb +2 -0
- data/lib/doorkeeper/rails/routes/mapping.rb +2 -0
- data/lib/doorkeeper/rails/routes.rb +23 -8
- 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 +1 -1
- data/lib/doorkeeper/request/client_credentials.rb +1 -1
- data/lib/doorkeeper/request/code.rb +1 -1
- data/lib/doorkeeper/request/password.rb +1 -1
- data/lib/doorkeeper/request/refresh_token.rb +1 -1
- data/lib/doorkeeper/request/strategy.rb +2 -0
- data/lib/doorkeeper/request/token.rb +1 -1
- data/lib/doorkeeper/request.rb +29 -34
- 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 +6 -24
- data/lib/doorkeeper.rb +20 -17
- data/lib/generators/doorkeeper/application_owner_generator.rb +23 -18
- 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 +23 -18
- data/lib/generators/doorkeeper/pkce_generator.rb +32 -0
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +29 -24
- data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
- data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +6 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +96 -13
- data/lib/generators/doorkeeper/templates/migration.rb.erb +2 -3
- 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 +123 -14
- data/spec/controllers/authorizations_controller_spec.rb +334 -51
- data/spec/controllers/protected_resources_controller_spec.rb +60 -18
- data/spec/controllers/token_info_controller_spec.rb +4 -12
- data/spec/controllers/tokens_controller_spec.rb +17 -20
- data/spec/dummy/Rakefile +1 -1
- data/spec/dummy/app/assets/config/manifest.js +2 -0
- data/spec/dummy/app/controllers/custom_authorizations_controller.rb +1 -1
- data/spec/dummy/app/controllers/home_controller.rb +1 -2
- data/spec/dummy/config/application.rb +1 -1
- data/spec/dummy/config/boot.rb +2 -4
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/dummy/config/environments/test.rb +5 -6
- data/spec/dummy/config/initializers/doorkeeper.rb +12 -6
- data/spec/dummy/config/initializers/new_framework_defaults.rb +2 -0
- data/spec/dummy/config/initializers/secret_token.rb +1 -1
- data/spec/dummy/config/routes.rb +3 -42
- data/spec/dummy/config.ru +1 -1
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +4 -4
- data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +1 -1
- data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +6 -0
- data/spec/dummy/db/migrate/{20180210183654_add_confidential_to_application.rb → 20180210183654_add_confidential_to_applications.rb} +1 -1
- data/spec/dummy/db/schema.rb +36 -36
- data/spec/dummy/script/rails +4 -3
- data/spec/factories.rb +6 -6
- data/spec/generators/application_owner_generator_spec.rb +1 -1
- data/spec/generators/confidential_applications_generator_spec.rb +45 -0
- data/spec/generators/install_generator_spec.rb +5 -2
- data/spec/generators/migration_generator_spec.rb +1 -1
- data/spec/generators/pkce_generator_spec.rb +43 -0
- data/spec/generators/previous_refresh_token_generator_spec.rb +1 -1
- data/spec/generators/templates/routes.rb +0 -1
- data/spec/generators/views_generator_spec.rb +2 -2
- data/spec/grape/grape_integration_spec.rb +2 -2
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
- data/spec/lib/config_spec.rb +105 -39
- data/spec/lib/doorkeeper_spec.rb +6 -131
- data/spec/lib/models/expirable_spec.rb +0 -3
- data/spec/lib/models/revocable_spec.rb +0 -2
- 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 +17 -7
- data/spec/lib/oauth/base_request_spec.rb +49 -11
- data/spec/lib/oauth/base_response_spec.rb +1 -1
- data/spec/lib/oauth/client/credentials_spec.rb +2 -4
- data/spec/lib/oauth/client_credentials/creator_spec.rb +5 -1
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +24 -7
- data/spec/lib/oauth/client_credentials/validation_spec.rb +4 -4
- data/spec/lib/oauth/client_credentials_integration_spec.rb +2 -2
- data/spec/lib/oauth/client_credentials_request_spec.rb +3 -5
- data/spec/lib/oauth/client_spec.rb +0 -3
- data/spec/lib/oauth/code_request_spec.rb +5 -3
- data/spec/lib/oauth/code_response_spec.rb +1 -1
- data/spec/lib/oauth/error_response_spec.rb +0 -3
- data/spec/lib/oauth/error_spec.rb +0 -2
- data/spec/lib/oauth/forbidden_token_response_spec.rb +1 -4
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +8 -11
- data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -1
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +22 -13
- data/spec/lib/oauth/invalid_token_response_spec.rb +1 -4
- data/spec/lib/oauth/password_access_token_request_spec.rb +53 -6
- data/spec/lib/oauth/pre_authorization_spec.rb +33 -4
- data/spec/lib/oauth/refresh_token_request_spec.rb +22 -14
- data/spec/lib/oauth/scopes_spec.rb +0 -3
- data/spec/lib/oauth/token_request_spec.rb +8 -9
- data/spec/lib/oauth/token_response_spec.rb +0 -1
- data/spec/lib/oauth/token_spec.rb +40 -14
- data/spec/lib/request/strategy_spec.rb +0 -1
- data/spec/lib/server_spec.rb +7 -7
- data/spec/lib/stale_records_cleaner_spec.rb +89 -0
- data/spec/models/doorkeeper/access_grant_spec.rb +44 -1
- data/spec/models/doorkeeper/access_token_spec.rb +80 -32
- data/spec/models/doorkeeper/application_spec.rb +293 -221
- data/spec/requests/applications/applications_request_spec.rb +134 -1
- data/spec/requests/applications/authorized_applications_spec.rb +1 -1
- data/spec/requests/endpoints/authorization_spec.rb +3 -3
- data/spec/requests/endpoints/token_spec.rb +7 -5
- data/spec/requests/flows/authorization_code_errors_spec.rb +2 -2
- data/spec/requests/flows/authorization_code_spec.rb +258 -2
- data/spec/requests/flows/client_credentials_spec.rb +46 -6
- 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 +61 -3
- data/spec/requests/flows/refresh_token_spec.rb +59 -2
- data/spec/requests/flows/revoke_token_spec.rb +20 -20
- data/spec/requests/flows/skip_authorization_spec.rb +16 -11
- data/spec/requests/protected_resources/metal_spec.rb +1 -1
- data/spec/requests/protected_resources/private_api_spec.rb +3 -3
- data/spec/routing/custom_controller_routes_spec.rb +59 -7
- data/spec/routing/default_routes_spec.rb +2 -2
- data/spec/routing/scoped_routes_spec.rb +16 -2
- data/spec/spec_helper.rb +54 -3
- data/spec/spec_helper_integration.rb +2 -74
- data/spec/support/dependencies/{factory_girl.rb → factory_bot.rb} +0 -0
- data/spec/support/doorkeeper_rspec.rb +20 -0
- data/spec/support/helpers/authorization_request_helper.rb +4 -4
- data/spec/support/helpers/model_helper.rb +8 -4
- data/spec/support/helpers/request_spec_helper.rb +10 -2
- data/spec/support/helpers/url_helper.rb +18 -14
- data/spec/support/http_method_shim.rb +12 -16
- data/spec/support/shared/controllers_shared_context.rb +56 -0
- data/spec/validators/redirect_uri_validator_spec.rb +9 -3
- data/spec/version/version_spec.rb +3 -3
- data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
- metadata +54 -35
- data/lib/generators/doorkeeper/add_client_confidentiality_generator.rb +0 -31
- data/lib/generators/doorkeeper/templates/add_confidential_to_application_migration.rb.erb +0 -11
- data/spec/controllers/application_metal_controller.rb +0 -10
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Doorkeeper
|
2
4
|
module OAuth
|
3
5
|
class PreAuthorization
|
@@ -7,17 +9,21 @@ module Doorkeeper
|
|
7
9
|
validate :client, error: :invalid_client
|
8
10
|
validate :scopes, error: :invalid_scope
|
9
11
|
validate :redirect_uri, error: :invalid_redirect_uri
|
12
|
+
validate :code_challenge_method, error: :invalid_code_challenge_method
|
10
13
|
|
11
|
-
attr_accessor :server, :client, :response_type, :redirect_uri, :state
|
14
|
+
attr_accessor :server, :client, :response_type, :redirect_uri, :state,
|
15
|
+
:code_challenge, :code_challenge_method
|
12
16
|
attr_writer :scope
|
13
17
|
|
14
18
|
def initialize(server, client, attrs = {})
|
15
|
-
@server
|
16
|
-
@client
|
17
|
-
@response_type
|
18
|
-
@redirect_uri
|
19
|
-
@scope
|
20
|
-
@state
|
19
|
+
@server = server
|
20
|
+
@client = client
|
21
|
+
@response_type = attrs[:response_type]
|
22
|
+
@redirect_uri = attrs[:redirect_uri]
|
23
|
+
@scope = attrs[:scope]
|
24
|
+
@state = attrs[:state]
|
25
|
+
@code_challenge = attrs[:code_challenge]
|
26
|
+
@code_challenge_method = attrs[:code_challenge_method]
|
21
27
|
end
|
22
28
|
|
23
29
|
def authorizable?
|
@@ -29,15 +35,36 @@ module Doorkeeper
|
|
29
35
|
end
|
30
36
|
|
31
37
|
def scope
|
32
|
-
@scope.presence ||
|
38
|
+
@scope.presence || build_scopes
|
33
39
|
end
|
34
40
|
|
35
41
|
def error_response
|
36
42
|
OAuth::ErrorResponse.from_request(self)
|
37
43
|
end
|
38
44
|
|
45
|
+
def as_json(_options)
|
46
|
+
{
|
47
|
+
client_id: client.uid,
|
48
|
+
redirect_uri: redirect_uri,
|
49
|
+
state: state,
|
50
|
+
response_type: response_type,
|
51
|
+
scope: scope,
|
52
|
+
client_name: client.name,
|
53
|
+
status: I18n.t('doorkeeper.pre_authorization.status')
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
39
57
|
private
|
40
58
|
|
59
|
+
def build_scopes
|
60
|
+
client_scopes = client.application.scopes
|
61
|
+
if client_scopes.blank?
|
62
|
+
server.default_scopes.to_s
|
63
|
+
else
|
64
|
+
(server.default_scopes & client_scopes).to_s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
41
68
|
def validate_response_type
|
42
69
|
server.authorization_response_types.include? response_type
|
43
70
|
end
|
@@ -47,7 +74,8 @@ module Doorkeeper
|
|
47
74
|
end
|
48
75
|
|
49
76
|
def validate_scopes
|
50
|
-
return true
|
77
|
+
return true if scope.blank?
|
78
|
+
|
51
79
|
Helpers::ScopeChecker.valid?(
|
52
80
|
scope,
|
53
81
|
server.scopes,
|
@@ -55,14 +83,18 @@ module Doorkeeper
|
|
55
83
|
)
|
56
84
|
end
|
57
85
|
|
58
|
-
# TODO: test uri should be matched against the client's one
|
59
86
|
def validate_redirect_uri
|
60
87
|
return false if redirect_uri.blank?
|
61
88
|
|
62
89
|
Helpers::URIChecker.valid_for_authorization?(
|
63
|
-
redirect_uri,
|
90
|
+
redirect_uri,
|
91
|
+
client.redirect_uri
|
64
92
|
)
|
65
93
|
end
|
94
|
+
|
95
|
+
def validate_code_challenge_method
|
96
|
+
!code_challenge.present? || (code_challenge_method.present? && code_challenge_method =~ /^plain$|^S256$/)
|
97
|
+
end
|
66
98
|
end
|
67
99
|
end
|
68
100
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Doorkeeper
|
2
4
|
module OAuth
|
3
5
|
class RefreshTokenRequest < BaseRequest
|
@@ -65,7 +67,12 @@ module Doorkeeper
|
|
65
67
|
end
|
66
68
|
|
67
69
|
def access_token_expires_in
|
68
|
-
Authorization::Token.
|
70
|
+
context = Authorization::Token.build_context(
|
71
|
+
client,
|
72
|
+
Doorkeeper::OAuth::REFRESH_TOKEN,
|
73
|
+
scopes
|
74
|
+
)
|
75
|
+
Authorization::Token.access_token_expires_in(server, context)
|
69
76
|
end
|
70
77
|
|
71
78
|
def validate_token_presence
|
@@ -77,11 +84,17 @@ module Doorkeeper
|
|
77
84
|
end
|
78
85
|
|
79
86
|
def validate_client
|
80
|
-
|
87
|
+
return true if credentials.blank?
|
88
|
+
|
89
|
+
client.present?
|
81
90
|
end
|
82
91
|
|
92
|
+
# @see https://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-1.5
|
93
|
+
#
|
83
94
|
def validate_client_match
|
84
|
-
|
95
|
+
return true if refresh_token.application_id.blank?
|
96
|
+
|
97
|
+
client && refresh_token.application_id == client.id
|
85
98
|
end
|
86
99
|
|
87
100
|
def validate_scope
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Doorkeeper
|
2
4
|
module OAuth
|
3
5
|
class Scopes
|
@@ -41,7 +43,7 @@ module Doorkeeper
|
|
41
43
|
end
|
42
44
|
|
43
45
|
def has_scopes?(scopes)
|
44
|
-
scopes.all? { |
|
46
|
+
scopes.all? { |scope| exists?(scope) }
|
45
47
|
end
|
46
48
|
|
47
49
|
def +(other)
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Doorkeeper
|
2
4
|
module OAuth
|
3
5
|
class Token
|
4
6
|
class << self
|
5
7
|
def from_request(request, *methods)
|
6
|
-
methods.inject(nil) do |
|
8
|
+
methods.inject(nil) do |_, method|
|
7
9
|
method = self.method(method) if method.is_a?(Symbol)
|
8
10
|
credentials = method.call(request)
|
9
11
|
break credentials unless credentials.blank?
|
@@ -13,7 +15,10 @@ module Doorkeeper
|
|
13
15
|
def authenticate(request, *methods)
|
14
16
|
if (token = from_request(request, *methods))
|
15
17
|
access_token = AccessToken.by_token(token)
|
16
|
-
|
18
|
+
refresh_token_enabled = Doorkeeper.configuration.refresh_token_enabled?
|
19
|
+
if access_token.present? && refresh_token_enabled
|
20
|
+
access_token.revoke_previous_refresh_token!
|
21
|
+
end
|
17
22
|
access_token
|
18
23
|
end
|
19
24
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Doorkeeper
|
2
4
|
module OAuth
|
3
5
|
# RFC7662 OAuth 2.0 Token Introspection
|
@@ -47,12 +49,12 @@ module Doorkeeper
|
|
47
49
|
|
48
50
|
# Client Authentication
|
49
51
|
def authorized_client
|
50
|
-
@
|
52
|
+
@authorized_client ||= server.credentials && server.client
|
51
53
|
end
|
52
54
|
|
53
55
|
# Bearer Token Authentication
|
54
56
|
def authorized_token
|
55
|
-
@
|
57
|
+
@authorized_token ||=
|
56
58
|
OAuth::Token.authenticate(server.context.request, :from_bearer_authorization)
|
57
59
|
end
|
58
60
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Doorkeeper
|
2
4
|
module OAuth
|
3
5
|
class TokenResponse
|
@@ -23,9 +25,11 @@ module Doorkeeper
|
|
23
25
|
end
|
24
26
|
|
25
27
|
def headers
|
26
|
-
{
|
28
|
+
{
|
29
|
+
'Cache-Control' => 'no-store',
|
27
30
|
'Pragma' => 'no-cache',
|
28
|
-
'Content-Type' => 'application/json; charset=utf-8'
|
31
|
+
'Content-Type' => 'application/json; charset=utf-8'
|
32
|
+
}
|
29
33
|
end
|
30
34
|
end
|
31
35
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Doorkeeper
|
4
|
+
module OAuth
|
5
|
+
GRANT_TYPES = [
|
6
|
+
AUTHORIZATION_CODE = 'authorization_code'.freeze,
|
7
|
+
IMPLICIT = 'implicit'.freeze,
|
8
|
+
PASSWORD = 'password'.freeze,
|
9
|
+
CLIENT_CREDENTIALS = 'client_credentials'.freeze,
|
10
|
+
REFRESH_TOKEN = 'refresh_token'.freeze
|
11
|
+
].freeze
|
12
|
+
end
|
13
|
+
end
|
@@ -13,6 +13,8 @@ module Doorkeeper
|
|
13
13
|
validates :redirect_uri, redirect_uri: true
|
14
14
|
validates :confidential, inclusion: { in: [true, false] }
|
15
15
|
|
16
|
+
validate :scopes_match_configured, if: :enforce_scopes?
|
17
|
+
|
16
18
|
before_validation :generate_uid, :generate_secret, on: :create
|
17
19
|
|
18
20
|
has_many :authorized_tokens, -> { where(revoked_at: nil) }, class_name: 'AccessToken'
|
@@ -32,20 +34,38 @@ module Doorkeeper
|
|
32
34
|
where(id: resource_access_tokens.select(:application_id).distinct)
|
33
35
|
end
|
34
36
|
|
35
|
-
#
|
36
|
-
#
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
# Revokes AccessToken and AccessGrant records that have not been revoked and
|
38
|
+
# associated with the specific Application and Resource Owner.
|
39
|
+
#
|
40
|
+
# @param resource_owner [ActiveRecord::Base]
|
41
|
+
# instance of the Resource Owner model
|
42
|
+
#
|
43
|
+
def self.revoke_tokens_and_grants_for(id, resource_owner)
|
44
|
+
AccessToken.revoke_all_for(id, resource_owner)
|
45
|
+
AccessGrant.revoke_all_for(id, resource_owner)
|
43
46
|
end
|
44
47
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
48
|
+
# Represents client as set of it's attributes in JSON format.
|
49
|
+
# This is the right way how we want to override ActiveRecord #to_json.
|
50
|
+
#
|
51
|
+
# Respects privacy settings and serializes minimum set of attributes
|
52
|
+
# for public/private clients and full set for authorized owners.
|
53
|
+
#
|
54
|
+
# @return [Hash] entity attributes for JSON
|
55
|
+
#
|
56
|
+
def as_json(options = {})
|
57
|
+
# if application belongs to some owner we need to check if it's the same as
|
58
|
+
# the one passed in the options or check if we render the client as an owner
|
59
|
+
if (respond_to?(:owner) && owner && owner == options[:current_resource_owner]) ||
|
60
|
+
options[:as_owner]
|
61
|
+
# Owners can see all the client attributes, fallback to ActiveModel serialization
|
62
|
+
super
|
63
|
+
else
|
64
|
+
# if application has no owner or it's owner doesn't match one from the options
|
65
|
+
# we render only minimum set of attributes that could be exposed to a public
|
66
|
+
only = extract_serializable_attributes(options)
|
67
|
+
super(options.merge(only: only))
|
68
|
+
end
|
49
69
|
end
|
50
70
|
|
51
71
|
private
|
@@ -57,5 +77,48 @@ module Doorkeeper
|
|
57
77
|
def generate_secret
|
58
78
|
self.secret = UniqueToken.generate if secret.blank?
|
59
79
|
end
|
80
|
+
|
81
|
+
def scopes_match_configured
|
82
|
+
if scopes.present? &&
|
83
|
+
!ScopeChecker.valid?(scopes.to_s, Doorkeeper.configuration.scopes)
|
84
|
+
errors.add(:scopes, :not_match_configured)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def enforce_scopes?
|
89
|
+
Doorkeeper.configuration.enforce_configured_scopes?
|
90
|
+
end
|
91
|
+
|
92
|
+
# Helper method to extract collection of serializable attribute names
|
93
|
+
# considering serialization options (like `only`, `except` and so on).
|
94
|
+
#
|
95
|
+
# @param options [Hash] serialization options
|
96
|
+
#
|
97
|
+
# @return [Array<String>]
|
98
|
+
# collection of attributes to be serialized using #as_json
|
99
|
+
#
|
100
|
+
def extract_serializable_attributes(options = {})
|
101
|
+
opts = options.try(:dup) || {}
|
102
|
+
only = Array.wrap(opts[:only]).map(&:to_s)
|
103
|
+
|
104
|
+
only = if only.blank?
|
105
|
+
serializable_attributes
|
106
|
+
else
|
107
|
+
only & serializable_attributes
|
108
|
+
end
|
109
|
+
|
110
|
+
only -= Array.wrap(opts[:except]).map(&:to_s) if opts.key?(:except)
|
111
|
+
only.uniq
|
112
|
+
end
|
113
|
+
|
114
|
+
# Collection of attributes that could be serialized for public.
|
115
|
+
# Override this method if you need additional attributes to be serialized.
|
116
|
+
#
|
117
|
+
# @return [Array<String>] collection of serializable attributes
|
118
|
+
def serializable_attributes
|
119
|
+
attributes = %w[id name created_at]
|
120
|
+
attributes << "uid" unless confidential?
|
121
|
+
attributes
|
122
|
+
end
|
60
123
|
end
|
61
124
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Doorkeeper
|
4
|
+
module Orm
|
5
|
+
module ActiveRecord
|
6
|
+
class StaleRecordsCleaner
|
7
|
+
def initialize(base_scope)
|
8
|
+
@base_scope = base_scope
|
9
|
+
end
|
10
|
+
|
11
|
+
def clean_revoked
|
12
|
+
table = @base_scope.arel_table
|
13
|
+
@base_scope.where.not(revoked_at: nil)
|
14
|
+
.where(table[:revoked_at].lt(Time.current))
|
15
|
+
.delete_all
|
16
|
+
end
|
17
|
+
|
18
|
+
def clean_expired(ttl)
|
19
|
+
table = @base_scope.arel_table
|
20
|
+
@base_scope.where(table[:created_at].lt(Time.current - ttl))
|
21
|
+
.delete_all
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,12 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Doorkeeper
|
2
4
|
module Rails
|
3
5
|
module Helpers
|
4
6
|
def doorkeeper_authorize!(*scopes)
|
5
7
|
@_doorkeeper_scopes = scopes.presence || Doorkeeper.configuration.default_scopes
|
6
8
|
|
7
|
-
unless valid_doorkeeper_token?
|
8
|
-
doorkeeper_render_error
|
9
|
-
end
|
9
|
+
doorkeeper_render_error unless valid_doorkeeper_token?
|
10
10
|
end
|
11
11
|
|
12
12
|
def doorkeeper_unauthorized_render_options(**); end
|
@@ -21,6 +21,8 @@ module Doorkeeper
|
|
21
21
|
|
22
22
|
def doorkeeper_render_error
|
23
23
|
error = doorkeeper_error
|
24
|
+
error.raise_exception! if Doorkeeper.configuration.raise_on_errors?
|
25
|
+
|
24
26
|
headers.merge!(error.headers.reject { |k| k == "Content-Type" })
|
25
27
|
doorkeeper_render_error_with(error)
|
26
28
|
end
|
@@ -68,7 +70,7 @@ module Doorkeeper
|
|
68
70
|
end
|
69
71
|
|
70
72
|
def doorkeeper_token
|
71
|
-
@
|
73
|
+
@doorkeeper_token ||= OAuth::Token.authenticate(
|
72
74
|
request,
|
73
75
|
*Doorkeeper.configuration.access_token_methods
|
74
76
|
)
|
@@ -1,9 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'doorkeeper/rails/routes/mapping'
|
2
4
|
require 'doorkeeper/rails/routes/mapper'
|
3
5
|
|
4
6
|
module Doorkeeper
|
5
7
|
module Rails
|
6
8
|
class Routes # :nodoc:
|
9
|
+
mattr_reader :mapping do
|
10
|
+
{}
|
11
|
+
end
|
12
|
+
|
7
13
|
module Helper
|
8
14
|
def use_doorkeeper(options = {}, &block)
|
9
15
|
Doorkeeper::Rails::Routes.new(self, &block).generate_routes!(options)
|
@@ -19,6 +25,10 @@ module Doorkeeper
|
|
19
25
|
def initialize(routes, &block)
|
20
26
|
@routes = routes
|
21
27
|
@mapping = Mapper.new.map(&block)
|
28
|
+
|
29
|
+
if Doorkeeper.configuration.api_only
|
30
|
+
@mapping.skips.push(:applications, :authorized_applications)
|
31
|
+
end
|
22
32
|
end
|
23
33
|
|
24
34
|
def generate_routes!(options)
|
@@ -36,7 +46,11 @@ module Doorkeeper
|
|
36
46
|
private
|
37
47
|
|
38
48
|
def map_route(name, method)
|
39
|
-
|
49
|
+
return if @mapping.skipped?(name)
|
50
|
+
|
51
|
+
send(method, @mapping[name])
|
52
|
+
|
53
|
+
mapping[name] = @mapping[name]
|
40
54
|
end
|
41
55
|
|
42
56
|
def authorization_routes(mapping)
|
@@ -47,7 +61,7 @@ module Doorkeeper
|
|
47
61
|
as: mapping[:as],
|
48
62
|
controller: mapping[:controllers]
|
49
63
|
) do
|
50
|
-
routes.get
|
64
|
+
routes.get '/native', action: :show, on: :member
|
51
65
|
routes.get '/', action: :new, on: :member
|
52
66
|
end
|
53
67
|
end
|
@@ -79,15 +93,16 @@ module Doorkeeper
|
|
79
93
|
end
|
80
94
|
|
81
95
|
def application_routes(mapping)
|
82
|
-
routes.resources :doorkeeper_applications,
|
96
|
+
routes.resources :doorkeeper_applications,
|
97
|
+
controller: mapping[:controllers],
|
98
|
+
as: :applications,
|
99
|
+
path: 'applications'
|
83
100
|
end
|
84
101
|
|
85
102
|
def authorized_applications_routes(mapping)
|
86
|
-
routes.resources :authorized_applications,
|
87
|
-
|
88
|
-
|
89
|
-
def native_authorization_code_route
|
90
|
-
Doorkeeper.configuration.native_authorization_code_route
|
103
|
+
routes.resources :authorized_applications,
|
104
|
+
only: %i[index destroy],
|
105
|
+
controller: mapping[:controllers]
|
91
106
|
end
|
92
107
|
end
|
93
108
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :doorkeeper do
|
4
|
+
namespace :db do
|
5
|
+
desc 'Removes stale data from doorkeeper related database tables'
|
6
|
+
task cleanup: [
|
7
|
+
'doorkeeper:db:cleanup:revoked_tokens',
|
8
|
+
'doorkeeper:db:cleanup:expired_tokens',
|
9
|
+
'doorkeeper:db:cleanup:revoked_grants',
|
10
|
+
'doorkeeper:db:cleanup:expired_grants'
|
11
|
+
]
|
12
|
+
|
13
|
+
namespace :cleanup do
|
14
|
+
desc 'Removes stale access tokens'
|
15
|
+
task revoked_tokens: 'doorkeeper:setup' do
|
16
|
+
cleaner = Doorkeeper::StaleRecordsCleaner.new(Doorkeeper::AccessToken)
|
17
|
+
cleaner.clean_revoked
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'Removes expired (TTL passed) access tokens'
|
21
|
+
task expired_tokens: 'doorkeeper:setup' do
|
22
|
+
expirable_tokens = Doorkeeper::AccessToken.where(refresh_token: nil)
|
23
|
+
cleaner = Doorkeeper::StaleRecordsCleaner.new(expirable_tokens)
|
24
|
+
cleaner.clean_expired(Doorkeeper.configuration.access_token_expires_in)
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Removes stale access grants'
|
28
|
+
task revoked_grants: 'doorkeeper:setup' do
|
29
|
+
cleaner = Doorkeeper::StaleRecordsCleaner.new(Doorkeeper::AccessGrant)
|
30
|
+
cleaner.clean_revoked
|
31
|
+
end
|
32
|
+
|
33
|
+
desc 'Removes expired (TTL passed) access grants'
|
34
|
+
task expired_grants: 'doorkeeper:setup' do
|
35
|
+
cleaner = Doorkeeper::StaleRecordsCleaner.new(Doorkeeper::AccessGrant)
|
36
|
+
cleaner.clean_expired(Doorkeeper.configuration.authorization_code_expires_in)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|