doorkeeper 3.1.0 → 5.6.2
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/CHANGELOG.md +1079 -0
- data/README.md +114 -326
- data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
- data/app/controllers/doorkeeper/application_controller.rb +7 -6
- data/app/controllers/doorkeeper/application_metal_controller.rb +9 -12
- data/app/controllers/doorkeeper/applications_controller.rb +66 -21
- data/app/controllers/doorkeeper/authorizations_controller.rb +100 -18
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +23 -4
- data/app/controllers/doorkeeper/token_info_controller.rb +16 -4
- data/app/controllers/doorkeeper/tokens_controller.rb +138 -22
- data/app/helpers/doorkeeper/dashboard_helper.rb +15 -9
- data/app/views/doorkeeper/applications/_delete_form.html.erb +4 -3
- data/app/views/doorkeeper/applications/_form.html.erb +33 -21
- data/app/views/doorkeeper/applications/edit.html.erb +1 -1
- data/app/views/doorkeeper/applications/index.html.erb +18 -6
- data/app/views/doorkeeper/applications/new.html.erb +1 -1
- data/app/views/doorkeeper/applications/show.html.erb +40 -16
- data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
- data/app/views/doorkeeper/authorizations/form_post.html.erb +15 -0
- data/app/views/doorkeeper/authorizations/new.html.erb +17 -11
- data/app/views/doorkeeper/authorized_applications/_delete_form.html.erb +1 -2
- data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
- data/app/views/layouts/doorkeeper/admin.html.erb +16 -14
- data/config/locales/en.yml +37 -9
- data/lib/doorkeeper/config/abstract_builder.rb +28 -0
- data/lib/doorkeeper/config/option.rb +82 -0
- data/lib/doorkeeper/config/validations.rb +53 -0
- data/lib/doorkeeper/config.rb +602 -142
- data/lib/doorkeeper/engine.rb +22 -7
- data/lib/doorkeeper/errors.rb +37 -10
- data/lib/doorkeeper/grant_flow/fallback_flow.rb +15 -0
- data/lib/doorkeeper/grant_flow/flow.rb +44 -0
- data/lib/doorkeeper/grant_flow/registry.rb +50 -0
- data/lib/doorkeeper/grant_flow.rb +45 -0
- data/lib/doorkeeper/grape/authorization_decorator.rb +6 -4
- data/lib/doorkeeper/grape/helpers.rb +24 -12
- data/lib/doorkeeper/helpers/controller.rb +49 -27
- data/lib/doorkeeper/models/access_grant_mixin.rb +99 -16
- data/lib/doorkeeper/models/access_token_mixin.rb +386 -77
- data/lib/doorkeeper/models/application_mixin.rb +73 -30
- data/lib/doorkeeper/models/concerns/accessible.rb +6 -0
- data/lib/doorkeeper/models/concerns/expirable.rb +20 -6
- data/lib/doorkeeper/models/concerns/expiration_time_sql_math.rb +88 -0
- data/lib/doorkeeper/models/concerns/orderable.rb +15 -0
- data/lib/doorkeeper/models/concerns/ownership.rb +4 -2
- data/lib/doorkeeper/models/concerns/resource_ownerable.rb +47 -0
- data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
- data/lib/doorkeeper/models/concerns/revocable.rb +13 -2
- data/lib/doorkeeper/models/concerns/scopes.rb +12 -2
- data/lib/doorkeeper/models/concerns/secret_storable.rb +106 -0
- data/lib/doorkeeper/oauth/authorization/code.rb +48 -12
- data/lib/doorkeeper/oauth/authorization/context.rb +17 -0
- data/lib/doorkeeper/oauth/authorization/token.rb +72 -28
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +22 -18
- data/lib/doorkeeper/oauth/authorization_code_request.rb +64 -14
- data/lib/doorkeeper/oauth/base_request.rb +66 -0
- data/lib/doorkeeper/oauth/base_response.rb +31 -0
- data/lib/doorkeeper/oauth/client/credentials.rb +23 -10
- data/lib/doorkeeper/oauth/client.rb +10 -12
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +48 -4
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +17 -9
- data/lib/doorkeeper/oauth/client_credentials/validator.rb +55 -0
- data/lib/doorkeeper/oauth/client_credentials_request.rb +14 -15
- data/lib/doorkeeper/oauth/code_request.rb +8 -12
- data/lib/doorkeeper/oauth/code_response.rb +31 -19
- data/lib/doorkeeper/oauth/error.rb +5 -3
- data/lib/doorkeeper/oauth/error_response.rb +41 -20
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +11 -3
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +24 -19
- data/lib/doorkeeper/oauth/helpers/unique_token.rb +20 -3
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +55 -4
- data/lib/doorkeeper/oauth/hooks/context.rb +21 -0
- data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
- data/lib/doorkeeper/oauth/invalid_token_response.rb +31 -5
- data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +46 -18
- data/lib/doorkeeper/oauth/pre_authorization.rb +135 -26
- data/lib/doorkeeper/oauth/refresh_token_request.rb +67 -30
- data/lib/doorkeeper/oauth/scopes.rb +26 -12
- data/lib/doorkeeper/oauth/token.rb +28 -25
- data/lib/doorkeeper/oauth/token_introspection.rb +202 -0
- data/lib/doorkeeper/oauth/token_request.rb +8 -21
- data/lib/doorkeeper/oauth/token_response.rb +14 -10
- data/lib/doorkeeper/oauth.rb +13 -0
- data/lib/doorkeeper/orm/active_record/access_grant.rb +6 -4
- data/lib/doorkeeper/orm/active_record/access_token.rb +5 -17
- data/lib/doorkeeper/orm/active_record/application.rb +6 -20
- data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +69 -0
- data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +81 -0
- data/lib/doorkeeper/orm/active_record/mixins/application.rb +214 -0
- data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +66 -0
- data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +33 -0
- data/lib/doorkeeper/orm/active_record.rb +36 -26
- data/lib/doorkeeper/rails/helpers.rb +14 -15
- data/lib/doorkeeper/rails/routes/abstract_router.rb +35 -0
- data/lib/doorkeeper/rails/routes/mapper.rb +4 -2
- data/lib/doorkeeper/rails/routes/mapping.rb +10 -8
- data/lib/doorkeeper/rails/routes/registry.rb +45 -0
- data/lib/doorkeeper/rails/routes.rb +45 -28
- 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 +12 -4
- data/lib/doorkeeper/request/client_credentials.rb +3 -3
- data/lib/doorkeeper/request/code.rb +1 -1
- data/lib/doorkeeper/request/password.rb +5 -4
- data/lib/doorkeeper/request/refresh_token.rb +6 -5
- data/lib/doorkeeper/request/strategy.rb +4 -2
- data/lib/doorkeeper/request/token.rb +1 -1
- data/lib/doorkeeper/request.rb +62 -29
- 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 +9 -19
- data/lib/doorkeeper/stale_records_cleaner.rb +24 -0
- data/lib/doorkeeper/validations.rb +5 -2
- data/lib/doorkeeper/version.rb +12 -1
- data/lib/doorkeeper.rb +112 -56
- data/lib/generators/doorkeeper/application_owner_generator.rb +28 -13
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +33 -0
- data/lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb +39 -0
- data/lib/generators/doorkeeper/install_generator.rb +19 -9
- data/lib/generators/doorkeeper/migration_generator.rb +27 -10
- data/lib/generators/doorkeeper/pkce_generator.rb +33 -0
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +41 -0
- data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
- data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb +9 -0
- data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb +13 -0
- data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +8 -0
- data/lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb +17 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +417 -32
- data/lib/generators/doorkeeper/templates/migration.rb.erb +88 -0
- data/lib/generators/doorkeeper/views_generator.rb +8 -4
- data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
- metadata +163 -280
- data/.gitignore +0 -14
- data/.hound.yml +0 -13
- data/.rspec +0 -1
- data/.travis.yml +0 -22
- data/CONTRIBUTING.md +0 -45
- data/Gemfile +0 -10
- data/NEWS.md +0 -525
- data/RELEASING.md +0 -17
- data/Rakefile +0 -20
- data/app/validators/redirect_uri_validator.rb +0 -34
- data/doorkeeper.gemspec +0 -27
- data/lib/doorkeeper/oauth/client/methods.rb +0 -18
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +0 -45
- data/lib/doorkeeper/oauth/request_concern.rb +0 -48
- data/lib/generators/doorkeeper/application_scopes_generator.rb +0 -34
- data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb +0 -7
- data/lib/generators/doorkeeper/templates/add_scopes_to_oauth_applications.rb +0 -5
- data/lib/generators/doorkeeper/templates/migration.rb +0 -50
- data/spec/controllers/applications_controller_spec.rb +0 -58
- data/spec/controllers/authorizations_controller_spec.rb +0 -203
- data/spec/controllers/protected_resources_controller_spec.rb +0 -271
- data/spec/controllers/token_info_controller_spec.rb +0 -52
- data/spec/controllers/tokens_controller_spec.rb +0 -88
- data/spec/dummy/Rakefile +0 -7
- data/spec/dummy/app/controllers/application_controller.rb +0 -3
- data/spec/dummy/app/controllers/custom_authorizations_controller.rb +0 -7
- data/spec/dummy/app/controllers/full_protected_resources_controller.rb +0 -12
- data/spec/dummy/app/controllers/home_controller.rb +0 -17
- data/spec/dummy/app/controllers/metal_controller.rb +0 -11
- data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +0 -11
- data/spec/dummy/app/helpers/application_helper.rb +0 -5
- data/spec/dummy/app/models/user.rb +0 -9
- data/spec/dummy/app/views/home/index.html.erb +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +0 -14
- data/spec/dummy/config/application.rb +0 -57
- data/spec/dummy/config/boot.rb +0 -9
- data/spec/dummy/config/database.yml +0 -15
- data/spec/dummy/config/environment.rb +0 -5
- data/spec/dummy/config/environments/development.rb +0 -29
- data/spec/dummy/config/environments/production.rb +0 -62
- data/spec/dummy/config/environments/test.rb +0 -55
- data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/dummy/config/initializers/doorkeeper.rb +0 -96
- data/spec/dummy/config/initializers/secret_token.rb +0 -9
- data/spec/dummy/config/initializers/session_store.rb +0 -8
- data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/spec/dummy/config/locales/doorkeeper.en.yml +0 -5
- data/spec/dummy/config/routes.rb +0 -52
- data/spec/dummy/config.ru +0 -4
- data/spec/dummy/db/migrate/20111122132257_create_users.rb +0 -9
- data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +0 -5
- data/spec/dummy/db/migrate/20130902165751_create_doorkeeper_tables.rb +0 -41
- data/spec/dummy/db/migrate/20130902175349_add_owner_to_application.rb +0 -7
- data/spec/dummy/db/migrate/20141209001746_add_scopes_to_oauth_applications.rb +0 -5
- data/spec/dummy/db/schema.rb +0 -66
- data/spec/dummy/public/404.html +0 -26
- data/spec/dummy/public/422.html +0 -26
- data/spec/dummy/public/500.html +0 -26
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +0 -6
- data/spec/factories.rb +0 -26
- data/spec/generators/application_owner_generator_spec.rb +0 -22
- data/spec/generators/install_generator_spec.rb +0 -31
- data/spec/generators/migration_generator_spec.rb +0 -20
- data/spec/generators/templates/routes.rb +0 -3
- data/spec/generators/views_generator_spec.rb +0 -27
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +0 -24
- data/spec/lib/config_spec.rb +0 -317
- data/spec/lib/doorkeeper_spec.rb +0 -28
- data/spec/lib/models/expirable_spec.rb +0 -51
- data/spec/lib/models/revocable_spec.rb +0 -36
- data/spec/lib/models/scopes_spec.rb +0 -43
- data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -42
- data/spec/lib/oauth/authorization_code_request_spec.rb +0 -80
- data/spec/lib/oauth/client/credentials_spec.rb +0 -47
- data/spec/lib/oauth/client/methods_spec.rb +0 -54
- data/spec/lib/oauth/client_credentials/creator_spec.rb +0 -44
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +0 -86
- data/spec/lib/oauth/client_credentials/validation_spec.rb +0 -54
- data/spec/lib/oauth/client_credentials_integration_spec.rb +0 -27
- data/spec/lib/oauth/client_credentials_request_spec.rb +0 -104
- data/spec/lib/oauth/client_spec.rb +0 -39
- data/spec/lib/oauth/code_request_spec.rb +0 -45
- data/spec/lib/oauth/error_response_spec.rb +0 -61
- data/spec/lib/oauth/error_spec.rb +0 -23
- data/spec/lib/oauth/forbidden_token_response_spec.rb +0 -23
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -64
- data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -20
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +0 -104
- data/spec/lib/oauth/invalid_token_response_spec.rb +0 -28
- data/spec/lib/oauth/password_access_token_request_spec.rb +0 -90
- data/spec/lib/oauth/pre_authorization_spec.rb +0 -155
- data/spec/lib/oauth/refresh_token_request_spec.rb +0 -123
- data/spec/lib/oauth/scopes_spec.rb +0 -123
- data/spec/lib/oauth/token_request_spec.rb +0 -98
- data/spec/lib/oauth/token_response_spec.rb +0 -85
- data/spec/lib/oauth/token_spec.rb +0 -109
- data/spec/lib/request/strategy_spec.rb +0 -53
- data/spec/lib/server_spec.rb +0 -52
- data/spec/models/doorkeeper/access_grant_spec.rb +0 -36
- data/spec/models/doorkeeper/access_token_spec.rb +0 -350
- data/spec/models/doorkeeper/application_spec.rb +0 -187
- data/spec/requests/applications/applications_request_spec.rb +0 -94
- data/spec/requests/applications/authorized_applications_spec.rb +0 -30
- data/spec/requests/endpoints/authorization_spec.rb +0 -72
- data/spec/requests/endpoints/token_spec.rb +0 -64
- data/spec/requests/flows/authorization_code_errors_spec.rb +0 -66
- data/spec/requests/flows/authorization_code_spec.rb +0 -156
- data/spec/requests/flows/client_credentials_spec.rb +0 -58
- data/spec/requests/flows/implicit_grant_errors_spec.rb +0 -32
- data/spec/requests/flows/implicit_grant_spec.rb +0 -61
- data/spec/requests/flows/password_spec.rb +0 -94
- data/spec/requests/flows/refresh_token_spec.rb +0 -104
- data/spec/requests/flows/revoke_token_spec.rb +0 -143
- data/spec/requests/flows/skip_authorization_spec.rb +0 -59
- data/spec/requests/protected_resources/metal_spec.rb +0 -14
- data/spec/requests/protected_resources/private_api_spec.rb +0 -81
- data/spec/routing/custom_controller_routes_spec.rb +0 -71
- data/spec/routing/default_routes_spec.rb +0 -35
- data/spec/routing/scoped_routes_spec.rb +0 -31
- data/spec/spec_helper.rb +0 -2
- data/spec/spec_helper_integration.rb +0 -56
- data/spec/support/dependencies/factory_girl.rb +0 -2
- data/spec/support/helpers/access_token_request_helper.rb +0 -11
- data/spec/support/helpers/authorization_request_helper.rb +0 -41
- data/spec/support/helpers/config_helper.rb +0 -9
- data/spec/support/helpers/model_helper.rb +0 -45
- data/spec/support/helpers/request_spec_helper.rb +0 -76
- data/spec/support/helpers/url_helper.rb +0 -55
- data/spec/support/orm/active_record.rb +0 -3
- data/spec/support/shared/controllers_shared_context.rb +0 -60
- data/spec/support/shared/models_shared_examples.rb +0 -52
- data/spec/validators/redirect_uri_validator_spec.rb +0 -78
@@ -1,142 +1,451 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Doorkeeper
|
2
4
|
module AccessTokenMixin
|
3
5
|
extend ActiveSupport::Concern
|
4
6
|
|
5
7
|
include OAuth::Helpers
|
6
8
|
include Models::Expirable
|
9
|
+
include Models::Reusable
|
7
10
|
include Models::Revocable
|
8
11
|
include Models::Accessible
|
12
|
+
include Models::Orderable
|
13
|
+
include Models::SecretStorable
|
9
14
|
include Models::Scopes
|
10
|
-
include
|
11
|
-
|
12
|
-
included do
|
13
|
-
belongs_to :application,
|
14
|
-
class_name: 'Doorkeeper::Application',
|
15
|
-
inverse_of: :access_tokens
|
15
|
+
include Models::ResourceOwnerable
|
16
|
+
include Models::ExpirationTimeSqlMath
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
module ClassMethods
|
19
|
+
# Returns an instance of the Doorkeeper::AccessToken with
|
20
|
+
# specific plain text token value.
|
21
|
+
#
|
22
|
+
# @param token [#to_s]
|
23
|
+
# Plain text token value (any object that responds to `#to_s`)
|
24
|
+
#
|
25
|
+
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
|
26
|
+
# if there is no record with such token
|
27
|
+
#
|
28
|
+
def by_token(token)
|
29
|
+
find_by_plaintext_token(:token, token)
|
30
|
+
end
|
21
31
|
|
22
|
-
|
23
|
-
|
24
|
-
|
32
|
+
# Returns an instance of the Doorkeeper::AccessToken
|
33
|
+
# with specific token value.
|
34
|
+
#
|
35
|
+
# @param refresh_token [#to_s]
|
36
|
+
# refresh token value (any object that responds to `#to_s`)
|
37
|
+
#
|
38
|
+
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
|
39
|
+
# if there is no record with such refresh token
|
40
|
+
#
|
41
|
+
def by_refresh_token(refresh_token)
|
42
|
+
find_by_plaintext_token(:refresh_token, refresh_token)
|
25
43
|
end
|
26
44
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
45
|
+
# Returns an instance of the Doorkeeper::AccessToken
|
46
|
+
# found by previous refresh token. Keep in mind that value
|
47
|
+
# of the previous_refresh_token isn't encrypted using
|
48
|
+
# secrets strategy.
|
49
|
+
#
|
50
|
+
# @param previous_refresh_token [#to_s]
|
51
|
+
# previous refresh token value (any object that responds to `#to_s`)
|
52
|
+
#
|
53
|
+
# @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
|
54
|
+
# if there is no record with such refresh token
|
55
|
+
#
|
56
|
+
def by_previous_refresh_token(previous_refresh_token)
|
57
|
+
find_by(refresh_token: previous_refresh_token)
|
58
|
+
end
|
32
59
|
|
33
|
-
|
34
|
-
|
35
|
-
|
60
|
+
# Revokes AccessToken records that have not been revoked and associated
|
61
|
+
# with the specific Application and Resource Owner.
|
62
|
+
#
|
63
|
+
# @param application_id [Integer]
|
64
|
+
# ID of the Application
|
65
|
+
# @param resource_owner [ActiveRecord::Base, Integer]
|
66
|
+
# instance of the Resource Owner model or it's ID
|
67
|
+
#
|
68
|
+
def revoke_all_for(application_id, resource_owner, clock = Time)
|
69
|
+
by_resource_owner(resource_owner)
|
70
|
+
.where(
|
71
|
+
application_id: application_id,
|
72
|
+
revoked_at: nil,
|
73
|
+
)
|
74
|
+
.update_all(revoked_at: clock.now.utc)
|
36
75
|
end
|
37
76
|
|
38
|
-
|
39
|
-
|
77
|
+
# Looking for not revoked Access Token with a matching set of scopes
|
78
|
+
# that belongs to specific Application and Resource Owner.
|
79
|
+
#
|
80
|
+
# @param application [Doorkeeper::Application]
|
81
|
+
# Application instance
|
82
|
+
# @param resource_owner [ActiveRecord::Base, Integer]
|
83
|
+
# Resource Owner model instance or it's ID
|
84
|
+
# @param scopes [String, Doorkeeper::OAuth::Scopes]
|
85
|
+
# set of scopes
|
86
|
+
#
|
87
|
+
# @return [Doorkeeper::AccessToken, nil] Access Token instance or
|
88
|
+
# nil if matching record was not found
|
89
|
+
#
|
90
|
+
def matching_token_for(application, resource_owner, scopes)
|
91
|
+
tokens = authorized_tokens_for(application&.id, resource_owner).not_expired
|
92
|
+
find_matching_token(tokens, application, scopes)
|
40
93
|
end
|
41
94
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
95
|
+
# Interface to enumerate access token records in batches in order not
|
96
|
+
# to bloat the memory. Could be overloaded in any ORM extension.
|
97
|
+
#
|
98
|
+
def find_access_token_in_batches(relation, **args, &block)
|
99
|
+
relation.find_in_batches(**args, &block)
|
47
100
|
end
|
48
101
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
102
|
+
# Enumerates AccessToken records in batches to find a matching token.
|
103
|
+
# Batching is required in order not to pollute the memory if Application
|
104
|
+
# has huge amount of associated records.
|
105
|
+
#
|
106
|
+
# ActiveRecord 5.x - 6.x ignores custom ordering so we can't perform a
|
107
|
+
# database sort by created_at, so we need to load all the matching records,
|
108
|
+
# sort them and find latest one.
|
109
|
+
#
|
110
|
+
# @param relation [ActiveRecord::Relation]
|
111
|
+
# Access tokens relation
|
112
|
+
# @param application [Doorkeeper::Application]
|
113
|
+
# Application instance
|
114
|
+
# @param scopes [String, Doorkeeper::OAuth::Scopes]
|
115
|
+
# set of scopes
|
116
|
+
#
|
117
|
+
# @return [Doorkeeper::AccessToken, nil] Access Token instance or
|
118
|
+
# nil if matching record was not found
|
119
|
+
#
|
120
|
+
def find_matching_token(relation, application, scopes)
|
121
|
+
return nil unless relation
|
122
|
+
|
123
|
+
matching_tokens = []
|
124
|
+
batch_size = Doorkeeper.configuration.token_lookup_batch_size
|
125
|
+
|
126
|
+
find_access_token_in_batches(relation, batch_size: batch_size) do |batch|
|
127
|
+
tokens = batch.select do |token|
|
128
|
+
scopes_match?(token.scopes, scopes, application&.scopes)
|
129
|
+
end
|
130
|
+
|
131
|
+
matching_tokens.concat(tokens)
|
58
132
|
end
|
133
|
+
|
134
|
+
matching_tokens.max_by(&:created_at)
|
59
135
|
end
|
60
136
|
|
137
|
+
# Checks whether the token scopes match the scopes from the parameters
|
138
|
+
#
|
139
|
+
# @param token_scopes [#to_s]
|
140
|
+
# set of scopes (any object that responds to `#to_s`)
|
141
|
+
# @param param_scopes [Doorkeeper::OAuth::Scopes]
|
142
|
+
# scopes from params
|
143
|
+
# @param app_scopes [Doorkeeper::OAuth::Scopes]
|
144
|
+
# Application scopes
|
145
|
+
#
|
146
|
+
# @return [Boolean] true if the param scopes match the token scopes,
|
147
|
+
# and all the param scopes are defined in the application (or in the
|
148
|
+
# server configuration if the application doesn't define any scopes),
|
149
|
+
# and false in other cases
|
150
|
+
#
|
61
151
|
def scopes_match?(token_scopes, param_scopes, app_scopes)
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
152
|
+
return true if token_scopes.empty? && param_scopes.empty?
|
153
|
+
|
154
|
+
(token_scopes.sort == param_scopes.sort) &&
|
155
|
+
Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
|
156
|
+
scope_str: param_scopes.to_s,
|
157
|
+
server_scopes: Doorkeeper.config.scopes,
|
158
|
+
app_scopes: app_scopes,
|
67
159
|
)
|
68
160
|
end
|
69
161
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
162
|
+
# Looking for not expired AccessToken record with a matching set of
|
163
|
+
# scopes that belongs to specific Application and Resource Owner.
|
164
|
+
# If it doesn't exists - then creates it.
|
165
|
+
#
|
166
|
+
# @param application [Doorkeeper::Application]
|
167
|
+
# Application instance
|
168
|
+
# @param resource_owner [ActiveRecord::Base, Integer]
|
169
|
+
# Resource Owner model instance or it's ID
|
170
|
+
# @param scopes [#to_s]
|
171
|
+
# set of scopes (any object that responds to `#to_s`)
|
172
|
+
# @param token_attributes [Hash]
|
173
|
+
# Additional attributes to use when creating a token
|
174
|
+
# @option token_attributes [Integer] :expires_in
|
175
|
+
# token lifetime in seconds
|
176
|
+
# @option token_attributes [Boolean] :use_refresh_token
|
177
|
+
# whether to use the refresh token
|
178
|
+
#
|
179
|
+
# @return [Doorkeeper::AccessToken] existing record or a new one
|
180
|
+
#
|
181
|
+
def find_or_create_for(application:, resource_owner:, scopes:, **token_attributes)
|
182
|
+
if Doorkeeper.config.reuse_access_token
|
183
|
+
access_token = matching_token_for(application, resource_owner, scopes)
|
184
|
+
|
185
|
+
return access_token if access_token&.reusable?
|
76
186
|
end
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
187
|
+
|
188
|
+
create_for(
|
189
|
+
application: application,
|
190
|
+
resource_owner: resource_owner,
|
191
|
+
scopes: scopes,
|
192
|
+
**token_attributes,
|
193
|
+
)
|
194
|
+
end
|
195
|
+
|
196
|
+
# Creates a not expired AccessToken record with a matching set of
|
197
|
+
# scopes that belongs to specific Application and Resource Owner.
|
198
|
+
#
|
199
|
+
# @param application [Doorkeeper::Application]
|
200
|
+
# Application instance
|
201
|
+
# @param resource_owner [ActiveRecord::Base, Integer]
|
202
|
+
# Resource Owner model instance or it's ID
|
203
|
+
# @param scopes [#to_s]
|
204
|
+
# set of scopes (any object that responds to `#to_s`)
|
205
|
+
# @param token_attributes [Hash]
|
206
|
+
# Additional attributes to use when creating a token
|
207
|
+
# @option token_attributes [Integer] :expires_in
|
208
|
+
# token lifetime in seconds
|
209
|
+
# @option token_attributes [Boolean] :use_refresh_token
|
210
|
+
# whether to use the refresh token
|
211
|
+
#
|
212
|
+
# @return [Doorkeeper::AccessToken] new access token
|
213
|
+
#
|
214
|
+
def create_for(application:, resource_owner:, scopes:, **token_attributes)
|
215
|
+
token_attributes[:application] = application
|
216
|
+
token_attributes[:scopes] = scopes.to_s
|
217
|
+
|
218
|
+
if Doorkeeper.config.polymorphic_resource_owner?
|
219
|
+
token_attributes[:resource_owner] = resource_owner
|
220
|
+
else
|
221
|
+
token_attributes[:resource_owner_id] = resource_owner_id_for(resource_owner)
|
222
|
+
end
|
223
|
+
|
224
|
+
create!(token_attributes)
|
225
|
+
end
|
226
|
+
|
227
|
+
# Looking for not revoked Access Token records that belongs to specific
|
228
|
+
# Application and Resource Owner.
|
229
|
+
#
|
230
|
+
# @param application_id [Integer]
|
231
|
+
# ID of the Application model instance
|
232
|
+
# @param resource_owner [ActiveRecord::Base, Integer]
|
233
|
+
# Resource Owner model instance or it's ID
|
234
|
+
#
|
235
|
+
# @return [ActiveRecord::Relation]
|
236
|
+
# collection of matching AccessToken objects
|
237
|
+
#
|
238
|
+
def authorized_tokens_for(application_id, resource_owner)
|
239
|
+
by_resource_owner(resource_owner).where(
|
240
|
+
application_id: application_id,
|
241
|
+
revoked_at: nil,
|
83
242
|
)
|
84
243
|
end
|
85
244
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
245
|
+
# Convenience method for backwards-compatibility, return the last
|
246
|
+
# matching token for the given Application and Resource Owner.
|
247
|
+
#
|
248
|
+
# @param application_id [Integer]
|
249
|
+
# ID of the Application model instance
|
250
|
+
# @param resource_owner [ActiveRecord::Base, Integer]
|
251
|
+
# ID of the Resource Owner model instance
|
252
|
+
#
|
253
|
+
# @return [Doorkeeper::AccessToken, nil] matching AccessToken object or
|
254
|
+
# nil if nothing was found
|
255
|
+
#
|
256
|
+
def last_authorized_token_for(application_id, resource_owner)
|
257
|
+
authorized_tokens_for(application_id, resource_owner)
|
258
|
+
.ordered_by(:created_at, :desc)
|
259
|
+
.first
|
260
|
+
end
|
261
|
+
|
262
|
+
##
|
263
|
+
# Determines the secret storing transformer
|
264
|
+
# Unless configured otherwise, uses the plain secret strategy
|
265
|
+
#
|
266
|
+
# @return [Doorkeeper::SecretStoring::Base]
|
267
|
+
#
|
268
|
+
def secret_strategy
|
269
|
+
::Doorkeeper.config.token_secret_strategy
|
270
|
+
end
|
271
|
+
|
272
|
+
##
|
273
|
+
# Determine the fallback storing strategy
|
274
|
+
# Unless configured, there will be no fallback
|
275
|
+
def fallback_secret_strategy
|
276
|
+
::Doorkeeper.config.token_secret_fallback_strategy
|
94
277
|
end
|
95
278
|
end
|
96
279
|
|
280
|
+
# Access Token type: Bearer.
|
281
|
+
# @see https://datatracker.ietf.org/doc/html/rfc6750
|
282
|
+
# The OAuth 2.0 Authorization Framework: Bearer Token Usage
|
283
|
+
#
|
97
284
|
def token_type
|
98
|
-
|
285
|
+
"Bearer"
|
99
286
|
end
|
100
287
|
|
101
288
|
def use_refresh_token?
|
289
|
+
@use_refresh_token ||= false
|
102
290
|
!!@use_refresh_token
|
103
291
|
end
|
104
292
|
|
293
|
+
# JSON representation of the Access Token instance.
|
294
|
+
#
|
295
|
+
# @return [Hash] hash with token data
|
105
296
|
def as_json(_options = {})
|
106
297
|
{
|
107
|
-
resource_owner_id:
|
108
|
-
|
109
|
-
|
110
|
-
application:
|
111
|
-
created_at:
|
112
|
-
}
|
298
|
+
resource_owner_id: resource_owner_id,
|
299
|
+
scope: scopes,
|
300
|
+
expires_in: expires_in_seconds,
|
301
|
+
application: { uid: application.try(:uid) },
|
302
|
+
created_at: created_at.to_i,
|
303
|
+
}.tap do |json|
|
304
|
+
if Doorkeeper.configuration.polymorphic_resource_owner?
|
305
|
+
json[:resource_owner_type] = resource_owner_type
|
306
|
+
end
|
307
|
+
end
|
113
308
|
end
|
114
309
|
|
115
|
-
#
|
310
|
+
# Indicates whether the token instance have the same credential
|
311
|
+
# as the other Access Token.
|
312
|
+
#
|
313
|
+
# @param access_token [Doorkeeper::AccessToken] other token
|
314
|
+
#
|
315
|
+
# @return [Boolean] true if credentials are same of false in other cases
|
316
|
+
#
|
116
317
|
def same_credential?(access_token)
|
117
318
|
application_id == access_token.application_id &&
|
319
|
+
same_resource_owner?(access_token)
|
320
|
+
end
|
321
|
+
|
322
|
+
# Indicates whether the token instance have the same credential
|
323
|
+
# as the other Access Token.
|
324
|
+
#
|
325
|
+
# @param access_token [Doorkeeper::AccessToken] other token
|
326
|
+
#
|
327
|
+
# @return [Boolean] true if credentials are same of false in other cases
|
328
|
+
#
|
329
|
+
def same_resource_owner?(access_token)
|
330
|
+
if Doorkeeper.configuration.polymorphic_resource_owner?
|
331
|
+
resource_owner == access_token.resource_owner
|
332
|
+
else
|
118
333
|
resource_owner_id == access_token.resource_owner_id
|
334
|
+
end
|
119
335
|
end
|
120
336
|
|
337
|
+
# Indicates if token is acceptable for specific scopes.
|
338
|
+
#
|
339
|
+
# @param scopes [Array<String>] scopes
|
340
|
+
#
|
341
|
+
# @return [Boolean] true if record is accessible and includes scopes or
|
342
|
+
# false in other cases
|
343
|
+
#
|
121
344
|
def acceptable?(scopes)
|
122
345
|
accessible? && includes_scope?(*scopes)
|
123
346
|
end
|
124
347
|
|
348
|
+
# We keep a volatile copy of the raw refresh token for initial communication
|
349
|
+
# The stored refresh_token may be mapped and not available in cleartext.
|
350
|
+
def plaintext_refresh_token
|
351
|
+
if secret_strategy.allows_restoring_secrets?
|
352
|
+
secret_strategy.restore_secret(self, :refresh_token)
|
353
|
+
else
|
354
|
+
@raw_refresh_token
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
# We keep a volatile copy of the raw token for initial communication
|
359
|
+
# The stored refresh_token may be mapped and not available in cleartext.
|
360
|
+
#
|
361
|
+
# Some strategies allow restoring stored secrets (e.g. symmetric encryption)
|
362
|
+
# while hashing strategies do not, so you cannot rely on this value
|
363
|
+
# returning a present value for persisted tokens.
|
364
|
+
def plaintext_token
|
365
|
+
if secret_strategy.allows_restoring_secrets?
|
366
|
+
secret_strategy.restore_secret(self, :token)
|
367
|
+
else
|
368
|
+
@raw_token
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
# Revokes token with `:refresh_token` equal to `:previous_refresh_token`
|
373
|
+
# and clears `:previous_refresh_token` attribute.
|
374
|
+
#
|
375
|
+
def revoke_previous_refresh_token!
|
376
|
+
return if !self.class.refresh_token_revoked_on_use? || previous_refresh_token.blank?
|
377
|
+
|
378
|
+
old_refresh_token&.revoke
|
379
|
+
update_attribute(:previous_refresh_token, "")
|
380
|
+
end
|
381
|
+
|
125
382
|
private
|
126
383
|
|
384
|
+
# Searches for Access Token record with `:refresh_token` equal to
|
385
|
+
# `:previous_refresh_token` value.
|
386
|
+
#
|
387
|
+
# @return [Doorkeeper::AccessToken, nil]
|
388
|
+
# Access Token record or nil if nothing found
|
389
|
+
#
|
390
|
+
def old_refresh_token
|
391
|
+
@old_refresh_token ||= self.class.by_previous_refresh_token(previous_refresh_token)
|
392
|
+
end
|
393
|
+
|
394
|
+
# Generates refresh token with UniqueToken generator.
|
395
|
+
#
|
396
|
+
# @return [String] refresh token value
|
397
|
+
#
|
127
398
|
def generate_refresh_token
|
128
|
-
|
399
|
+
@raw_refresh_token = UniqueToken.generate
|
400
|
+
secret_strategy.store_secret(self, :refresh_token, @raw_refresh_token)
|
129
401
|
end
|
130
402
|
|
403
|
+
# Generates and sets the token value with the
|
404
|
+
# configured Generator class (see Doorkeeper.config).
|
405
|
+
#
|
406
|
+
# @return [String] generated token value
|
407
|
+
#
|
408
|
+
# @raise [Doorkeeper::Errors::UnableToGenerateToken]
|
409
|
+
# custom class doesn't implement .generate method
|
410
|
+
# @raise [Doorkeeper::Errors::TokenGeneratorNotFound]
|
411
|
+
# custom class doesn't exist
|
412
|
+
#
|
131
413
|
def generate_token
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
414
|
+
self.created_at ||= Time.now.utc
|
415
|
+
|
416
|
+
@raw_token = token_generator.generate(attributes_for_token_generator)
|
417
|
+
secret_strategy.store_secret(self, :token, @raw_token)
|
418
|
+
@raw_token
|
419
|
+
end
|
420
|
+
|
421
|
+
# Set of attributes that would be passed to token generator to
|
422
|
+
# generate unique token based on them.
|
423
|
+
#
|
424
|
+
# @return [Hash] set of attributes
|
425
|
+
#
|
426
|
+
def attributes_for_token_generator
|
427
|
+
{
|
428
|
+
resource_owner_id: resource_owner_id,
|
429
|
+
scopes: scopes,
|
430
|
+
application: application,
|
431
|
+
expires_in: expires_in,
|
432
|
+
created_at: created_at,
|
433
|
+
}.tap do |attributes|
|
434
|
+
if Doorkeeper.config.polymorphic_resource_owner?
|
435
|
+
attributes[:resource_owner] = resource_owner
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
def token_generator
|
441
|
+
generator_name = Doorkeeper.config.access_token_generator
|
442
|
+
generator = generator_name.constantize
|
443
|
+
|
444
|
+
return generator if generator.respond_to?(:generate)
|
445
|
+
|
137
446
|
raise Errors::UnableToGenerateToken, "#{generator} does not respond to `.generate`."
|
138
447
|
rescue NameError
|
139
|
-
raise Errors::TokenGeneratorNotFound, "#{
|
448
|
+
raise Errors::TokenGeneratorNotFound, "#{generator_name} not found"
|
140
449
|
end
|
141
450
|
end
|
142
451
|
end
|
@@ -1,52 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Doorkeeper
|
2
4
|
module ApplicationMixin
|
3
5
|
extend ActiveSupport::Concern
|
4
6
|
|
5
7
|
include OAuth::Helpers
|
8
|
+
include Models::Orderable
|
9
|
+
include Models::SecretStorable
|
6
10
|
include Models::Scopes
|
7
|
-
include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
|
8
|
-
|
9
|
-
included do
|
10
|
-
has_many :access_grants, dependent: :destroy, class_name: 'Doorkeeper::AccessGrant'
|
11
|
-
has_many :access_tokens, dependent: :destroy, class_name: 'Doorkeeper::AccessToken'
|
12
|
-
|
13
|
-
validates :name, :secret, :uid, presence: true
|
14
|
-
validates :uid, uniqueness: true
|
15
|
-
validates :redirect_uri, redirect_uri: true
|
16
|
-
|
17
|
-
before_validation :generate_uid, :generate_secret, on: :create
|
18
|
-
|
19
|
-
if respond_to?(:attr_accessible)
|
20
|
-
attr_accessible :name, :redirect_uri, :scopes
|
21
|
-
end
|
22
|
-
end
|
23
11
|
|
12
|
+
# :nodoc
|
24
13
|
module ClassMethods
|
14
|
+
# Returns an instance of the Doorkeeper::Application with
|
15
|
+
# specific UID and secret.
|
16
|
+
#
|
17
|
+
# Public/Non-confidential applications will only find by uid if secret is
|
18
|
+
# blank.
|
19
|
+
#
|
20
|
+
# @param uid [#to_s] UID (any object that responds to `#to_s`)
|
21
|
+
# @param secret [#to_s] secret (any object that responds to `#to_s`)
|
22
|
+
#
|
23
|
+
# @return [Doorkeeper::Application, nil]
|
24
|
+
# Application instance or nil if there is no record with such credentials
|
25
|
+
#
|
25
26
|
def by_uid_and_secret(uid, secret)
|
26
|
-
|
27
|
+
app = by_uid(uid)
|
28
|
+
return unless app
|
29
|
+
return app if secret.blank? && !app.confidential?
|
30
|
+
return unless app.secret_matches?(secret)
|
31
|
+
|
32
|
+
app
|
27
33
|
end
|
28
34
|
|
35
|
+
# Returns an instance of the Doorkeeper::Application with specific UID.
|
36
|
+
#
|
37
|
+
# @param uid [#to_s] UID (any object that responds to `#to_s`)
|
38
|
+
#
|
39
|
+
# @return [Doorkeeper::Application, nil] Application instance or nil
|
40
|
+
# if there is no record with such UID
|
41
|
+
#
|
29
42
|
def by_uid(uid)
|
30
|
-
|
43
|
+
find_by(uid: uid.to_s)
|
31
44
|
end
|
32
|
-
end
|
33
45
|
|
34
|
-
|
46
|
+
##
|
47
|
+
# Determines the secret storing transformer
|
48
|
+
# Unless configured otherwise, uses the plain secret strategy
|
49
|
+
def secret_strategy
|
50
|
+
::Doorkeeper.config.application_secret_strategy
|
51
|
+
end
|
35
52
|
|
36
|
-
|
37
|
-
|
38
|
-
|
53
|
+
##
|
54
|
+
# Determine the fallback storing strategy
|
55
|
+
# Unless configured, there will be no fallback
|
56
|
+
def fallback_secret_strategy
|
57
|
+
::Doorkeeper.config.application_secret_fallback_strategy
|
58
|
+
end
|
39
59
|
end
|
40
60
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
61
|
+
# Set an application's valid redirect URIs.
|
62
|
+
#
|
63
|
+
# @param uris [String, Array<String>] Newline-separated string or array the URI(s)
|
64
|
+
#
|
65
|
+
# @return [String] The redirect URI(s) separated by newlines.
|
66
|
+
#
|
67
|
+
def redirect_uri=(uris)
|
68
|
+
super(uris.is_a?(Array) ? uris.join("\n") : uris)
|
45
69
|
end
|
46
70
|
|
47
|
-
|
48
|
-
|
49
|
-
|
71
|
+
# Check whether the given plain text secret matches our stored secret
|
72
|
+
#
|
73
|
+
# @param input [#to_s] Plain secret provided by user
|
74
|
+
# (any object that responds to `#to_s`)
|
75
|
+
#
|
76
|
+
# @return [Boolean] Whether the given secret matches the stored secret
|
77
|
+
# of this application.
|
78
|
+
#
|
79
|
+
def secret_matches?(input)
|
80
|
+
# return false if either is nil, since secure_compare depends on strings
|
81
|
+
# but Application secrets MAY be nil depending on confidentiality.
|
82
|
+
return false if input.nil? || secret.nil?
|
83
|
+
|
84
|
+
# When matching the secret by comparer function, all is well.
|
85
|
+
return true if secret_strategy.secret_matches?(input, secret)
|
86
|
+
|
87
|
+
# When fallback lookup is enabled, ensure applications
|
88
|
+
# with plain secrets can still be found
|
89
|
+
if fallback_secret_strategy
|
90
|
+
fallback_secret_strategy.secret_matches?(input, secret)
|
91
|
+
else
|
92
|
+
false
|
50
93
|
end
|
51
94
|
end
|
52
95
|
end
|
@@ -1,6 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Doorkeeper
|
2
4
|
module Models
|
3
5
|
module Accessible
|
6
|
+
# Indicates whether the object is accessible (not expired and not revoked).
|
7
|
+
#
|
8
|
+
# @return [Boolean] true if object accessible or false in other case
|
9
|
+
#
|
4
10
|
def accessible?
|
5
11
|
!expired? && !revoked?
|
6
12
|
end
|