mirror of
https://github.com/discourse/discourse.git
synced 2024-11-29 20:24:05 -06:00
9238767f7e
Previously, Discourse's password hashing was hard-coded to a specific algorithm and parameters. Any changes to the algorithm or parameters would essentially invalidate all existing user passwords. This commit introduces a new `password_algorithm` column on the `users` table. This persists the algorithm/parameters which were use to generate the hash for a given user. All existing rows in the users table are assumed to be using Discourse's current algorithm/parameters. With this data stored per-user in the database, we'll be able to keep existing passwords working while adjusting the algorithm/parameters for newly hashed passwords. Passwords which were hashed with an old algorithm will be automatically re-hashed with the new algorithm when the user next logs in. Values in the `password_algorithm` column are based on the PHC string format (https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md). Discourse's existing algorithm is described by the string `$pbkdf2-sha256$i=64000,l=32$` To introduce a new algorithm and start using it, make sure it's implemented in the `PasswordHasher` library, then update `User::TARGET_PASSWORD_ALGORITHM`.
67 lines
2.1 KiB
Ruby
67 lines
2.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
describe PasswordHasher do
|
|
def hash(password: "mypass", salt: "mysalt", algorithm:)
|
|
PasswordHasher.hash_password(password: password, salt: salt, algorithm: algorithm)
|
|
end
|
|
|
|
describe "pbkdf2-sha256 algorithm" do
|
|
it "can hash correctly" do
|
|
result = hash(password: "mypass", salt: "mysalt", algorithm: "$pbkdf2-sha256$i=3,l=32$")
|
|
expect(result).to eq("8d4f9a685ff73eef7b06e07ab5889784775290a0a9cebb6eb4492a695c93a51e")
|
|
end
|
|
|
|
it "supports different iteration numbers" do
|
|
iter_3 = hash(algorithm: "$pbkdf2-sha256$i=3,l=32$")
|
|
iter_4 = hash(algorithm: "$pbkdf2-sha256$i=4,l=32$")
|
|
|
|
expect(iter_3).not_to eq(iter_4)
|
|
end
|
|
|
|
it "raises an error for non-standard length" do
|
|
expect { hash(algorithm: "$pbkdf2-sha256$i=3,l=20$") }.to raise_error(
|
|
PasswordHasher::UnsupportedAlgorithmError,
|
|
)
|
|
end
|
|
|
|
it "raises an error for missing length param" do
|
|
expect { hash(algorithm: "$pbkdf2-sha256$i=3$") }.to raise_error(
|
|
PasswordHasher::UnsupportedAlgorithmError,
|
|
)
|
|
end
|
|
|
|
it "raises an error for missing iteration param" do
|
|
expect { hash(algorithm: "$pbkdf2-sha256$l=32$") }.to raise_error(
|
|
PasswordHasher::UnsupportedAlgorithmError,
|
|
)
|
|
end
|
|
|
|
it "raises an error for missing salt" do
|
|
expect { hash(salt: nil, algorithm: "$pbkdf2-sha256$l=32,i=3$") }.to raise_error(
|
|
ArgumentError,
|
|
)
|
|
end
|
|
|
|
it "raises an error for missing password" do
|
|
expect { hash(password: nil, algorithm: "$pbkdf2-sha256$l=32,i=3$") }.to raise_error(
|
|
ArgumentError,
|
|
)
|
|
end
|
|
end
|
|
|
|
it "raises error for invalid algorithm" do
|
|
expect { hash(algorithm: "$pbkdf2-sha256$l=32$somethinginvalid") }.to raise_error(
|
|
PasswordHasher::InvalidAlgorithmError,
|
|
)
|
|
end
|
|
|
|
it "raises error for unknown algorithm" do
|
|
expect { hash(algorithm: "$pbkdf2-invalid$l=32$") }.to raise_error(
|
|
PasswordHasher::UnsupportedAlgorithmError,
|
|
)
|
|
expect { hash(algorithm: "$unknown$l=32$") }.to raise_error(
|
|
PasswordHasher::UnsupportedAlgorithmError,
|
|
)
|
|
end
|
|
end
|