Skip to content

Instantly share code, notes, and snippets.

@matsumos
Created December 21, 2012 10:22
Show Gist options
  • Save matsumos/4351980 to your computer and use it in GitHub Desktop.
Save matsumos/4351980 to your computer and use it in GitHub Desktop.
zxcvbn をつかって Rails のパスワードバリデーションを強化してみる ref: http://qiita.com/items/5563519a8e7f2dbc427a
gem "zxcvbn-ruby", :require => 'zxcvbn'
ja:
errors:
messages:
week_password: より複雑な%{attribute}を入力してください。あなたのパスワードは%{crack_time}で破られます。
validates :password, zxcvbn: {score: 3}
require 'zxcvbn'
class ZxcvbnValidator < ActiveModel::EachValidator
attr_reader :record, :attribute, :value
SCORE_RANGE = (1..4).freeze
def initialize(options)
unless SCORE_RANGE.include? options[:score]
raise ArgumentError, ":range must be a #{SCORE_RANGE.to_s.gsub('..', ' to ')}"
end
super
end
def validate_each(record, attribute, value)
@record, @attribute, @value = record, attribute, value
@score = options[:score]
add_error unless valid?
end
private
def valid?
@messages_options = {}
week_keywords = []
for key, val in @record.attributes
next if %w[password password_confirmation encrypted_password].include?(key)
next unless val.kind_of?(String)
week_keywords.push val if val
for splited_val in val.split(/[@._]/)
week_keywords.push splited_val
end
end
result = Zxcvbn.test(value, week_keywords)
@messages_options[:crack_time] = display_time result.crack_time
result.score >= @score
end
def add_error
if message = options[:message]
record.errors[attribute] << message
else
record.errors.add attribute, :week_password, @messages_options
end
end
def display_time(seconds)
minute = 60
hour = minute * 60
day = hour * 24
month = day * 31
year = month * 12
century = year * 100
case
when seconds < minute
unit = 'x_seconds'
count = 1 + seconds.ceil
when seconds < hour
unit = 'x_minutes'
count = 1 + (seconds / minute).ceil
when seconds < day
unit = 'about_x_hours'
count = 1 + (seconds / hour).ceil
when seconds < month
unit = 'x_days'
count = 1 + (seconds / day).ceil
when seconds < year
unit = 'x_months'
count = 1 + (seconds / month).ceil
when seconds < century
unit = 'x_years'
count = 1 + (seconds / year).ceil
end
I18n.t "#{unit}.other", scope: [:datetime, :distance_in_words], count: count
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment