From 33c6997ded3f3553f79dca10483a4d3955b6cb85 Mon Sep 17 00:00:00 2001
From: Neil Lalonde <neillalonde@gmail.com>
Date: Thu, 19 Dec 2013 15:12:03 -0500
Subject: [PATCH] Move password validation into PasswordValidator

---
 app/models/user.rb                            | 18 +++++---
 lib/validators/password_validator.rb          | 12 +++++
 .../validators/password_validator_spec.rb     | 46 +++++++++++++++++++
 3 files changed, 70 insertions(+), 6 deletions(-)
 create mode 100644 lib/validators/password_validator.rb
 create mode 100644 spec/components/validators/password_validator_spec.rb

diff --git a/app/models/user.rb b/app/models/user.rb
index 4e4831c3554..44ff39d55e0 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -246,11 +246,23 @@ class User < ActiveRecord::Base
     @raw_password = password unless password.blank?
   end
 
+  def password
+    '' # so that validator doesn't complain that a password attribute doesn't exist
+  end
+
   # Indicate that this is NOT a passwordless account for the purposes of validation
   def password_required!
     @password_required = true
   end
 
+  def password_required?
+    !!@password_required
+  end
+
+  def password_validator
+    PasswordValidator.new(attributes: :password).validate_each(self, :password, @raw_password)
+  end
+
   def confirm_password?(password)
     return false unless password_hash && salt
     self.password_hash == hash_password(password, salt)
@@ -561,12 +573,6 @@ class User < ActiveRecord::Base
     end
   end
 
-  def password_validator
-    if (@raw_password && @raw_password.length < 6) || (@password_required && !@raw_password)
-      errors.add(:password, "must be 6 letters or longer")
-    end
-  end
-
   def send_approval_email
     Jobs.enqueue(:user_email,
       type: :signup_after_approval,
diff --git a/lib/validators/password_validator.rb b/lib/validators/password_validator.rb
new file mode 100644
index 00000000000..9f87fdecd68
--- /dev/null
+++ b/lib/validators/password_validator.rb
@@ -0,0 +1,12 @@
+class PasswordValidator < ActiveModel::EachValidator
+
+  def validate_each(record, attribute, value)
+    return unless record.password_required?
+    if value.nil?
+      record.errors.add(attribute, :blank)
+    elsif value.length < 6
+      record.errors.add(attribute, :too_short, count: 6)
+    end
+  end
+
+end
diff --git a/spec/components/validators/password_validator_spec.rb b/spec/components/validators/password_validator_spec.rb
new file mode 100644
index 00000000000..cdaa6a38d0e
--- /dev/null
+++ b/spec/components/validators/password_validator_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe PasswordValidator do
+
+  let(:validator) { described_class.new({attributes: :password}) }
+  subject(:validate) { validator.validate_each(record,:password,@password) }
+
+  context "password required" do
+    let(:record) { u = Fabricate.build(:user, password: @password); u.password_required!; u }
+
+    it "doesn't add an error when password is good" do
+      @password = "weron235alsfn234"
+      validate
+      record.errors[:password].should_not be_present
+    end
+
+    it "adds an error when password is too short" do
+      @password = "p"
+      validate
+      record.errors[:password].should be_present
+    end
+
+    it "adds an error when password is blank" do
+      @password = ''
+      validate
+      record.errors[:password].should be_present
+    end
+
+    it "adds an error when password is nil" do
+      @password = nil
+      validate
+      record.errors[:password].should be_present
+    end
+  end
+
+  context "password not required" do
+    let(:record) { Fabricate.build(:user, password: @password) }
+
+    it "doesn't add an error if password is not required" do
+      @password = nil
+      validate
+      record.errors[:password].should_not be_present
+    end
+  end
+
+end