From bcf4e4f2b02157cecc1f1727a95cdf5bfa471771 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 18 Dec 2010 13:38:05 -0800 Subject: Added ActiveRecord::Base#has_secure_password (via ActiveModel::SecurePassword) to encapsulate dead-simple password usage with SHA2 encryption and salting --- activemodel/lib/active_model/secure_password.rb | 73 +++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 activemodel/lib/active_model/secure_password.rb (limited to 'activemodel/lib/active_model/secure_password.rb') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb new file mode 100644 index 0000000000..0599ce6865 --- /dev/null +++ b/activemodel/lib/active_model/secure_password.rb @@ -0,0 +1,73 @@ +require 'digest/sha2' + +module ActiveModel + module SecurePassword + extend ActiveSupport::Concern + + module ClassMethods + # Adds methods to set and authenticate against a SHA2-encrypted and salted password. + # This mechanism requires you to have password_digest and password_salt attributes. + # + # Validations for presence of password, confirmation of password (using a "password_confirmation" attribute), + # and strength of password (at least 6 chars, not "password") are automatically added. + # You can add more validations by hand if need be. + # + # Example using Active Record (which automatically includes ActiveModel::SecurePassword): + # + # # Schema: User(name:string, password_digest:string, password_salt:string) + # class User < ActiveRecord::Base + # has_secure_password + # end + # + # user = User.new(:name => "david", :password => "secret", :password_confirmation => "nomatch") + # user.save # => false, password not long enough + # user.password = "mUc3m00RsqyRe" + # user.save # => false, confirmation doesn't match + # user.password_confirmation = "mUc3m00RsqyRe" + # user.save # => true + # user.authenticate("notright") # => false + # user.authenticate("mUc3m00RsqyRe") # => user + # User.find_by_name("david").try(:authenticate, "notright") # => nil + # User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user + def has_secure_password + attr_reader :password + attr_accessor :password_confirmation + + attr_protected(:password_digest, :password_salt) if respond_to?(:attr_protected) + + validates_confirmation_of :password + validates_presence_of :password_digest + validate :password_must_be_strong + end + end + + module InstanceMethods + # Returns self if the password is correct, otherwise false. + def authenticate(unencrypted_password) + password_digest == encrypt_password(unencrypted_password) ? self : false + end + + # Encrypts the password into the password_digest attribute. + def password=(unencrypted_password) + @password = unencrypted_password + self.password_digest = encrypt_password(unencrypted_password) + end + + private + def salt_for_password + self.password_salt ||= self.object_id.to_s + rand.to_s + end + + def encrypt_password(unencrypted_password) + Digest::SHA2.hexdigest(unencrypted_password + salt_for_password) + end + + def password_must_be_strong + if @password.present? + errors.add(:password, "must be longer than 6 characters") unless @password.size > 6 + errors.add(:password, "can't be 'password'") if @password == "password" + end + end + end + end +end \ No newline at end of file -- cgit v1.2.3 From 39b5ea6e01f6fc652cc63ab4e7e701cfaa9f9405 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 18 Dec 2010 15:39:32 -0800 Subject: Switch from SHA2 to BCrypt (easy Windows compatibility is coming shortly with new compiled gem) --- activemodel/lib/active_model/secure_password.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'activemodel/lib/active_model/secure_password.rb') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 0599ce6865..900205cf3f 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -1,4 +1,4 @@ -require 'digest/sha2' +require 'bcrypt' module ActiveModel module SecurePassword @@ -44,13 +44,17 @@ module ActiveModel module InstanceMethods # Returns self if the password is correct, otherwise false. def authenticate(unencrypted_password) - password_digest == encrypt_password(unencrypted_password) ? self : false + if BCrypt::Password.new(password_digest) == (unencrypted_password + salt_for_password) + self + else + false + end end # Encrypts the password into the password_digest attribute. def password=(unencrypted_password) @password = unencrypted_password - self.password_digest = encrypt_password(unencrypted_password) + self.password_digest = BCrypt::Password.create(unencrypted_password + salt_for_password) end private @@ -58,10 +62,6 @@ module ActiveModel self.password_salt ||= self.object_id.to_s + rand.to_s end - def encrypt_password(unencrypted_password) - Digest::SHA2.hexdigest(unencrypted_password + salt_for_password) - end - def password_must_be_strong if @password.present? errors.add(:password, "must be longer than 6 characters") unless @password.size > 6 -- cgit v1.2.3 From bd9dc4ff23ab1e185df6ccf35d6058c0a3d234ce Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 18 Dec 2010 19:09:07 -0800 Subject: BCrypt does its own salting, lovely! --- activemodel/lib/active_model/secure_password.rb | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'activemodel/lib/active_model/secure_password.rb') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 900205cf3f..54191f41df 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -4,17 +4,19 @@ module ActiveModel module SecurePassword extend ActiveSupport::Concern + WEAK_PASSWORDS = %w( password qwerty 123456 ) + module ClassMethods - # Adds methods to set and authenticate against a SHA2-encrypted and salted password. - # This mechanism requires you to have password_digest and password_salt attributes. + # Adds methods to set and authenticate against a BCrypt password. + # This mechanism requires you to have a password_digest attribute. # # Validations for presence of password, confirmation of password (using a "password_confirmation" attribute), - # and strength of password (at least 6 chars, not "password") are automatically added. + # and strength of password (at least 6 chars, not "password", etc) are automatically added. # You can add more validations by hand if need be. # # Example using Active Record (which automatically includes ActiveModel::SecurePassword): # - # # Schema: User(name:string, password_digest:string, password_salt:string) + # # Schema: User(name:string, password_digest:string) # class User < ActiveRecord::Base # has_secure_password # end @@ -33,7 +35,7 @@ module ActiveModel attr_reader :password attr_accessor :password_confirmation - attr_protected(:password_digest, :password_salt) if respond_to?(:attr_protected) + attr_protected(:password_digest) if respond_to?(:attr_protected) validates_confirmation_of :password validates_presence_of :password_digest @@ -44,7 +46,7 @@ module ActiveModel module InstanceMethods # Returns self if the password is correct, otherwise false. def authenticate(unencrypted_password) - if BCrypt::Password.new(password_digest) == (unencrypted_password + salt_for_password) + if BCrypt::Password.new(password_digest) == unencrypted_password self else false @@ -54,18 +56,15 @@ module ActiveModel # Encrypts the password into the password_digest attribute. def password=(unencrypted_password) @password = unencrypted_password - self.password_digest = BCrypt::Password.create(unencrypted_password + salt_for_password) + self.password_digest = BCrypt::Password.create(unencrypted_password) end - private - def salt_for_password - self.password_salt ||= self.object_id.to_s + rand.to_s - end + private def password_must_be_strong if @password.present? errors.add(:password, "must be longer than 6 characters") unless @password.size > 6 - errors.add(:password, "can't be 'password'") if @password == "password" + errors.add(:password, "is a too weak and common") if WEAK_PASSWORDS.include?(@password) end end end -- cgit v1.2.3 From d592fa946d43fdadf23f872a5c3334fb4f108f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 19 Dec 2010 09:28:15 +0100 Subject: Avoid warnings and fix small typo on SecurePassword. --- activemodel/lib/active_model/secure_password.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activemodel/lib/active_model/secure_password.rb') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 54191f41df..3e7d3174ac 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -62,9 +62,9 @@ module ActiveModel private def password_must_be_strong - if @password.present? - errors.add(:password, "must be longer than 6 characters") unless @password.size > 6 - errors.add(:password, "is a too weak and common") if WEAK_PASSWORDS.include?(@password) + if password.present? + errors.add(:password, "must be longer than 6 characters") unless password.size > 6 + errors.add(:password, "is too weak and common") if WEAK_PASSWORDS.include?(password) end end end -- cgit v1.2.3 From b8f6dd8cbb2de870a4805800fd89148a417bc612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 19 Dec 2010 09:30:46 +0100 Subject: Add missing require and remove extra module. --- activemodel/lib/active_model/secure_password.rb | 39 ++++++++++++------------- 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'activemodel/lib/active_model/secure_password.rb') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 3e7d3174ac..cd6256e3d6 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/object/blank' require 'bcrypt' module ActiveModel @@ -43,30 +44,28 @@ module ActiveModel end end - module InstanceMethods - # Returns self if the password is correct, otherwise false. - def authenticate(unencrypted_password) - if BCrypt::Password.new(password_digest) == unencrypted_password - self - else - false - end + # Returns self if the password is correct, otherwise false. + def authenticate(unencrypted_password) + if BCrypt::Password.new(password_digest) == unencrypted_password + self + else + false end + end - # Encrypts the password into the password_digest attribute. - def password=(unencrypted_password) - @password = unencrypted_password - self.password_digest = BCrypt::Password.create(unencrypted_password) - end + # Encrypts the password into the password_digest attribute. + def password=(unencrypted_password) + @password = unencrypted_password + self.password_digest = BCrypt::Password.create(unencrypted_password) + end + private - private - def password_must_be_strong - if password.present? - errors.add(:password, "must be longer than 6 characters") unless password.size > 6 - errors.add(:password, "is too weak and common") if WEAK_PASSWORDS.include?(password) - end - end + def password_must_be_strong + if password.present? + errors.add(:password, "must be longer than 6 characters") unless password.size > 6 + errors.add(:password, "is too weak and common") if WEAK_PASSWORDS.include?(password) + end end end end \ No newline at end of file -- cgit v1.2.3 From 432556b9238b182dbd380a8f9936e3ca5dc6fa57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 19 Dec 2010 09:34:31 +0100 Subject: Make password messages translatable. --- activemodel/lib/active_model/secure_password.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activemodel/lib/active_model/secure_password.rb') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index cd6256e3d6..cd997a61db 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -63,8 +63,8 @@ module ActiveModel def password_must_be_strong if password.present? - errors.add(:password, "must be longer than 6 characters") unless password.size > 6 - errors.add(:password, "is too weak and common") if WEAK_PASSWORDS.include?(password) + errors.add(:password, :too_short, :count => 7) unless password.size > 6 + errors.add(:password, :unsecure) if WEAK_PASSWORDS.include?(password) end end end -- cgit v1.2.3 From 863de37b05900f037132656812b7ef550d096ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 19 Dec 2010 09:37:08 +0100 Subject: 'unsecure' => 'insecure' --- activemodel/lib/active_model/secure_password.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activemodel/lib/active_model/secure_password.rb') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index cd997a61db..1dcd389f8a 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -64,7 +64,7 @@ module ActiveModel def password_must_be_strong if password.present? errors.add(:password, :too_short, :count => 7) unless password.size > 6 - errors.add(:password, :unsecure) if WEAK_PASSWORDS.include?(password) + errors.add(:password, :insecure) if WEAK_PASSWORDS.include?(password) end end end -- cgit v1.2.3 From a39a3337698ca42ab158dc3b4b08ea75039b8a89 Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Sun, 19 Dec 2010 20:39:54 +1100 Subject: Added ability to specify which passwords you want as weak passwords --- activemodel/lib/active_model/secure_password.rb | 31 ++++++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'activemodel/lib/active_model/secure_password.rb') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 1dcd389f8a..06af18dfd1 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -5,12 +5,10 @@ module ActiveModel module SecurePassword extend ActiveSupport::Concern - WEAK_PASSWORDS = %w( password qwerty 123456 ) - module ClassMethods # Adds methods to set and authenticate against a BCrypt password. # This mechanism requires you to have a password_digest attribute. - # + # # Validations for presence of password, confirmation of password (using a "password_confirmation" attribute), # and strength of password (at least 6 chars, not "password", etc) are automatically added. # You can add more validations by hand if need be. @@ -24,9 +22,9 @@ module ActiveModel # # user = User.new(:name => "david", :password => "secret", :password_confirmation => "nomatch") # user.save # => false, password not long enough - # user.password = "mUc3m00RsqyRe" + # user.password = "mUc3m00RsqyRe" # user.save # => false, confirmation doesn't match - # user.password_confirmation = "mUc3m00RsqyRe" + # user.password_confirmation = "mUc3m00RsqyRe" # user.save # => true # user.authenticate("notright") # => false # user.authenticate("mUc3m00RsqyRe") # => user @@ -42,6 +40,27 @@ module ActiveModel validates_presence_of :password_digest validate :password_must_be_strong end + + # Allows you to specify the set of weak passwords that will be validated against + # if you specify has_secure_password in your model. + # + # The default set of weak passwords are: + # + # class User < ActiveRecord::Base + # weak_passwords = %w( password qwerty 123456 mypass ) + # end + def weak_passwords=(*values) + @weak_passwords = values.flatten + end + + # Returns the list of current weak passwords defined. Defaults to the standard + # list of 'password', 'qwerty' and '123456' + # + # User.weak_passwords #=> ['password', 'qwerty', '123456'] + def weak_passwords + @weak_passwords ||= %w( password qwerty 123456 ) + end + end # Returns self if the password is correct, otherwise false. @@ -64,7 +83,7 @@ module ActiveModel def password_must_be_strong if password.present? errors.add(:password, :too_short, :count => 7) unless password.size > 6 - errors.add(:password, :insecure) if WEAK_PASSWORDS.include?(password) + errors.add(:password, :insecure) if self.class.weak_passwords.include?(password) end end end -- cgit v1.2.3 From fa14df08a845abc61a6ed5bed1742ba59a4c2b8d Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Sun, 19 Dec 2010 20:54:15 +1100 Subject: Fix incorrect docs --- activemodel/lib/active_model/secure_password.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'activemodel/lib/active_model/secure_password.rb') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 06af18dfd1..c5f32fa3fa 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -42,9 +42,7 @@ module ActiveModel end # Allows you to specify the set of weak passwords that will be validated against - # if you specify has_secure_password in your model. - # - # The default set of weak passwords are: + # if you specify has_secure_password in your model: # # class User < ActiveRecord::Base # weak_passwords = %w( password qwerty 123456 mypass ) -- cgit v1.2.3 From 6c217f98db6984c8d000103ac3cf66970eeaeb3f Mon Sep 17 00:00:00 2001 From: Mikel Lindsaar Date: Sun, 19 Dec 2010 21:28:37 +1100 Subject: Add set_weak_passwords call in alignment with set_table_name. --- activemodel/lib/active_model/secure_password.rb | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'activemodel/lib/active_model/secure_password.rb') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index c5f32fa3fa..6703d5daac 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -41,12 +41,18 @@ module ActiveModel validate :password_must_be_strong end - # Allows you to specify the set of weak passwords that will be validated against - # if you specify has_secure_password in your model: + # Specify the weak passwords to be used in the model: # - # class User < ActiveRecord::Base - # weak_passwords = %w( password qwerty 123456 mypass ) + # class User + # weak_passwords %w( password qwerty 123456 mypass ) # end + def set_weak_passwords(values) + @weak_passwords = values + end + + # Change the list of weak passwords that will be validated against: + # + # User.weak_passwords = %w( password qwerty 123456 mypass ) def weak_passwords=(*values) @weak_passwords = values.flatten end @@ -55,7 +61,7 @@ module ActiveModel # list of 'password', 'qwerty' and '123456' # # User.weak_passwords #=> ['password', 'qwerty', '123456'] - def weak_passwords + def weak_passwords(values = nil) @weak_passwords ||= %w( password qwerty 123456 ) end -- cgit v1.2.3 From 6d80f3a1ba52248cb8837af5ad40a23dc4fea213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 19 Dec 2010 13:30:19 +0100 Subject: Use class_attribute to ensure weak_passwords are inheritable. --- activemodel/lib/active_model/secure_password.rb | 26 ++++++++----------------- 1 file changed, 8 insertions(+), 18 deletions(-) (limited to 'activemodel/lib/active_model/secure_password.rb') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 6703d5daac..8da08f34ec 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -1,10 +1,16 @@ require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/class/attribute' require 'bcrypt' module ActiveModel module SecurePassword extend ActiveSupport::Concern + included do + class_attribute :weak_passwords + self.weak_passwords = %w( password qwerty 123456 ) + end + module ClassMethods # Adds methods to set and authenticate against a BCrypt password. # This mechanism requires you to have a password_digest attribute. @@ -44,27 +50,11 @@ module ActiveModel # Specify the weak passwords to be used in the model: # # class User - # weak_passwords %w( password qwerty 123456 mypass ) + # set_weak_passwords %w( password qwerty 123456 mypass ) # end def set_weak_passwords(values) - @weak_passwords = values - end - - # Change the list of weak passwords that will be validated against: - # - # User.weak_passwords = %w( password qwerty 123456 mypass ) - def weak_passwords=(*values) - @weak_passwords = values.flatten + self.weak_passwords = values end - - # Returns the list of current weak passwords defined. Defaults to the standard - # list of 'password', 'qwerty' and '123456' - # - # User.weak_passwords #=> ['password', 'qwerty', '123456'] - def weak_passwords(values = nil) - @weak_passwords ||= %w( password qwerty 123456 ) - end - end # Returns self if the password is correct, otherwise false. -- cgit v1.2.3 From 08ccd29b5b1e3badc2176a8036fea138b774c38f Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 19 Dec 2010 14:58:14 -0200 Subject: Remove weak_passwords list and the length/strong password validator, leave that up to the programmer --- activemodel/lib/active_model/secure_password.rb | 37 ++++--------------------- 1 file changed, 6 insertions(+), 31 deletions(-) (limited to 'activemodel/lib/active_model/secure_password.rb') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 8da08f34ec..f4411cde80 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -1,22 +1,16 @@ -require 'active_support/core_ext/object/blank' -require 'active_support/core_ext/class/attribute' +require 'active_support/concern' require 'bcrypt' module ActiveModel module SecurePassword extend ActiveSupport::Concern - included do - class_attribute :weak_passwords - self.weak_passwords = %w( password qwerty 123456 ) - end - module ClassMethods # Adds methods to set and authenticate against a BCrypt password. # This mechanism requires you to have a password_digest attribute. # - # Validations for presence of password, confirmation of password (using a "password_confirmation" attribute), - # and strength of password (at least 6 chars, not "password", etc) are automatically added. + # Validations for presence of password, confirmation of password (using + # a "password_confirmation" attribute) are automatically added. # You can add more validations by hand if need be. # # Example using Active Record (which automatically includes ActiveModel::SecurePassword): @@ -26,8 +20,8 @@ module ActiveModel # has_secure_password # end # - # user = User.new(:name => "david", :password => "secret", :password_confirmation => "nomatch") - # user.save # => false, password not long enough + # user = User.new(:name => "david", :password => "", :password_confirmation => "nomatch") + # user.save # => false, password required # user.password = "mUc3m00RsqyRe" # user.save # => false, confirmation doesn't match # user.password_confirmation = "mUc3m00RsqyRe" @@ -44,16 +38,6 @@ module ActiveModel validates_confirmation_of :password validates_presence_of :password_digest - validate :password_must_be_strong - end - - # Specify the weak passwords to be used in the model: - # - # class User - # set_weak_passwords %w( password qwerty 123456 mypass ) - # end - def set_weak_passwords(values) - self.weak_passwords = values end end @@ -71,14 +55,5 @@ module ActiveModel @password = unencrypted_password self.password_digest = BCrypt::Password.create(unencrypted_password) end - - private - - def password_must_be_strong - if password.present? - errors.add(:password, :too_short, :count => 7) unless password.size > 6 - errors.add(:password, :insecure) if self.class.weak_passwords.include?(password) - end - end end -end \ No newline at end of file +end -- cgit v1.2.3 From ab2bde45f8140a3ebf8b478e688ef612fc4181fc Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Sun, 19 Dec 2010 19:15:26 -0200 Subject: Remove require AS::Concern from places where is already in --- activemodel/lib/active_model/secure_password.rb | 1 - 1 file changed, 1 deletion(-) (limited to 'activemodel/lib/active_model/secure_password.rb') diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index f4411cde80..52941942b8 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -1,4 +1,3 @@ -require 'active_support/concern' require 'bcrypt' module ActiveModel -- cgit v1.2.3