super-smart-kit 0.0.1

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.
Files changed (149) hide show
  1. checksums.yaml +7 -0
  2. data/doorkeeper-5.9.3/CHANGELOG.md +1208 -0
  3. data/doorkeeper-5.9.3/MIT-LICENSE +20 -0
  4. data/doorkeeper-5.9.3/README.md +186 -0
  5. data/doorkeeper-5.9.3/app/assets/stylesheets/doorkeeper/admin/application.css +10 -0
  6. data/doorkeeper-5.9.3/app/assets/stylesheets/doorkeeper/application.css +64 -0
  7. data/doorkeeper-5.9.3/app/controllers/doorkeeper/application_controller.rb +14 -0
  8. data/doorkeeper-5.9.3/app/controllers/doorkeeper/application_metal_controller.rb +13 -0
  9. data/doorkeeper-5.9.3/app/controllers/doorkeeper/applications_controller.rb +99 -0
  10. data/doorkeeper-5.9.3/app/controllers/doorkeeper/authorizations_controller.rb +164 -0
  11. data/doorkeeper-5.9.3/app/controllers/doorkeeper/authorized_applications_controller.rb +33 -0
  12. data/doorkeeper-5.9.3/app/controllers/doorkeeper/token_info_controller.rb +25 -0
  13. data/doorkeeper-5.9.3/app/controllers/doorkeeper/tokens_controller.rb +180 -0
  14. data/doorkeeper-5.9.3/app/helpers/doorkeeper/dashboard_helper.rb +21 -0
  15. data/doorkeeper-5.9.3/app/views/doorkeeper/applications/_delete_form.html.erb +6 -0
  16. data/doorkeeper-5.9.3/app/views/doorkeeper/applications/_form.html.erb +59 -0
  17. data/doorkeeper-5.9.3/app/views/doorkeeper/applications/edit.html.erb +5 -0
  18. data/doorkeeper-5.9.3/app/views/doorkeeper/applications/index.html.erb +38 -0
  19. data/doorkeeper-5.9.3/app/views/doorkeeper/applications/new.html.erb +5 -0
  20. data/doorkeeper-5.9.3/app/views/doorkeeper/applications/show.html.erb +63 -0
  21. data/doorkeeper-5.9.3/app/views/doorkeeper/authorizations/error.html.erb +9 -0
  22. data/doorkeeper-5.9.3/app/views/doorkeeper/authorizations/form_post.html.erb +15 -0
  23. data/doorkeeper-5.9.3/app/views/doorkeeper/authorizations/new.html.erb +46 -0
  24. data/doorkeeper-5.9.3/app/views/doorkeeper/authorizations/show.html.erb +7 -0
  25. data/doorkeeper-5.9.3/app/views/doorkeeper/authorized_applications/_delete_form.html.erb +4 -0
  26. data/doorkeeper-5.9.3/app/views/doorkeeper/authorized_applications/index.html.erb +24 -0
  27. data/doorkeeper-5.9.3/app/views/layouts/doorkeeper/admin.html.erb +39 -0
  28. data/doorkeeper-5.9.3/app/views/layouts/doorkeeper/application.html.erb +23 -0
  29. data/doorkeeper-5.9.3/config/locales/en.yml +155 -0
  30. data/doorkeeper-5.9.3/lib/doorkeeper/config/abstract_builder.rb +28 -0
  31. data/doorkeeper-5.9.3/lib/doorkeeper/config/option.rb +82 -0
  32. data/doorkeeper-5.9.3/lib/doorkeeper/config/validations.rb +65 -0
  33. data/doorkeeper-5.9.3/lib/doorkeeper/config.rb +721 -0
  34. data/doorkeeper-5.9.3/lib/doorkeeper/engine.rb +38 -0
  35. data/doorkeeper-5.9.3/lib/doorkeeper/errors.rb +85 -0
  36. data/doorkeeper-5.9.3/lib/doorkeeper/grant_flow/fallback_flow.rb +15 -0
  37. data/doorkeeper-5.9.3/lib/doorkeeper/grant_flow/flow.rb +44 -0
  38. data/doorkeeper-5.9.3/lib/doorkeeper/grant_flow/registry.rb +50 -0
  39. data/doorkeeper-5.9.3/lib/doorkeeper/grant_flow.rb +45 -0
  40. data/doorkeeper-5.9.3/lib/doorkeeper/grape/authorization_decorator.rb +19 -0
  41. data/doorkeeper-5.9.3/lib/doorkeeper/grape/helpers.rb +58 -0
  42. data/doorkeeper-5.9.3/lib/doorkeeper/helpers/controller.rb +91 -0
  43. data/doorkeeper-5.9.3/lib/doorkeeper/models/access_grant_mixin.rb +124 -0
  44. data/doorkeeper-5.9.3/lib/doorkeeper/models/access_token_mixin.rb +526 -0
  45. data/doorkeeper-5.9.3/lib/doorkeeper/models/application_mixin.rb +96 -0
  46. data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/accessible.rb +15 -0
  47. data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/expirable.rb +36 -0
  48. data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/expiration_time_sql_math.rb +96 -0
  49. data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/orderable.rb +15 -0
  50. data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/ownership.rb +18 -0
  51. data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/polymorphic_resource_owner.rb +29 -0
  52. data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/resource_ownerable.rb +47 -0
  53. data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/reusable.rb +19 -0
  54. data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/revocable.rb +31 -0
  55. data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/scopes.rb +27 -0
  56. data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/secret_storable.rb +106 -0
  57. data/doorkeeper-5.9.3/lib/doorkeeper/models/concerns/write_to_primary.rb +57 -0
  58. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/authorization/code.rb +76 -0
  59. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/authorization/context.rb +17 -0
  60. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/authorization/token.rb +100 -0
  61. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/authorization/uri_builder.rb +33 -0
  62. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/authorization_code_request.rb +125 -0
  63. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/base_request.rb +68 -0
  64. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/base_response.rb +31 -0
  65. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/client/credentials.rb +34 -0
  66. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/client.rb +28 -0
  67. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/client_credentials/creator.rb +57 -0
  68. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/client_credentials/issuer.rb +48 -0
  69. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/client_credentials/validator.rb +55 -0
  70. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/client_credentials_request.rb +46 -0
  71. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/code_request.rb +25 -0
  72. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/code_response.rb +51 -0
  73. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/error.rb +16 -0
  74. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/error_response.rb +121 -0
  75. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/forbidden_token_response.rb +31 -0
  76. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/helpers/scope_checker.rb +50 -0
  77. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/helpers/unique_token.rb +30 -0
  78. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/helpers/uri_checker.rb +83 -0
  79. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/hooks/context.rb +21 -0
  80. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/invalid_request_response.rb +47 -0
  81. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/invalid_token_response.rb +54 -0
  82. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/nonstandard.rb +39 -0
  83. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/password_access_token_request.rb +75 -0
  84. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/pre_authorization.rb +187 -0
  85. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/refresh_token_request.rb +144 -0
  86. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/scopes.rb +134 -0
  87. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/token.rb +66 -0
  88. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/token_introspection.rb +220 -0
  89. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/token_request.rb +25 -0
  90. data/doorkeeper-5.9.3/lib/doorkeeper/oauth/token_response.rb +38 -0
  91. data/doorkeeper-5.9.3/lib/doorkeeper/oauth.rb +13 -0
  92. data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/access_grant.rb +9 -0
  93. data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/access_token.rb +9 -0
  94. data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/application.rb +10 -0
  95. data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +64 -0
  96. data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/mixins/access_token.rb +95 -0
  97. data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/mixins/application.rb +223 -0
  98. data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +66 -0
  99. data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +44 -0
  100. data/doorkeeper-5.9.3/lib/doorkeeper/orm/active_record.rb +41 -0
  101. data/doorkeeper-5.9.3/lib/doorkeeper/rails/helpers.rb +82 -0
  102. data/doorkeeper-5.9.3/lib/doorkeeper/rails/routes/abstract_router.rb +35 -0
  103. data/doorkeeper-5.9.3/lib/doorkeeper/rails/routes/mapper.rb +30 -0
  104. data/doorkeeper-5.9.3/lib/doorkeeper/rails/routes/mapping.rb +40 -0
  105. data/doorkeeper-5.9.3/lib/doorkeeper/rails/routes/registry.rb +45 -0
  106. data/doorkeeper-5.9.3/lib/doorkeeper/rails/routes.rb +110 -0
  107. data/doorkeeper-5.9.3/lib/doorkeeper/rake/db.rake +40 -0
  108. data/doorkeeper-5.9.3/lib/doorkeeper/rake/setup.rake +6 -0
  109. data/doorkeeper-5.9.3/lib/doorkeeper/rake.rb +14 -0
  110. data/doorkeeper-5.9.3/lib/doorkeeper/request/authorization_code.rb +26 -0
  111. data/doorkeeper-5.9.3/lib/doorkeeper/request/client_credentials.rb +17 -0
  112. data/doorkeeper-5.9.3/lib/doorkeeper/request/code.rb +17 -0
  113. data/doorkeeper-5.9.3/lib/doorkeeper/request/password.rb +19 -0
  114. data/doorkeeper-5.9.3/lib/doorkeeper/request/refresh_token.rb +22 -0
  115. data/doorkeeper-5.9.3/lib/doorkeeper/request/strategy.rb +19 -0
  116. data/doorkeeper-5.9.3/lib/doorkeeper/request/token.rb +17 -0
  117. data/doorkeeper-5.9.3/lib/doorkeeper/request.rb +73 -0
  118. data/doorkeeper-5.9.3/lib/doorkeeper/revocable_tokens/revocable_access_token.rb +21 -0
  119. data/doorkeeper-5.9.3/lib/doorkeeper/revocable_tokens/revocable_refresh_token.rb +21 -0
  120. data/doorkeeper-5.9.3/lib/doorkeeper/secret_storing/base.rb +64 -0
  121. data/doorkeeper-5.9.3/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
  122. data/doorkeeper-5.9.3/lib/doorkeeper/secret_storing/plain.rb +33 -0
  123. data/doorkeeper-5.9.3/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
  124. data/doorkeeper-5.9.3/lib/doorkeeper/server.rb +44 -0
  125. data/doorkeeper-5.9.3/lib/doorkeeper/stale_records_cleaner.rb +24 -0
  126. data/doorkeeper-5.9.3/lib/doorkeeper/validations.rb +33 -0
  127. data/doorkeeper-5.9.3/lib/doorkeeper/version.rb +14 -0
  128. data/doorkeeper-5.9.3/lib/doorkeeper.rb +209 -0
  129. data/doorkeeper-5.9.3/lib/generators/doorkeeper/application_owner_generator.rb +33 -0
  130. data/doorkeeper-5.9.3/lib/generators/doorkeeper/confidential_applications_generator.rb +33 -0
  131. data/doorkeeper-5.9.3/lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb +39 -0
  132. data/doorkeeper-5.9.3/lib/generators/doorkeeper/install_generator.rb +22 -0
  133. data/doorkeeper-5.9.3/lib/generators/doorkeeper/migration_generator.rb +32 -0
  134. data/doorkeeper-5.9.3/lib/generators/doorkeeper/pkce_generator.rb +33 -0
  135. data/doorkeeper-5.9.3/lib/generators/doorkeeper/previous_refresh_token_generator.rb +41 -0
  136. data/doorkeeper-5.9.3/lib/generators/doorkeeper/remove_applications_secret_not_null_constraint_generator.rb +33 -0
  137. data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/README +24 -0
  138. data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
  139. data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb +9 -0
  140. data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb +13 -0
  141. data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +8 -0
  142. data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb +17 -0
  143. data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/initializer.rb +544 -0
  144. data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/migration.rb.erb +99 -0
  145. data/doorkeeper-5.9.3/lib/generators/doorkeeper/templates/remove_applications_secret_not_null_constraint.rb.erb +7 -0
  146. data/doorkeeper-5.9.3/lib/generators/doorkeeper/views_generator.rb +18 -0
  147. data/doorkeeper-5.9.3/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +6 -0
  148. data/super-smart-kit.gemspec +12 -0
  149. metadata +188 -0
@@ -0,0 +1,526 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module AccessTokenMixin
5
+ extend ActiveSupport::Concern
6
+
7
+ include OAuth::Helpers
8
+ include Models::Expirable
9
+ include Models::Reusable
10
+ include Models::Revocable
11
+ include Models::Accessible
12
+ include Models::Orderable
13
+ include Models::SecretStorable
14
+ include Models::Scopes
15
+ include Models::ResourceOwnerable
16
+ include Models::ExpirationTimeSqlMath
17
+ include Models::Concerns::WriteToPrimary
18
+
19
+ module ClassMethods
20
+ # Returns an instance of the Doorkeeper::AccessToken with
21
+ # specific plain text token value.
22
+ #
23
+ # @param token [#to_s]
24
+ # Plain text token value (any object that responds to `#to_s`)
25
+ #
26
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
27
+ # if there is no record with such token
28
+ #
29
+ def by_token(token)
30
+ find_by_plaintext_token(:token, token)
31
+ end
32
+
33
+ # Returns an instance of the Doorkeeper::AccessToken
34
+ # with specific token value.
35
+ #
36
+ # @param refresh_token [#to_s]
37
+ # refresh token value (any object that responds to `#to_s`)
38
+ #
39
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
40
+ # if there is no record with such refresh token
41
+ #
42
+ def by_refresh_token(refresh_token)
43
+ find_by_plaintext_token(:refresh_token, refresh_token)
44
+ end
45
+
46
+ # Returns an instance of the Doorkeeper::AccessToken
47
+ # found by previous refresh token. Keep in mind that value
48
+ # of the previous_refresh_token isn't encrypted using
49
+ # secrets strategy.
50
+ #
51
+ # @param previous_refresh_token [#to_s]
52
+ # previous refresh token value (any object that responds to `#to_s`)
53
+ #
54
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
55
+ # if there is no record with such refresh token
56
+ #
57
+ def by_previous_refresh_token(previous_refresh_token)
58
+ find_by(refresh_token: previous_refresh_token)
59
+ end
60
+
61
+ # Revokes AccessToken records that have not been revoked and associated
62
+ # with the specific Application and Resource Owner.
63
+ #
64
+ # @param application_id [Integer]
65
+ # ID of the Application
66
+ # @param resource_owner [ActiveRecord::Base, Integer]
67
+ # instance of the Resource Owner model or it's ID
68
+ #
69
+ def revoke_all_for(application_id, resource_owner, clock = Time)
70
+ with_primary_role do
71
+ by_resource_owner(resource_owner)
72
+ .where(
73
+ application_id: application_id,
74
+ revoked_at: nil,
75
+ )
76
+ .update_all(revoked_at: clock.now.utc)
77
+ end
78
+ end
79
+
80
+ # Looking for not revoked Access Token with a matching set of scopes
81
+ # that belongs to specific Application and Resource Owner.
82
+ #
83
+ # @param application [Doorkeeper::Application]
84
+ # Application instance
85
+ # @param resource_owner [ActiveRecord::Base, Integer]
86
+ # Resource Owner model instance or it's ID
87
+ # @param scopes [String, Doorkeeper::OAuth::Scopes]
88
+ # set of scopes
89
+ # @param custom_attributes [Nilable Hash]
90
+ # A nil value, or hash with keys corresponding to the custom attributes
91
+ # configured with the `custom_access_token_attributes` config option.
92
+ # A nil value will ignore custom attributes.
93
+ #
94
+ # @return [Doorkeeper::AccessToken, nil] Access Token instance or
95
+ # nil if matching record was not found
96
+ #
97
+ def matching_token_for(application, resource_owner, scopes, custom_attributes: nil, include_expired: true)
98
+ tokens = authorized_tokens_for(application&.id, resource_owner)
99
+ tokens = tokens.not_expired unless include_expired
100
+ find_matching_token(tokens, application, custom_attributes, scopes)
101
+ end
102
+
103
+ # Interface to enumerate access token records in batches in order not
104
+ # to bloat the memory. Could be overloaded in any ORM extension.
105
+ #
106
+ def find_access_token_in_batches(relation, **args, &block)
107
+ relation.find_in_batches(**args, &block)
108
+ end
109
+
110
+ # Enumerates AccessToken records in batches to find a matching token.
111
+ # Batching is required in order not to pollute the memory if Application
112
+ # has huge amount of associated records.
113
+ #
114
+ # ActiveRecord 5.x - 6.x ignores custom ordering so we can't perform a
115
+ # database sort by created_at, so we need to load all the matching records,
116
+ # sort them and find latest one.
117
+ #
118
+ # @param relation [ActiveRecord::Relation]
119
+ # Access tokens relation
120
+ # @param application [Doorkeeper::Application]
121
+ # Application instance
122
+ # @param scopes [String, Doorkeeper::OAuth::Scopes]
123
+ # set of scopes
124
+ # @param custom_attributes [Nilable Hash]
125
+ # A nil value, or hash with keys corresponding to the custom attributes
126
+ # configured with the `custom_access_token_attributes` config option.
127
+ # A nil value will ignore custom attributes.
128
+ #
129
+ # @return [Doorkeeper::AccessToken, nil] Access Token instance or
130
+ # nil if matching record was not found
131
+ #
132
+ def find_matching_token(relation, application, custom_attributes, scopes)
133
+ return nil unless relation
134
+
135
+ matching_tokens = []
136
+ batch_size = Doorkeeper.configuration.token_lookup_batch_size
137
+
138
+ find_access_token_in_batches(relation, batch_size: batch_size) do |batch|
139
+ tokens = batch.select do |token|
140
+ scopes_match?(token.scopes, scopes, application&.scopes) &&
141
+ custom_attributes_match?(token, custom_attributes)
142
+ end
143
+
144
+ matching_tokens.concat(tokens)
145
+ end
146
+
147
+ matching_tokens.max_by(&:created_at)
148
+ end
149
+
150
+ # Checks whether the token scopes match the scopes from the parameters
151
+ #
152
+ # @param token_scopes [#to_s]
153
+ # set of scopes (any object that responds to `#to_s`)
154
+ # @param param_scopes [Doorkeeper::OAuth::Scopes]
155
+ # scopes from params
156
+ # @param app_scopes [Doorkeeper::OAuth::Scopes]
157
+ # Application scopes
158
+ #
159
+ # @return [Boolean] true if the param scopes match the token scopes,
160
+ # and all the param scopes are defined in the application (or in the
161
+ # server configuration if the application doesn't define any scopes),
162
+ # and false in other cases
163
+ #
164
+ def scopes_match?(token_scopes, param_scopes, app_scopes)
165
+ return true if token_scopes.empty? && param_scopes.empty?
166
+
167
+ (token_scopes.sort == param_scopes.sort) &&
168
+ Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
169
+ scope_str: param_scopes.to_s,
170
+ server_scopes: Doorkeeper.config.scopes,
171
+ app_scopes: app_scopes,
172
+ )
173
+ end
174
+
175
+ # Checks whether the token custom attribute values match the custom
176
+ # attributes from the parameters.
177
+ #
178
+ # @param token [Doorkeeper::AccessToken]
179
+ # The access token whose custom attributes are being compared
180
+ # to the custom_attributes.
181
+ #
182
+ # @param custom_attributes [Hash]
183
+ # A hash of the attributes for which we want to determine whether
184
+ # the token's custom attributes match.
185
+ #
186
+ # @return [Boolean] true if the token's custom attribute values
187
+ # match those in the custom_attributes, or if both are empty/blank.
188
+ # False otherwise.
189
+ def custom_attributes_match?(token, custom_attributes)
190
+ return true if custom_attributes.nil?
191
+
192
+ token_attribs = token.custom_attributes
193
+ return true if token_attribs.blank? && custom_attributes.blank?
194
+
195
+ Doorkeeper.config.custom_access_token_attributes.all? do |attribute|
196
+ token_attribs[attribute] == custom_attributes[attribute]
197
+ end
198
+ end
199
+
200
+ # Looking for not expired AccessToken record with a matching set of
201
+ # scopes that belongs to specific Application and Resource Owner.
202
+ # If it doesn't exists - then creates it.
203
+ #
204
+ # @param application [Doorkeeper::Application]
205
+ # Application instance
206
+ # @param resource_owner [ActiveRecord::Base, Integer]
207
+ # Resource Owner model instance or it's ID
208
+ # @param scopes [#to_s]
209
+ # set of scopes (any object that responds to `#to_s`)
210
+ # @param token_attributes [Hash]
211
+ # Additional attributes to use when creating a token
212
+ # @option token_attributes [Integer] :expires_in
213
+ # token lifetime in seconds
214
+ # @option token_attributes [Boolean] :use_refresh_token
215
+ # whether to use the refresh token
216
+ #
217
+ # @return [Doorkeeper::AccessToken] existing record or a new one
218
+ #
219
+ def find_or_create_for(application:, resource_owner:, scopes:, **token_attributes)
220
+ scopes = Doorkeeper::OAuth::Scopes.from_string(scopes) if scopes.is_a?(String)
221
+
222
+ if Doorkeeper.config.reuse_access_token
223
+ custom_attributes = extract_custom_attributes(token_attributes).presence
224
+ access_token = matching_token_for(
225
+ application, resource_owner, scopes, custom_attributes: custom_attributes, include_expired: false,
226
+ )
227
+
228
+ return access_token if access_token&.reusable?
229
+ end
230
+
231
+ create_for(
232
+ application: application,
233
+ resource_owner: resource_owner,
234
+ scopes: scopes,
235
+ **token_attributes,
236
+ )
237
+ end
238
+
239
+ # Creates a not expired AccessToken record with a matching set of
240
+ # scopes that belongs to specific Application and Resource Owner.
241
+ #
242
+ # @param application [Doorkeeper::Application]
243
+ # Application instance
244
+ # @param resource_owner [ActiveRecord::Base, Integer]
245
+ # Resource Owner model instance or it's ID
246
+ # @param scopes [#to_s]
247
+ # set of scopes (any object that responds to `#to_s`)
248
+ # @param token_attributes [Hash]
249
+ # Additional attributes to use when creating a token
250
+ # @option token_attributes [Integer] :expires_in
251
+ # token lifetime in seconds
252
+ # @option token_attributes [Boolean] :use_refresh_token
253
+ # whether to use the refresh token
254
+ #
255
+ # @return [Doorkeeper::AccessToken] new access token
256
+ #
257
+ def create_for(application:, resource_owner:, scopes:, **token_attributes)
258
+ token_attributes[:application] = application
259
+ token_attributes[:scopes] = scopes.to_s
260
+
261
+ if Doorkeeper.config.polymorphic_resource_owner?
262
+ token_attributes[:resource_owner] = resource_owner
263
+ else
264
+ token_attributes[:resource_owner_id] = resource_owner_id_for(resource_owner)
265
+ end
266
+
267
+ with_primary_role do
268
+ create!(token_attributes)
269
+ end
270
+ end
271
+
272
+ # Looking for not revoked Access Token records that belongs to specific
273
+ # Application and Resource Owner.
274
+ #
275
+ # @param application_id [Integer]
276
+ # ID of the Application model instance
277
+ # @param resource_owner [ActiveRecord::Base, Integer]
278
+ # Resource Owner model instance or it's ID
279
+ #
280
+ # @return [ActiveRecord::Relation]
281
+ # collection of matching AccessToken objects
282
+ #
283
+ def authorized_tokens_for(application_id, resource_owner)
284
+ by_resource_owner(resource_owner).where(
285
+ application_id: application_id,
286
+ revoked_at: nil,
287
+ )
288
+ end
289
+
290
+ # Convenience method for backwards-compatibility, return the last
291
+ # matching token for the given Application and Resource Owner.
292
+ #
293
+ # @param application_id [Integer]
294
+ # ID of the Application model instance
295
+ # @param resource_owner [ActiveRecord::Base, Integer]
296
+ # ID of the Resource Owner model instance
297
+ #
298
+ # @return [Doorkeeper::AccessToken, nil] matching AccessToken object or
299
+ # nil if nothing was found
300
+ #
301
+ def last_authorized_token_for(application_id, resource_owner)
302
+ authorized_tokens_for(application_id, resource_owner)
303
+ .ordered_by(:created_at, :desc)
304
+ .first
305
+ end
306
+
307
+ ##
308
+ # Determines the secret storing transformer
309
+ # Unless configured otherwise, uses the plain secret strategy
310
+ #
311
+ # @return [Doorkeeper::SecretStoring::Base]
312
+ #
313
+ def secret_strategy
314
+ ::Doorkeeper.config.token_secret_strategy
315
+ end
316
+
317
+ ##
318
+ # Determine the fallback storing strategy
319
+ # Unless configured, there will be no fallback
320
+ def fallback_secret_strategy
321
+ ::Doorkeeper.config.token_secret_fallback_strategy
322
+ end
323
+
324
+ # Extracts the token's custom attributes (defined by the
325
+ # custom_access_token_attributes config option) from the token's attributes.
326
+ #
327
+ # @param attributes [Hash]
328
+ # A hash of the access token's attributes.
329
+ # @return [Hash]
330
+ # A hash containing only the custom access token attributes.
331
+ def extract_custom_attributes(attributes)
332
+ attributes.with_indifferent_access.slice(
333
+ *Doorkeeper.configuration.custom_access_token_attributes,
334
+ )
335
+ end
336
+ end
337
+
338
+ # Access Token type: Bearer.
339
+ # @see https://datatracker.ietf.org/doc/html/rfc6750
340
+ # The OAuth 2.0 Authorization Framework: Bearer Token Usage
341
+ #
342
+ def token_type
343
+ "Bearer"
344
+ end
345
+
346
+ def use_refresh_token?
347
+ @use_refresh_token ||= false
348
+ !!@use_refresh_token
349
+ end
350
+
351
+ # JSON representation of the Access Token instance.
352
+ #
353
+ # @return [Hash] hash with token data
354
+ def as_json(_options = {})
355
+ {
356
+ resource_owner_id: resource_owner_id,
357
+ scope: scopes,
358
+ expires_in: expires_in_seconds,
359
+ application: { uid: application.try(:uid) },
360
+ created_at: created_at.to_i,
361
+ }.tap do |json|
362
+ if Doorkeeper.configuration.polymorphic_resource_owner?
363
+ json[:resource_owner_type] = resource_owner_type
364
+ end
365
+ end
366
+ end
367
+
368
+ # The token's custom attributes, as defined by
369
+ # the custom_access_token_attributes config option.
370
+ #
371
+ # @return [Hash] hash of custom access token attributes.
372
+ def custom_attributes
373
+ self.class.extract_custom_attributes(attributes)
374
+ end
375
+
376
+ # Indicates whether the token instance have the same credential
377
+ # as the other Access Token.
378
+ #
379
+ # @param access_token [Doorkeeper::AccessToken] other token
380
+ #
381
+ # @return [Boolean] true if credentials are same of false in other cases
382
+ #
383
+ def same_credential?(access_token)
384
+ application_id == access_token.application_id &&
385
+ same_resource_owner?(access_token)
386
+ end
387
+
388
+ # Indicates whether the token instance have the same credential
389
+ # as the other Access Token.
390
+ #
391
+ # @param access_token [Doorkeeper::AccessToken] other token
392
+ #
393
+ # @return [Boolean] true if credentials are same of false in other cases
394
+ #
395
+ def same_resource_owner?(access_token)
396
+ if Doorkeeper.configuration.polymorphic_resource_owner?
397
+ resource_owner == access_token.resource_owner
398
+ else
399
+ resource_owner_id == access_token.resource_owner_id
400
+ end
401
+ end
402
+
403
+ # Indicates if token is acceptable for specific scopes.
404
+ #
405
+ # @param scopes [Array<String>] scopes
406
+ #
407
+ # @return [Boolean] true if record is accessible and includes scopes or
408
+ # false in other cases
409
+ #
410
+ def acceptable?(scopes)
411
+ accessible? && includes_scope?(*scopes)
412
+ end
413
+
414
+ # We keep a volatile copy of the raw refresh token for initial communication
415
+ # The stored refresh_token may be mapped and not available in cleartext.
416
+ def plaintext_refresh_token
417
+ if secret_strategy.allows_restoring_secrets?
418
+ secret_strategy.restore_secret(self, :refresh_token)
419
+ else
420
+ @raw_refresh_token
421
+ end
422
+ end
423
+
424
+ # We keep a volatile copy of the raw token for initial communication
425
+ # The stored refresh_token may be mapped and not available in cleartext.
426
+ #
427
+ # Some strategies allow restoring stored secrets (e.g. symmetric encryption)
428
+ # while hashing strategies do not, so you cannot rely on this value
429
+ # returning a present value for persisted tokens.
430
+ def plaintext_token
431
+ if secret_strategy.allows_restoring_secrets?
432
+ secret_strategy.restore_secret(self, :token)
433
+ else
434
+ @raw_token
435
+ end
436
+ end
437
+
438
+ # Revokes token with `:refresh_token` equal to `:previous_refresh_token`
439
+ # and clears `:previous_refresh_token` attribute.
440
+ #
441
+ def revoke_previous_refresh_token!
442
+ return if !self.class.refresh_token_revoked_on_use? || previous_refresh_token.blank?
443
+
444
+ old_refresh_token&.revoke
445
+
446
+ if self.class.respond_to?(:with_primary_role)
447
+ self.class.with_primary_role { update_attribute(:previous_refresh_token, "") }
448
+ else
449
+ update_attribute(:previous_refresh_token, "")
450
+ end
451
+ end
452
+
453
+ private
454
+
455
+ # Searches for Access Token record with `:refresh_token` equal to
456
+ # `:previous_refresh_token` value.
457
+ #
458
+ # @return [Doorkeeper::AccessToken, nil]
459
+ # Access Token record or nil if nothing found
460
+ #
461
+ def old_refresh_token
462
+ @old_refresh_token ||= self.class.by_previous_refresh_token(previous_refresh_token)
463
+ end
464
+
465
+ # Generates refresh token with UniqueToken generator.
466
+ #
467
+ # @return [String] refresh token value
468
+ #
469
+ def generate_refresh_token
470
+ @raw_refresh_token = UniqueToken.generate
471
+ secret_strategy.store_secret(self, :refresh_token, @raw_refresh_token)
472
+ end
473
+
474
+ # Generates and sets the token value with the
475
+ # configured Generator class (see Doorkeeper.config).
476
+ #
477
+ # @return [String] generated token value
478
+ #
479
+ # @raise [Doorkeeper::Errors::UnableToGenerateToken]
480
+ # custom class doesn't implement .generate method
481
+ # @raise [Doorkeeper::Errors::TokenGeneratorNotFound]
482
+ # custom class doesn't exist
483
+ #
484
+ def generate_token
485
+ self.created_at ||= Time.now.utc
486
+
487
+ @raw_token = token_generator.generate(attributes_for_token_generator)
488
+ secret_strategy.store_secret(self, :token, @raw_token)
489
+ @raw_token
490
+ end
491
+
492
+ # Set of attributes that would be passed to token generator to
493
+ # generate unique token based on them.
494
+ #
495
+ # @return [Hash] set of attributes
496
+ #
497
+ def attributes_for_token_generator
498
+ {
499
+ resource_owner_id: resource_owner_id,
500
+ scopes: scopes,
501
+ application: application,
502
+ expires_in: expires_in,
503
+ created_at: created_at,
504
+ }.tap do |attributes|
505
+ if Doorkeeper.config.polymorphic_resource_owner?
506
+ attributes[:resource_owner] = resource_owner
507
+ end
508
+
509
+ Doorkeeper.config.custom_access_token_attributes.each do |attribute_name|
510
+ attributes[attribute_name] = public_send(attribute_name)
511
+ end
512
+ end
513
+ end
514
+
515
+ def token_generator
516
+ generator_name = Doorkeeper.config.access_token_generator
517
+ generator = generator_name.constantize
518
+
519
+ return generator if generator.respond_to?(:generate)
520
+
521
+ raise Errors::UnableToGenerateToken, "#{generator} does not respond to `.generate`."
522
+ rescue NameError
523
+ raise Errors::TokenGeneratorNotFound, "#{generator_name} not found"
524
+ end
525
+ end
526
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module ApplicationMixin
5
+ extend ActiveSupport::Concern
6
+
7
+ include OAuth::Helpers
8
+ include Models::Orderable
9
+ include Models::SecretStorable
10
+ include Models::Scopes
11
+
12
+ # :nodoc
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
+ #
26
+ def by_uid_and_secret(uid, secret)
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
33
+ end
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
+ #
42
+ def by_uid(uid)
43
+ find_by(uid: uid.to_s)
44
+ end
45
+
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
52
+
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
59
+ end
60
+
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)
69
+ end
70
+
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
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module Models
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
+ #
10
+ def accessible?
11
+ !expired? && !revoked?
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module Models
5
+ module Expirable
6
+ # Indicates whether the object is expired (`#expires_in` present and
7
+ # expiration time has come).
8
+ #
9
+ # @return [Boolean] true if object expired and false in other case
10
+ def expired?
11
+ !!(expires_in && Time.now.utc > expires_at)
12
+ end
13
+
14
+ # Calculates expiration time in seconds.
15
+ #
16
+ # @return [Integer, nil] number of seconds if object has expiration time
17
+ # or nil if object never expires.
18
+ def expires_in_seconds
19
+ return nil if expires_in.nil?
20
+
21
+ expires = expires_at - Time.now.utc
22
+ expires_sec = expires.seconds.round(0)
23
+ expires_sec > 0 ? expires_sec : 0
24
+ end
25
+
26
+ # Expiration time (date time of creation + TTL).
27
+ #
28
+ # @return [Time, nil] expiration time in UTC
29
+ # or nil if the object never expires.
30
+ #
31
+ def expires_at
32
+ expires_in && created_at + expires_in.seconds
33
+ end
34
+ end
35
+ end
36
+ end