require 'al-test-utils'

class TestUserPassword < Test::Unit::TestCase
  priority :must

  priority :normal
  def test_valid?
    {
      "{CRYPT}.yNLaKqtwQbnY" => 'wibble', #CRYPT
      "{MD5}DRB9CfW75Ayt495ccenptw==" => 'letmein', #MD5
      "{SMD5}8L2iXJuazftLVHrAf7ptPFQIDaw=" => 'letmein', #SMD5 as generated by slappasswd (4 bytes of salt)
      "{SMD5}kXibTNG+O98gaQtkugYcmSTiE+M2Z5TA" => 'letmein', #SMD5 as generated by Apache Directory Studio (8 bytes of salt)
      "{SHA}t6h1/B6iKLkGEEG3zsS9PFKrPOM=" => 'letmein', #SHA
      "{SSHA}YA87hc9/L/cCGR1HValcJb7a8AYxZXY4" => 'wibble', # SSHA as generated by slappasswd (4 bytes of salt)
      "{SSHA}6J6Ios3l1panY9sm0+g9l3/jFz2kwOPrVA4+OA==" => 'letmein', # SSHA as generated by Apache Directory Studio (8 bytes of salt)
      "letmein" => 'letmein', #Cleartext password
    }.each do |hash, plain|
      assert_send([ActiveLdap::UserPassword, :valid?,
                   plain, hash])
      assert_not_send([ActiveLdap::UserPassword, :valid?,
                   "not#{plain}", hash])
    end
  end

  def test_crypt
    salt = ".WoUoU9f3IlUx9Hh7D/8y.xA6ziklGib"
    assert_equal("{CRYPT}.W57FZhV52w0s",
                 ActiveLdap::UserPassword.crypt("password", salt))

    password = "PASSWORD"
    hashed_password = ActiveLdap::UserPassword.crypt(password)
    salt = hashed_password.sub(/^\{CRYPT\}/, '')
    assert_equal(hashed_password,
                 ActiveLdap::UserPassword.crypt(password, salt))
  end

  def test_extract_salt_for_crypt
    assert_extract_salt(:crypt, "AB", "ABCDE")
    assert_extract_salt(:crypt, "$1", "$1")
    assert_extract_salt(:crypt, "$1$$", "$1$")
    assert_extract_salt(:crypt, "$1$$", "$1$$")
    assert_extract_salt(:crypt, "$1$abcdefgh$", "$1$abcdefgh$")
    assert_extract_salt(:crypt, "$1$abcdefgh$", "$1$abcdefghi$")
  end

  def test_md5
    assert_equal("{MD5}X03MO1qnZdYdgyfeuILPmQ==",
                 ActiveLdap::UserPassword.md5("password"))
  end

  def test_smd5
    assert_equal("{SMD5}gjz+SUSfZaux99Xsji/No200cGI=",
                 ActiveLdap::UserPassword.smd5("password", "m4pb"))

    password = "PASSWORD"
    hashed_password = ActiveLdap::UserPassword.smd5(password)
    salt = decode64(hashed_password.sub(/^\{SMD5\}/, ''))[-4, 4]
    assert_equal(hashed_password,
                 ActiveLdap::UserPassword.smd5(password, salt))
  end

  def test_extract_salt_for_smd5
    assert_extract_salt(:smd5, 'this', encode64("1234567890123456this"))
    assert_extract_salt(:smd5, 'this is the salt', encode64("1234567890123456this is the salt"))
    assert_extract_salt(:smd5, nil, encode64("123456789"))
    assert_extract_salt(:smd5, nil, encode64("123456789012345"))
    assert_extract_salt(:smd5, nil, encode64("1234567890123456"))
  end

  def test_sha
    assert_equal("{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=",
                 ActiveLdap::UserPassword.sha("password"))
  end

  def test_ssha
    assert_equal("{SSHA}ipnlCLA1HaK3mm3hyneJIp+Px2h1RGk3",
                 ActiveLdap::UserPassword.ssha("password", "uDi7"))

    password = "PASSWORD"
    hashed_password = ActiveLdap::UserPassword.ssha(password)
    salt = decode64(hashed_password.sub(/^\{SSHA\}/, ''))[-4, 4]
    assert_equal(hashed_password,
                 ActiveLdap::UserPassword.ssha(password, salt))
  end

  def test_extract_salt_for_ssha
    assert_extract_salt(:ssha, 'this', encode64("12345678901234567890this"))
    assert_extract_salt(:ssha, 'this is the salt', encode64("12345678901234567890this is the salt"))
    assert_extract_salt(:ssha, nil, encode64("12345678901234"))
    assert_extract_salt(:ssha, nil, encode64("1234567890123456789"))
    assert_extract_salt(:ssha, nil, encode64("12345678901234567890"))
  end

  private
  def assert_extract_salt(type, expected, hashed_password)
    actual = ActiveLdap::UserPassword.send("extract_salt_for_#{type}",
                                           hashed_password)
    assert_equal(expected, actual)
  end

  def encode64(string)
    [string].pack('m').chomp
  end

  def decode64(string)
    string.unpack('m')[0]
  end
end
