doorkeeper-rethinkdb 1.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 856cb49c0238f249ed8a203b4dfb93bf57b1b2edf2953d47c8d0b1708a74063e
4
+ data.tar.gz: 8b5171dbe6d20e837f8a7b238079dd26b07808db5adc5192cab2439d597449e1
5
+ SHA512:
6
+ metadata.gz: 5a1f0cc85cd8764b277cf4872e9284044305552a4f11f150f537489317689a0523864a917de070e5482f6d83179256c28e37026284a29ac8d6b4a9b76dd138aa
7
+ data.tar.gz: 26b37570029b3087f4a179c5ced78085fb4e952a30aed77830f9143f6a64526c06490d8b701be7b1575bbbce90c487d7a460e922a6b8d3899d7745d451a1343c
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016 ACAProjects
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # doorkeeper-rethinkdb extension
2
+
3
+ ## Installation
4
+
5
+ doorkeeper-rethinkdb provides doorkeeper support to rethinkdb
6
+ To start using it, add to your Gemfile:
7
+
8
+ ``` ruby
9
+ gem "doorkeeper-rethinkdb"
10
+ ```
11
+
12
+ Run [doorkeeper’s installation generator]:
13
+
14
+ rails generate doorkeeper:install
15
+
16
+ [doorkeeper’s installation generator]: https://github.com/doorkeeper-gem/doorkeeper#installation
17
+
18
+ This will install the doorkeeper initializer into
19
+ `config/initializers/doorkeeper.rb`.
20
+
21
+ Set the ORM configuration:
22
+
23
+ ``` ruby
24
+ Doorkeeper.configure do
25
+ orm :rethinkdb
26
+ end
27
+ ```
28
+
29
+ ## Tests
30
+
31
+ To run tests, clone this repository and run `rake`. It will copy and run
32
+ doorkeeper’s original test suite, after configuring the ORM according to the
33
+ variables defined in `.travis.yml` file.
34
+
35
+
36
+ ---
37
+
38
+ Please refer to https://github.com/doorkeeper-gem/doorkeeper for instructions on
39
+ doorkeeper’s project.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'bundler/setup'
2
+ require 'rspec/core/rake_task'
3
+
4
+ task :load_doorkeeper do
5
+ `rm -rf spec/`
6
+ `git checkout -- spec`
7
+ `git submodule init`
8
+ `git submodule update`
9
+ `cp -r -n doorkeeper/spec .`
10
+ `bundle exec rspec`
11
+ end
12
+
13
+ desc 'Default: run specs.'
14
+ task default: :spec
15
+
16
+ desc 'Clone down doorkeeper specs'
17
+ task spec: :load_doorkeeper
18
+
19
+ RSpec::Core::RakeTask.new(:spec) do |config|
20
+ config.verbose = false
21
+ end
22
+
23
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,7 @@
1
+ require 'doorkeeper-rethinkdb/version'
2
+
3
+ require 'doorkeeper'
4
+ require 'support/orm/rethinkdb'
5
+
6
+ module DoorkeeperRethinkdb
7
+ end
@@ -0,0 +1,3 @@
1
+ module DoorkeeperRethinkdb
2
+ VERSION = "1.1.1"
3
+ end
@@ -0,0 +1,20 @@
1
+ module Doorkeeper
2
+ module Orm
3
+ module Rethinkdb
4
+ def self.initialize_models!
5
+ require 'support/orm/rethinkdb/timestamps'
6
+ require 'support/orm/rethinkdb/scopes'
7
+ require 'support/orm/rethinkdb/access_grant'
8
+ require 'support/orm/rethinkdb/access_token'
9
+ require 'support/orm/rethinkdb/application'
10
+ end
11
+
12
+ def self.initialize_application_owner!
13
+ #require 'doorkeeper/models/concerns/ownership'
14
+ #Doorkeeper::Application.send :include, Doorkeeper::Models::Ownership
15
+ end
16
+
17
+ def self.check_requirements!(_config); end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,122 @@
1
+
2
+ module Doorkeeper
3
+ class AccessGrant
4
+ include NoBrainer::Document
5
+
6
+ table_config name: 'doorkeeper_grant'
7
+
8
+ include OAuth::Helpers
9
+ include Models::Expirable
10
+ include Models::Revocable
11
+ include Models::Accessible
12
+ include Models::Scopes
13
+ include Models::SecretStorable
14
+
15
+ include Timestamps
16
+
17
+ belongs_to :application, class_name: 'Doorkeeper::Application'
18
+
19
+ field :resource_owner_id, type: String
20
+ field :token, type: String, uniq: true, index: true
21
+ field :scopes, type: String
22
+ field :redirect_uri, type: String
23
+
24
+ field :expires_in, type: Integer
25
+ field :ttl, type: Integer
26
+ field :revoked_at, type: Time
27
+
28
+ # PKCE support
29
+ field :code_challenge, type: String
30
+ field :code_challenge_method, type: String
31
+
32
+ index :ttl
33
+
34
+ class << self
35
+ def by_token(token)
36
+ find_by_plaintext_token(:token, token)
37
+ end
38
+
39
+ def find_by_plaintext_token(attr, token)
40
+ # We are not implementing the fallback strategy
41
+ where(attr => secret_strategy.transform_secret(token.to_s)).first
42
+ end
43
+
44
+ # @param code_verifier [#to_s] a one time use value (any object that responds to `#to_s`)
45
+ #
46
+ # @return [#to_s] An encoded code challenge based on the provided verifier
47
+ # suitable for PKCE validation
48
+ #
49
+ def generate_code_challenge(code_verifier)
50
+ padded_result = Base64.urlsafe_encode64(Digest::SHA256.digest(code_verifier))
51
+ padded_result.split("=")[0] # Remove any trailing '='
52
+ end
53
+
54
+ def pkce_supported?
55
+ true
56
+ end
57
+
58
+ ##
59
+ # Determines the secret storing transformer
60
+ # Unless configured otherwise, uses the plain secret strategy
61
+ #
62
+ # @return [Doorkeeper::SecretStoring::Base]
63
+ #
64
+ def secret_strategy
65
+ ::Doorkeeper.config.token_secret_strategy
66
+ end
67
+
68
+ ##
69
+ # Determine the fallback storing strategy
70
+ # Unless configured, there will be no fallback
71
+ #
72
+ # @return [Doorkeeper::SecretStoring::Base]
73
+ #
74
+ def fallback_secret_strategy
75
+ ::Doorkeeper.config.token_secret_fallback_strategy
76
+ end
77
+ end
78
+
79
+ validates :resource_owner_id, :application, :token, :expires_in, :redirect_uri, presence: true
80
+ before_validation :generate_token, on: :create
81
+
82
+ def transaction; yield; end
83
+ def lock!; end
84
+
85
+ def uses_pkce?
86
+ self.code_challenge.present?
87
+ end
88
+
89
+ # We keep a volatile copy of the raw token for initial communication
90
+ # The stored refresh_token may be mapped and not available in cleartext.
91
+ #
92
+ # Some strategies allow restoring stored secrets (e.g. symmetric encryption)
93
+ # while hashing strategies do not, so you cannot rely on this value
94
+ # returning a present value for persisted tokens.
95
+ def plaintext_token
96
+ if secret_strategy.allows_restoring_secrets?
97
+ secret_strategy.restore_secret(self, :token)
98
+ else
99
+ @raw_token
100
+ end
101
+ end
102
+
103
+ def revoke(clock = Time)
104
+ self.revoked_at = clock.now.utc
105
+ self.save!
106
+ end
107
+
108
+ private
109
+
110
+ # Generates token value with UniqueToken class.
111
+ #
112
+ # @return [String] token value
113
+ #
114
+ def generate_token
115
+ self.ttl = (self.created_at + self.expires_in + 30).to_i if self.created_at && self.expires_in
116
+ if self.token.blank?
117
+ @raw_token = Doorkeeper::OAuth::Helpers::UniqueToken.generate
118
+ secret_strategy.store_secret(self, :token, @raw_token)
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,376 @@
1
+
2
+ module Doorkeeper
3
+ class AccessToken
4
+ include NoBrainer::Document
5
+
6
+ table_config name: 'doorkeeper_token'
7
+
8
+ include OAuth::Helpers
9
+ include Models::Expirable
10
+ include Models::Reusable
11
+ include Models::Revocable
12
+ include Models::Accessible
13
+ include Models::Scopes
14
+ include Models::SecretStorable
15
+
16
+ include ::Doorkeeper::Rethinkdb::Timestamps
17
+
18
+ attr_writer :use_refresh_token
19
+
20
+ belongs_to :application, class_name: 'Doorkeeper::Application'
21
+
22
+ field :resource_owner_id, type: String, index: true
23
+ field :token, type: String, uniq: true, index: true
24
+ field :refresh_token, type: String, index: true
25
+
26
+ field :scopes, type: String
27
+ field :previous_refresh_token, type: String, default: ->{ '' }
28
+ field :expires_in, type: Integer
29
+ field :ttl, type: Integer
30
+ field :revoked_at, type: Time
31
+
32
+ index :ttl
33
+
34
+ alias :plaintext_token :token
35
+ alias :plaintext_refresh_token :refresh_token
36
+
37
+ validates :resource_owner_id, :application, :expires_in, :token, presence: true
38
+
39
+ class << self
40
+ def refresh_token_revoked_on_use?
41
+ true
42
+ end
43
+
44
+ def find_by_plaintext_token(attr, token)
45
+ # We are not implementing the fallback strategy
46
+ where(attr => secret_strategy.transform_secret(token.to_s)).first
47
+ end
48
+
49
+ # Returns an instance of the Doorkeeper::AccessToken with
50
+ # specific token value.
51
+ #
52
+ # @param token [#to_s]
53
+ # token value (any object that responds to `#to_s`)
54
+ #
55
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
56
+ # if there is no record with such token
57
+ #
58
+ def by_token(token)
59
+ find_by_plaintext_token(:token, token)
60
+ end
61
+
62
+ # Returns an instance of the Doorkeeper::AccessToken
63
+ # with specific token value.
64
+ #
65
+ # @param refresh_token [#to_s]
66
+ # refresh token value (any object that responds to `#to_s`)
67
+ #
68
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
69
+ # if there is no record with such refresh token
70
+ #
71
+ def by_refresh_token(refresh_token)
72
+ find_by_plaintext_token(:refresh_token, refresh_token)
73
+ end
74
+
75
+ # Returns an instance of the Doorkeeper::AccessToken
76
+ # found by previous refresh token. Keep in mind that value
77
+ # of the previous_refresh_token isn't encrypted using
78
+ # secrets strategy.
79
+ #
80
+ # @param previous_refresh_token [#to_s]
81
+ # previous refresh token value (any object that responds to `#to_s`)
82
+ #
83
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
84
+ # if there is no record with such refresh token
85
+ #
86
+ def by_previous_refresh_token(previous_refresh_token)
87
+ return nil unless previous_refresh_token.present?
88
+ where(previous_refresh_token: previous_refresh_token).first
89
+ end
90
+
91
+ # Revokes AccessToken records that have not been revoked and associated
92
+ # with the specific Application and Resource Owner.
93
+ #
94
+ # @param application_id [Integer]
95
+ # ID of the Application
96
+ # @param resource_owner [ActiveRecord::Base]
97
+ # instance of the Resource Owner model
98
+ #
99
+ def revoke_all_for(application_id, resource_owner)
100
+ where(application_id: application_id, resource_owner_id: resource_owner.id).each do |at|
101
+ at.revoke
102
+ end
103
+ end
104
+
105
+ # Looking for not revoked Access Token record that belongs to specific
106
+ # Application and Resource Owner.
107
+ #
108
+ # @param application_id [Integer]
109
+ # ID of the Application model instance
110
+ # @param resource_owner_id [Integer]
111
+ # ID of the Resource Owner model instance
112
+ #
113
+ # @return [Doorkeeper::AccessToken, nil] matching AccessToken object or
114
+ # nil if nothing was found
115
+ #
116
+ def last_authorized_token_for(application_id, resource_owner_id)
117
+ resource_owner_id = resource_owner_id.try(:id) || resource_owner_id
118
+ result = where(application_id: application_id, resource_owner_id: resource_owner_id).last
119
+ return nil unless result
120
+ result[:revoked_at] ? nil : result
121
+ end
122
+
123
+ # Looking for not expired Access Token with a matching set of scopes
124
+ # that belongs to specific Application and Resource Owner.
125
+ #
126
+ # @param application [Doorkeeper::Application]
127
+ # Application instance
128
+ # @param resource_owner_or_id [ActiveRecord::Base, Integer]
129
+ # Resource Owner model instance or it's ID
130
+ # @param scopes [String, Doorkeeper::OAuth::Scopes]
131
+ # set of scopes
132
+ #
133
+ # @return [Doorkeeper::AccessToken, nil] Access Token instance or
134
+ # nil if matching record was not found
135
+ #
136
+ def matching_token_for(application, resource_owner_or_id, scopes)
137
+ resource_owner_id = resource_owner_or_id.try(:id) || resource_owner_or_id
138
+ token = last_authorized_token_for(application.try(:id), resource_owner_id)
139
+ if token && scopes_match?(token.scopes, scopes, application.try(:scopes))
140
+ token
141
+ end
142
+ end
143
+
144
+ # Checks whether the token scopes match the scopes from the parameters or
145
+ # Application scopes (if present).
146
+ #
147
+ # @param token_scopes [#to_s]
148
+ # set of scopes (any object that responds to `#to_s`)
149
+ # @param param_scopes [String]
150
+ # scopes from params
151
+ # @param app_scopes [String]
152
+ # Application scopes
153
+ #
154
+ # @return [Boolean] true if all scopes and blank or matches
155
+ # and false in other cases
156
+ #
157
+ def scopes_match?(token_scopes, param_scopes, app_scopes)
158
+ (!token_scopes.present? && !param_scopes.present?) ||
159
+ Doorkeeper::OAuth::Helpers::ScopeChecker.match?(
160
+ token_scopes.to_s,
161
+ param_scopes,
162
+ app_scopes
163
+ )
164
+ end
165
+
166
+ # Looking for not expired AccessToken record with a matching set of
167
+ # scopes that belongs to specific Application and Resource Owner.
168
+ # If it doesn't exists - then creates it.
169
+ #
170
+ # @param application [Doorkeeper::Application]
171
+ # Application instance
172
+ # @param resource_owner_id [ActiveRecord::Base, Integer]
173
+ # Resource Owner model instance or it's ID
174
+ # @param scopes [#to_s]
175
+ # set of scopes (any object that responds to `#to_s`)
176
+ # @param expires_in [Integer]
177
+ # token lifetime in seconds
178
+ # @param use_refresh_token [Boolean]
179
+ # whether to use the refresh token
180
+ #
181
+ # @return [Doorkeeper::AccessToken] existing record or a new one
182
+ #
183
+ def find_or_create_for(application:, resource_owner:, scopes:, **token_attributes)
184
+ resource_owner = resource_owner.try(:id) || resource_owner
185
+
186
+ if Doorkeeper.config.reuse_access_token
187
+ access_token = matching_token_for(application, resource_owner, scopes)
188
+
189
+ return access_token if access_token&.reusable?
190
+ end
191
+
192
+ create!(
193
+ application_id: application.try(:id),
194
+ resource_owner_id: resource_owner || application.try(:owner_id),
195
+ scopes: scopes.to_s,
196
+ **token_attributes,
197
+ )
198
+ end
199
+
200
+ def create_for(application:, resource_owner:, scopes:, **token_attributes)
201
+ token_attributes[:application_id] = application&.id
202
+ token_attributes[:scopes] = scopes.to_s
203
+
204
+ resource_owner = resource_owner.try(:id) || resource_owner
205
+ token_attributes[:resource_owner_id] = resource_owner || application.try(:owner_id)
206
+
207
+ create!(**token_attributes)
208
+ end
209
+
210
+ ##
211
+ # Determines the secret storing transformer
212
+ # Unless configured otherwise, uses the plain secret strategy
213
+ #
214
+ # @return [Doorkeeper::SecretStoring::Base]
215
+ #
216
+ def secret_strategy
217
+ ::Doorkeeper.config.token_secret_strategy
218
+ end
219
+
220
+ ##
221
+ # Determine the fallback storing strategy
222
+ # Unless configured, there will be no fallback
223
+ def fallback_secret_strategy
224
+ ::Doorkeeper.config.token_secret_fallback_strategy
225
+ end
226
+ end
227
+
228
+ # Access Token type: Bearer.
229
+ # @see https://tools.ietf.org/html/rfc6750
230
+ # The OAuth 2.0 Authorization Framework: Bearer Token Usage
231
+ #
232
+ def token_type
233
+ 'bearer'
234
+ end
235
+
236
+ def use_refresh_token?
237
+ @use_refresh_token ||= false
238
+ !!@use_refresh_token
239
+ end
240
+
241
+ # JSON representation of the Access Token instance.
242
+ #
243
+ # @return [Hash] hash with token data
244
+ def as_json(_options = {})
245
+ {
246
+ resource_owner_id: resource_owner_id,
247
+ scopes: scopes,
248
+ expires_in_seconds: expires_in_seconds,
249
+ application: { uid: application.try(:uid) },
250
+ created_at: created_at.to_i
251
+ }
252
+ end
253
+
254
+ # Indicates whether the token instance have the same credential
255
+ # as the other Access Token.
256
+ #
257
+ # @param access_token [Doorkeeper::AccessToken] other token
258
+ #
259
+ # @return [Boolean] true if credentials are same of false in other cases
260
+ #
261
+ def same_credential?(access_token)
262
+ application_id == access_token.application_id &&
263
+ resource_owner_id == access_token.resource_owner_id
264
+ end
265
+
266
+ # Indicates if token is acceptable for specific scopes.
267
+ #
268
+ # @param scopes [Array<String>] scopes
269
+ #
270
+ # @return [Boolean] true if record is accessible and includes scopes or
271
+ # false in other cases
272
+ #
273
+ def acceptable?(scopes)
274
+ accessible? && includes_scope?(*scopes)
275
+ end
276
+
277
+ # We keep a volatile copy of the raw refresh token for initial communication
278
+ # The stored refresh_token may be mapped and not available in cleartext.
279
+ def plaintext_refresh_token
280
+ if secret_strategy.allows_restoring_secrets?
281
+ secret_strategy.restore_secret(self, :refresh_token)
282
+ else
283
+ @raw_refresh_token
284
+ end
285
+ end
286
+
287
+ # We keep a volatile copy of the raw token for initial communication
288
+ # The stored refresh_token may be mapped and not available in cleartext.
289
+ #
290
+ # Some strategies allow restoring stored secrets (e.g. symmetric encryption)
291
+ # while hashing strategies do not, so you cannot rely on this value
292
+ # returning a present value for persisted tokens.
293
+ def plaintext_token
294
+ if secret_strategy.allows_restoring_secrets?
295
+ secret_strategy.restore_secret(self, :token)
296
+ else
297
+ @raw_token
298
+ end
299
+ end
300
+
301
+ # Revokes token with `:refresh_token` equal to `:previous_refresh_token`
302
+ # and clears `:previous_refresh_token` attribute.
303
+ #
304
+ def revoke_previous_refresh_token!
305
+ return unless self.class.refresh_token_revoked_on_use?
306
+ self.previous_refresh_token = ''
307
+ self.save!
308
+ end
309
+
310
+ def revoke(clock = Time)
311
+ self.revoked_at = clock.now.utc
312
+ self.save!
313
+ end
314
+
315
+ def transaction; yield; end
316
+ def lock!; end
317
+
318
+ private
319
+
320
+ before_validation :generate_token, on: :create
321
+ before_validation :generate_refresh_token, on: :create, if: :use_refresh_token?
322
+
323
+ validate :refresh_token_unique
324
+
325
+ def refresh_token_unique
326
+ if refresh_token_changed? && refresh_token.present? && ::Doorkeeper::AccessToken.where(refresh_token: refresh_token).count > 0
327
+ errors.add(:refresh_token, "must be unique")
328
+ end
329
+ end
330
+
331
+ # Generates refresh token with UniqueToken generator.
332
+ #
333
+ # @return [String] refresh token value
334
+ #
335
+ def generate_refresh_token
336
+ if self.refresh_token.blank?
337
+ @raw_refresh_token = UniqueToken.generate
338
+ secret_strategy.store_secret(self, :refresh_token, @raw_refresh_token)
339
+ end
340
+ end
341
+
342
+ def generate_token
343
+ return if self.token.present?
344
+
345
+ self.created_at ||= Time.now.utc
346
+
347
+ if use_refresh_token?
348
+ self.ttl = (self.created_at + 1.months).to_i
349
+ else
350
+ self.ttl = (self.created_at + self.expires_in + 30).to_i
351
+ end
352
+
353
+ generator = Doorkeeper.configuration.access_token_generator.constantize
354
+ @raw_token = generator.generate(
355
+ resource_owner_id: resource_owner_id,
356
+ scopes: scopes,
357
+ application: application,
358
+ expires_in: expires_in,
359
+ created_at: created_at
360
+ )
361
+ secret_strategy.store_secret(self, :token, @raw_token)
362
+ @raw_token
363
+ rescue NoMethodError
364
+ raise Errors::UnableToGenerateToken, "#{generator} does not respond to `.generate`."
365
+ rescue NameError
366
+ raise Errors::TokenGeneratorNotFound, "#{generator} not found"
367
+ end
368
+ end
369
+
370
+ # # Skips validation if field is empty
371
+ # class UniqueOrEmptyValidator < UniquenessValidator
372
+ # def validate_each(doc, attr, value)
373
+ # super if value
374
+ # end
375
+ # end
376
+ end
@@ -0,0 +1,93 @@
1
+
2
+ # Load these before the has_many
3
+ require File.expand_path("../access_grant", __FILE__)
4
+ require File.expand_path("../access_token", __FILE__)
5
+
6
+ # Required for Rails 6 support
7
+ require "doorkeeper/orm/active_record/redirect_uri_validator"
8
+
9
+ module Doorkeeper
10
+ class Application
11
+ include NoBrainer::Document
12
+
13
+ table_config name: 'doorkeeper_app'
14
+
15
+ include OAuth::Helpers
16
+ include Models::Scopes
17
+
18
+ field :name, type: String
19
+ field :uid, type: String, uniq: true, index: true
20
+ field :secret, type: String
21
+ field :scopes, type: String
22
+ field :redirect_uri, type: String, uniq: true, index: true
23
+
24
+ field :skip_authorization, type: Boolean, default: false
25
+ field :confidential, type: Boolean, default: false
26
+
27
+ field :owner_id, type: String
28
+
29
+ validates :owner, presence: true, if: :validate_owner?
30
+ def validate_owner?
31
+ Doorkeeper.configuration.confirm_application_owner?
32
+ end
33
+
34
+ has_many :access_grants, dependent: :destroy, class_name: 'Doorkeeper::AccessGrant'
35
+ has_many :access_tokens, dependent: :destroy, class_name: 'Doorkeeper::AccessToken'
36
+
37
+ class << self
38
+ def by_uid(uid)
39
+ where(uid: uid).first
40
+ end
41
+
42
+ def by_uid_and_secret(uid, secret)
43
+ where(uid: uid, secret: secret).first
44
+ end
45
+
46
+ def by_uid_and_secret(uid, secret)
47
+ app = by_uid(uid)
48
+ return unless app
49
+ return app if secret.blank? && !app.confidential?
50
+ return unless app.secret_matches?(secret)
51
+
52
+ app
53
+ end
54
+
55
+ def authorized_for(resource_owner)
56
+ AccessToken.find_by_resource_owner_id(resource_owner.id).collect(&:application)
57
+ end
58
+ end
59
+
60
+ def authorized_for_resource_owner?(resource_owner)
61
+ Doorkeeper.configuration.authorize_resource_owner_for_client.call(self, resource_owner)
62
+ end
63
+
64
+ def secret_matches?(input)
65
+ # return false if either is nil, since secure_compare depends on strings
66
+ # but Application secrets MAY be nil depending on confidentiality.
67
+ return false if input.nil? || secret.nil?
68
+
69
+ # When matching the secret by comparer function, all is well.
70
+ input == secret
71
+ end
72
+
73
+ private
74
+
75
+ validates :name, :secret, :uid, presence: true
76
+ validates :redirect_uri, "doorkeeper/redirect_uri": true
77
+ validates :confidential, inclusion: { in: [true, false] }
78
+
79
+ before_validation :generate_uid, :generate_secret, on: :create
80
+
81
+ def has_scopes?
82
+ true
83
+ end
84
+
85
+ def generate_uid
86
+ self.uid = UniqueToken.generate if uid.blank?
87
+ end
88
+
89
+ def generate_secret
90
+ self.secret = UniqueToken.generate if secret.blank?
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,22 @@
1
+
2
+ module Doorkeeper
3
+ # Reopen to align with NoBrainer's exclusively `super` overrides.
4
+ # ORM is thrust into an infinite loop, otherwise.
5
+ module Models::Scopes
6
+ def scopes
7
+ OAuth::Scopes.from_string(super)
8
+ end
9
+
10
+ def scopes=(value)
11
+ super Array(value).join(" ")
12
+ end
13
+
14
+ def scopes_string
15
+ self._read_attribute(:scopes)
16
+ end
17
+
18
+ def includes_scope?(*required_scopes)
19
+ required_scopes.blank? || required_scopes.any? { |scope| scopes.exists?(scope.to_s) }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+ require 'date'
2
+
3
+ module Doorkeeper
4
+ module Rethinkdb
5
+ module Timestamps
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ field :revoked_at, type: Integer
10
+ field :created_at, type: Integer, default: ->{ Time.now.to_i + 1 }
11
+ end
12
+
13
+ def revoked_at
14
+ revoked = super
15
+ Time.at(revoked) unless revoked.nil?
16
+ end
17
+
18
+ def revoked_at=(time)
19
+ if time
20
+ number = time.is_a?(Numeric) ? time.to_i : time.to_time.to_i
21
+ super number
22
+ else
23
+ super nil
24
+ end
25
+ end
26
+
27
+ def created_at
28
+ Time.at(super)
29
+ end
30
+ end
31
+ end
32
+ end
metadata ADDED
@@ -0,0 +1,207 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: doorkeeper-rethinkdb
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Stephen von Takach
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-08-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: doorkeeper
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 4.0.0
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '6'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 4.0.0
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '6'
33
+ - !ruby/object:Gem::Dependency
34
+ name: nobrainer
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 0.33.0
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '1'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 0.33.0
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '1'
53
+ - !ruby/object:Gem::Dependency
54
+ name: sqlite3
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ - !ruby/object:Gem::Dependency
68
+ name: capybara
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ - !ruby/object:Gem::Dependency
82
+ name: database_cleaner
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '1.5'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '1.5'
95
+ - !ruby/object:Gem::Dependency
96
+ name: factory_girl
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '4.7'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '4.7'
109
+ - !ruby/object:Gem::Dependency
110
+ name: generator_spec
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '0.9'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: '0.9'
123
+ - !ruby/object:Gem::Dependency
124
+ name: rake
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: 12.3.3
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: 12.3.3
137
+ - !ruby/object:Gem::Dependency
138
+ name: rspec-rails
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ type: :development
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ - !ruby/object:Gem::Dependency
152
+ name: timecop
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: '0.8'
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: '0.8'
165
+ description: Doorkeeper RethinkDB ORMs
166
+ email:
167
+ - steve@aca.im
168
+ executables: []
169
+ extensions: []
170
+ extra_rdoc_files: []
171
+ files:
172
+ - MIT-LICENSE
173
+ - README.md
174
+ - Rakefile
175
+ - lib/doorkeeper-rethinkdb.rb
176
+ - lib/doorkeeper-rethinkdb/version.rb
177
+ - lib/support/orm/rethinkdb.rb
178
+ - lib/support/orm/rethinkdb/access_grant.rb
179
+ - lib/support/orm/rethinkdb/access_token.rb
180
+ - lib/support/orm/rethinkdb/application.rb
181
+ - lib/support/orm/rethinkdb/scopes.rb
182
+ - lib/support/orm/rethinkdb/timestamps.rb
183
+ homepage: http://github.com/aca-labs/doorkeeper-rethinkdb
184
+ licenses:
185
+ - MIT
186
+ metadata: {}
187
+ post_install_message:
188
+ rdoc_options: []
189
+ require_paths:
190
+ - lib
191
+ required_ruby_version: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - ">="
194
+ - !ruby/object:Gem::Version
195
+ version: '0'
196
+ required_rubygems_version: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: '0'
201
+ requirements: []
202
+ rubyforge_project:
203
+ rubygems_version: 2.7.7
204
+ signing_key:
205
+ specification_version: 4
206
+ summary: Doorkeeper RethinkDB ORMs
207
+ test_files: []