いくつかの制限を設けた、初期値パスワードとして使うランダム文字列をユルく生成する。
要件
- 一部のまぎらわしい文字を使用しない(今回は o, O, 0)
- 同じ文字が3つ以上連続しない
- 記号を1〜2つ含むよう指示するオプションを設ける
- 最初と最後の文字には記号は使わない
制限
- 生成できる長さは 32 文字までとする
- これ以上増やすと、連続しないパターンを生成する計算量が爆発する
使用例
Password.new.generate(16) # hk84wnIskvCBDITz
Password.new(symbol: true).generate(16) # EIf{7fpkexis.vvs
実装
class Password
require 'securerandom'
CHAR_LIST = 'abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ123456789'.split('').freeze # omit o,O,0
SYMBOL_LIST = '._-=[]{}+#^!?'.split('').freeze
# SYMBOL_LIST = '"#$%&\'()*+,-./:;<=>?[\]^_`{|}~'.split('').freeze
SYMBOL_LIST_ESCAPED = SYMBOL_LIST.reduce('') { |r, s| r + '\\' + s }.freeze
def initialize(symbol: false)
@symbol = symbol
end
def generate(len = 8)
raise 'too short' if len < 4
raise 'too long' if len > 32
loop do
result = (0..len - 1).reduce('') { |r, n| r + pick_one(len, n) }
return result if check(result)
end
end
private
def pick_one(len, n)
list = char_list(len, n)
list[(SecureRandom.random_number(1.0) * list.size).floor]
end
def char_list(len, n)
!@symbol || (n.zero? || n == len - 1) ? CHAR_LIST : CHAR_LIST + SYMBOL_LIST
end
def count_symbol(str)
str.scan(Regexp.new('[%s]' % SYMBOL_LIST_ESCAPED)).size
end
def check(str)
# omit successive chars
return false if /(.)\1\1/ =~ str
# confirm the str contains 1..2 symbols
if @symbol
return false if count_symbol(str).zero?
return false if count_symbol(str) > 2
end
true
end
end