Iacutone.rb

coding and things

Changing Your Password From Scratch

| Comments

So say you are using devise and have none of the has_secure_password medthod available to you. One should learn the bcrypt gem... and needs to abstract some methods in order to parse an encrypted password. Cool, Bcrypt can do that, as located here. Here is my modified code in order to confirm if a new password in order to apply a boolean value to an inputed password.

edit.html.haml lang: ruby
1
2
3
4
5
6
7
8
9
10
11
 %li
      = f.label :current_password
      = f.password_field :current_password
      # => 'password'
    %li
      = f.label :new_password, 'New Password'
      = f.password_field :new_password
      # => 'new_password'

  .buttons
    %button Save
user.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

  def authenticate(unencrypted_password)
    if BCrypt::Password.new(encrypted_password) == unencrypted_password && self
      bcrypt = ::BCrypt::Password.new(encrypted_password)
      # creates a bcrypt variable if the encrypted passwors result is true
      # => "$2a$10$wgOzLhy84peHUD9wr9UkgOKRpwfls/0h48NYVvKIOdUdbz3XOEpSK" 
      password = ::BCrypt::Engine.hash_secret(password, bcrypt.salt)
      # then salts the new password
      # => "$2a$10$VUNoD3xdAp7ytTIsTyH5feY.DNUKA4efIdkcI6ViBQ532o8lyNV/e" 
      user = nil unless reset_secure_compare(password, encrypted_password)
      return true
    else BCrypt::Password.new(encrypted_password) != unencrypted_password && self
      return false
    end
  end

You might be wondering what the reset_secure_password method is doing. Well, it is pulled from the Devise docs and is preventing timing attacks, when an attacker attempts to compromise an encryption by analyzing the time taken in order to execute the password and salting algorithms.

Cool, now I can pass my current_password attribute to make sure it is true. I need to make a custom Active Record Validations.

user.rb
1
2
3
4
5
 def correct_password_update_validator
    if self.authenticate(current_password) == true and current_password.present? and new_password.present?
      self.encrypted_password = ::BCrypt::Password.create(new_password)
    end
   end

If this validates, encrypted_password on the database returns true. Now I need validations for events with blank fields (I want nothing to happen), and also, if current_password is blank and new_password and present, visa versa.

user.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 validate :incorrect_password_update_validator, on: :update
  validate :correct_password_update_validator, on: :update
  validate :current_password_present, on: :update
  validate :new_password_present, on: :update
  validate :current_password_true_present, on: :update

  private

  def current_password_true_present
    if self.authenticate(current_password) == true and new_password.blank? and current_password.present?
      errors.add(:new_password, " needs to be filled out.")
    end
  end

  def current_password_present
    if self.authenticate(current_password) == false and new_password.present? and current_password.blank?
      errors.add(:current_password, " needs to be filled out.")
    end
  end

  def new_password_present
    if self.authenticate(current_password) == false and new_password.blank? and current_password.present?
      errors.add(:new_password, " needs to be filled out.")
    end
  end

  def incorrect_password_update_validator
    if self.authenticate(current_password) == false and current_password.present? and new_password.present?
      errors.add(:current_password, " does not match.")
    end
  end

  def reset_secure_compare(a, b)
    return false if a.blank? || b.blank? || a.bytesize != b.bytesize
    l = a.unpack "C#{a.bytesize}"

    res = 0
    b.each_byte { |byte| res |= byte ^ l.shift }
    res == 0
  end

Now, all use cases of the user improperly editing the form result in false and a validation error occurs.

Comments