forked from redhat-openstack/openstack-puppet-modules
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #408 from elyscape/feature/pw_hash
(MODULES-1737) Add pw_hash() function
Showing
4 changed files
with
195 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
Puppet::Parser::Functions::newfunction( | ||
:pw_hash, | ||
:type => :rvalue, | ||
:arity => 3, | ||
:doc => "Hashes a password using the crypt function. Provides a hash | ||
usable on most POSIX systems. | ||
The first argument to this function is the password to hash. If it is | ||
undef or an empty string, this function returns undef. | ||
The second argument to this function is which type of hash to use. It | ||
will be converted into the appropriate crypt(3) hash specifier. Valid | ||
hash types are: | ||
|Hash type |Specifier| | ||
|---------------------|---------| | ||
|MD5 |1 | | ||
|SHA-256 |5 | | ||
|SHA-512 (recommended)|6 | | ||
The third argument to this function is the salt to use. | ||
Note: this uses the Puppet Master's implementation of crypt(3). If your | ||
environment contains several different operating systems, ensure that they | ||
are compatible before using this function.") do |args| | ||
raise ArgumentError, "pw_hash(): wrong number of arguments (#{args.size} for 3)" if args.size != 3 | ||
raise ArgumentError, "pw_hash(): first argument must be a string" unless args[0].is_a? String or args[0].nil? | ||
raise ArgumentError, "pw_hash(): second argument must be a string" unless args[1].is_a? String | ||
hashes = { 'md5' => '1', | ||
'sha-256' => '5', | ||
'sha-512' => '6' } | ||
hash_type = hashes[args[1].downcase] | ||
raise ArgumentError, "pw_hash(): #{args[1]} is not a valid hash type" if hash_type.nil? | ||
raise ArgumentError, "pw_hash(): third argument must be a string" unless args[2].is_a? String | ||
raise ArgumentError, "pw_hash(): third argument must not be empty" if args[2].empty? | ||
raise ArgumentError, "pw_hash(): characters in salt must be in the set [a-zA-Z0-9./]" unless args[2].match(/\A[a-zA-Z0-9.\/]+\z/) | ||
|
||
password = args[0] | ||
return nil if password.nil? or password.empty? | ||
|
||
# handle weak implementations of String#crypt | ||
if 'test'.crypt('$1$1') != '$1$1$Bp8CU9Oujr9SSEw53WV6G.' | ||
# JRuby < 1.7.17 | ||
if RUBY_PLATFORM == 'java' | ||
# override String#crypt for password variable | ||
def password.crypt(salt) | ||
# puppetserver bundles Apache Commons Codec | ||
org.apache.commons.codec.digest.Crypt.crypt(self.to_java_bytes, salt) | ||
end | ||
else | ||
# MS Windows and other systems that don't support enhanced salts | ||
raise Puppet::ParseError, 'system does not support enhanced salts' | ||
end | ||
end | ||
password.crypt("$#{hash_type}$#{args[2]}") | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#! /usr/bin/env ruby -S rspec | ||
require 'spec_helper_acceptance' | ||
|
||
# Windows and OS X do not have useful implementations of crypt(3) | ||
describe 'pw_hash function', :unless => (UNSUPPORTED_PLATFORMS + ['windows', 'Darwin']).include?(fact('operatingsystem')) do | ||
describe 'success' do | ||
it 'hashes passwords' do | ||
pp = <<-EOS | ||
$o = pw_hash('password', 6, 'salt') | ||
notice(inline_template('pw_hash is <%= @o.inspect %>')) | ||
EOS | ||
|
||
apply_manifest(pp, :catch_failures => true) do |r| | ||
expect(r.stdout).to match(/pw_hash is "\$6\$salt\$IxDD3jeSOb5eB1CX5LBsqZFVkJdido3OUILO5Ifz5iwMuTS4XMS130MTSuDDl3aCI6WouIL9AjRbLCelDCy\.g\."/) | ||
end | ||
end | ||
|
||
it 'returns nil if no password is provided' do | ||
pp = <<-EOS | ||
$o = pw_hash('', 6, 'salt') | ||
notice(inline_template('pw_hash is <%= @o.inspect %>')) | ||
EOS | ||
|
||
apply_manifest(pp, :catch_failures => true) do |r| | ||
expect(r.stdout).to match(/pw_hash is ""/) | ||
end | ||
end | ||
end | ||
describe 'failure' do | ||
it 'handles less than three arguments' | ||
it 'handles more than three arguments' | ||
it 'handles non strings' | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
#! /usr/bin/env ruby -S rspec | ||
require 'spec_helper' | ||
|
||
describe "the pw_hash function" do | ||
let(:scope) { PuppetlabsSpec::PuppetInternals.scope } | ||
|
||
it "should exist" do | ||
expect(Puppet::Parser::Functions.function("pw_hash")).to eq("function_pw_hash") | ||
end | ||
|
||
it "should raise an ArgumentError if there are less than 3 arguments" do | ||
expect { scope.function_pw_hash([]) }.to( raise_error(ArgumentError, /[Ww]rong number of arguments/) ) | ||
expect { scope.function_pw_hash(['password']) }.to( raise_error(ArgumentError, /[Ww]rong number of arguments/) ) | ||
expect { scope.function_pw_hash(['password', 'sha-512']) }.to( raise_error(ArgumentError, /[Ww]rong number of arguments/) ) | ||
end | ||
|
||
it "should raise an ArgumentError if there are more than 3 arguments" do | ||
expect { scope.function_pw_hash(['password', 'sha-512', 'salt', 5]) }.to( raise_error(ArgumentError, /[Ww]rong number of arguments/) ) | ||
end | ||
|
||
it "should raise an ArgumentError if the first argument is not a string" do | ||
expect { scope.function_pw_hash([['password'], 'sha-512', 'salt']) }.to( raise_error(ArgumentError, /first argument must be a string/) ) | ||
# in Puppet 3, numbers are passed as strings, so we can't test that | ||
end | ||
|
||
it "should return nil if the first argument is empty" do | ||
expect(scope.function_pw_hash(['', 'sha-512', 'salt'])).to eq(nil) | ||
end | ||
|
||
it "should return nil if the first argument is undef" do | ||
expect(scope.function_pw_hash([nil, 'sha-512', 'salt'])).to eq(nil) | ||
end | ||
|
||
it "should raise an ArgumentError if the second argument is an invalid hash type" do | ||
expect { scope.function_pw_hash(['', 'invalid', 'salt']) }.to( raise_error(ArgumentError, /not a valid hash type/) ) | ||
end | ||
|
||
it "should raise an ArgumentError if the second argument is not a string" do | ||
expect { scope.function_pw_hash(['', [], 'salt']) }.to( raise_error(ArgumentError, /second argument must be a string/) ) | ||
end | ||
|
||
it "should raise an ArgumentError if the third argument is not a string" do | ||
expect { scope.function_pw_hash(['password', 'sha-512', ['salt']]) }.to( raise_error(ArgumentError, /third argument must be a string/) ) | ||
# in Puppet 3, numbers are passed as strings, so we can't test that | ||
end | ||
|
||
it "should raise an ArgumentError if the third argument is empty" do | ||
expect { scope.function_pw_hash(['password', 'sha-512', '']) }.to( raise_error(ArgumentError, /third argument must not be empty/) ) | ||
end | ||
|
||
it "should raise an ArgumentError if the third argument has invalid characters" do | ||
expect { scope.function_pw_hash(['password', 'sha-512', '%']) }.to( raise_error(ArgumentError, /characters in salt must be in the set/) ) | ||
end | ||
|
||
it "should fail on platforms with weak implementations of String#crypt" do | ||
String.any_instance.expects(:crypt).with('$1$1').returns('$1SoNol0Ye6Xk') | ||
expect { scope.function_pw_hash(['password', 'sha-512', 'salt']) }.to( raise_error(Puppet::ParseError, /system does not support enhanced salts/) ) | ||
end | ||
|
||
it "should return a hashed password" do | ||
result = scope.function_pw_hash(['password', 'sha-512', 'salt']) | ||
expect(result).to eql('$6$salt$IxDD3jeSOb5eB1CX5LBsqZFVkJdido3OUILO5Ifz5iwMuTS4XMS130MTSuDDl3aCI6WouIL9AjRbLCelDCy.g.') | ||
end | ||
|
||
it "should use the specified salt" do | ||
result = scope.function_pw_hash(['password', 'sha-512', 'salt']) | ||
expect(result).to match('salt') | ||
end | ||
|
||
it "should use the specified hash type" do | ||
resultmd5 = scope.function_pw_hash(['password', 'md5', 'salt']) | ||
resultsha256 = scope.function_pw_hash(['password', 'sha-256', 'salt']) | ||
resultsha512 = scope.function_pw_hash(['password', 'sha-512', 'salt']) | ||
|
||
expect(resultmd5).to eql('$1$salt$qJH7.N4xYta3aEG/dfqo/0') | ||
expect(resultsha256).to eql('$5$salt$Gcm6FsVtF/Qa77ZKD.iwsJlCVPY0XSMgLJL0Hnww/c1') | ||
expect(resultsha512).to eql('$6$salt$IxDD3jeSOb5eB1CX5LBsqZFVkJdido3OUILO5Ifz5iwMuTS4XMS130MTSuDDl3aCI6WouIL9AjRbLCelDCy.g.') | ||
end | ||
|
||
it "should generate a valid hash" do | ||
password_hash = scope.function_pw_hash(['password', 'sha-512', 'salt']) | ||
|
||
hash_parts = password_hash.match(%r{\A\$(.*)\$([a-zA-Z0-9./]+)\$([a-zA-Z0-9./]+)\z}) | ||
|
||
expect(hash_parts).not_to eql(nil) | ||
end | ||
end |