effective_resources 2.10.1 → 2.11.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69383be96bec20188162a0c42162ce4727bf0f14ce74fa36556b09930bc2307a
4
- data.tar.gz: 7d4ae8dc17da1c34834af13b9d864502de5b580eaa88e3e9da5258f7afc2efb7
3
+ metadata.gz: 70b3835f1e45128ade83d38d69adbcd8096fd5aecce4882028d0512dab970dae
4
+ data.tar.gz: 7c38e5cb62a55b4fa4924970aad1b0f45cf06b0dccedb41770f8df394e98a9c0
5
5
  SHA512:
6
- metadata.gz: ecb3f7fb7caff620a403c577fc9d1196061e7da0ea2e1d137b6b4889e02097b46cc4a3cc8b7497cfc9fcb1641d9111170daed413a9f4743ee6005e0ba0ed4b0e
7
- data.tar.gz: 6cc85b52a0ee048f15ee61e9b370cc3cad27910529bbf4d83c8260d67bcdaf7ce464d5841fcfa45c1adb6a2fd518c4348f4e46aba5831740fb4f9d4972e44194
6
+ metadata.gz: 50be7d9a989a25f8aa6777e85603c18b8ccc3be72e78aa35e4aa83814ca0fa74371de434c1138dc9a2331b57d79c1e032dbf987628c2b3b800b8b3819b26eb3d
7
+ data.tar.gz: 46e3229e7ecc0a7e989ac022afed029183b96e9a225f7a3d93edc3d9ff0a14a37682bf4d1685e1c3ebf3f8c8a5760e0cd09eafc7346eb940bf13d5aecd1752ad
data/README.md CHANGED
@@ -392,6 +392,43 @@ def to_select2
392
392
  end
393
393
  ```
394
394
 
395
+ ## Authentication
396
+ Effective Resources is designed to work with Devise. It also adds support for an `alternate email`
397
+ for authentication. You just need to add an `alternate_email` column to your `User` model table.
398
+
399
+ After that column is added, any user would be able to log in with either their `email` or their
400
+ `alternate_email`.
401
+
402
+ You can also create another mailer for `devise` in order to send password reset emails to the both
403
+ the primary `email` and also the `alternate_email`, like this:
404
+
405
+ ```ruby
406
+ class DeviseMailer < Devise::Mailer
407
+ # Overriding Devise's #headers_for to support alternate_email when present
408
+ def headers_for(action, opts)
409
+ headers = super(action, opts)
410
+
411
+ if [:reset_password_instructions].include?(action)
412
+ headers.merge!(
413
+ to: [resource.email, resource.try(:alternate_email)].compact.uniq
414
+ )
415
+ end
416
+
417
+ headers
418
+ end
419
+ end
420
+ ```
421
+
422
+ and set this new mailer to be used by `devise` in `config/initializers/devise.rb`:
423
+
424
+ ```ruby
425
+ Devise.setup do |config|
426
+ # ...
427
+ config.mailer = 'DeviseMailer'
428
+ # ...
429
+ end
430
+ ```
431
+
395
432
  ## Testing
396
433
 
397
434
  Run tests by:
@@ -46,7 +46,7 @@ module EffectiveDeviseUser
46
46
  end
47
47
 
48
48
  # Devise invitable ignores model validations, so we manually check for duplicate email addresses.
49
- before_save(if: -> { new_record? && invitation_sent_at.present? }) do
49
+ before_save(if: -> { new_record? && try(:invitation_sent_at).present? }) do
50
50
  if email.blank?
51
51
  self.errors.add(:email, "can't be blank")
52
52
  raise("email can't be blank")
@@ -62,6 +62,28 @@ module EffectiveDeviseUser
62
62
  before_save(if: -> { persisted? && encrypted_password_changed? }) do
63
63
  assign_attributes(provider: nil, access_token: nil, refresh_token: nil, token_expires_at: nil)
64
64
  end
65
+
66
+ # Uniqueness validation of emails and alternate emails across all users
67
+ validate(if: -> { respond_to?(:alternate_email) }) do
68
+ records = self.class.where.not(id: self.id) # exclude self
69
+ email_duplicates = records.where("lower(email) = :email OR lower(alternate_email) = :email", email: email.to_s.strip.downcase)
70
+ alternate_email_duplicates = records.where("lower(email) = :alternate_email OR lower(alternate_email) = :alternate_email", alternate_email: alternate_email.to_s.strip.downcase)
71
+
72
+ # Check if a uniqueness validation was already performed before triggering the exists query
73
+ if !self.errors.added?(:email, 'has already been taken') && email_duplicates.exists?
74
+ self.errors.add(:email, 'has already been taken')
75
+ end
76
+
77
+ # Check if the alternate email is set before triggering the exists query
78
+ if try(:alternate_email).present? && alternate_email_duplicates.exists?
79
+ self.errors.add(:alternate_email, 'has already been taken')
80
+ end
81
+ end
82
+
83
+ with_options(if: -> { respond_to?(:alternate_email) }) do
84
+ validates :alternate_email, email: true, allow_blank: true
85
+ end
86
+
65
87
  end
66
88
 
67
89
  module ClassMethods
@@ -162,10 +184,48 @@ module EffectiveDeviseUser
162
184
  recoverable
163
185
  end
164
186
 
187
+ # https://github.com/heartcombo/devise/blob/f6e73e5b5c8f519f4be29ac9069c6ed8a2343ce4/lib/devise/models/authenticatable.rb#L276
188
+ def find_first_by_auth_conditions(tainted_conditions, opts = {})
189
+ conditions = devise_parameter_filter.filter(tainted_conditions).merge(opts)
190
+
191
+ user = to_adapter.find_first(conditions)
192
+ return user if user.present? && user.persisted?
193
+
194
+ to_adapter.find_first(alternate_email: conditions[:email]) if has_alternate_email?
195
+ end
196
+
197
+ # https://github.com/heartcombo/devise/blob/f6e73e5b5c8f519f4be29ac9069c6ed8a2343ce4/lib/devise/models/database_authenticatable.rb#L216
198
+ def find_for_database_authentication(warden_conditions)
199
+ conditions = warden_conditions.dup.presence || {}
200
+ primary_or_alternate_email = conditions[:email]
201
+ conditions.delete(:email)
202
+
203
+ raise "Expected an email #{has_alternate_email? ? 'or alternate email' : ''} but got [#{primary_or_alternate_email}] instead" if primary_or_alternate_email.blank?
204
+
205
+ query = if has_alternate_email?
206
+ "lower(email) = :value OR lower(alternate_email) = :value"
207
+ else
208
+ "lower(email) = :value"
209
+ end
210
+
211
+ all
212
+ .where(conditions)
213
+ .where(query, value: primary_or_alternate_email.strip.downcase)
214
+ .first
215
+ end
216
+
217
+ def has_alternate_email?
218
+ 'alternate_email'.in? column_names
219
+ end
220
+
165
221
  end
166
222
 
167
223
  # EffectiveDeviseUser Instance Methods
168
224
 
225
+ def alternate_email=(value)
226
+ super(value.to_s.strip.downcase.presence)
227
+ end
228
+
169
229
  def reinvite!
170
230
  invite!
171
231
  end
@@ -1,3 +1,3 @@
1
1
  module EffectiveResources
2
- VERSION = '2.10.1'.freeze
2
+ VERSION = '2.11.1'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_resources
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.10.1
4
+ version: 2.11.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-13 00:00:00.000000000 Z
11
+ date: 2023-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails