Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions lib/puppet/parser/functions/str2saltedsha512.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#
# str2saltedsha512.rb
#

module Puppet::Parser::Functions
newfunction(:str2saltedsha512, :type => :rvalue, :doc => <<-EOS
This converts a string to a salted-SHA512 password hash (which is used for
OS X versions >= 10.7). Given any simple string, you will get a hex version
of a salted-SHA512 password hash that can be inserted into your Puppet
manifests as a valid password attribute.
EOS
) do |arguments|
require 'digest/sha2'

raise(Puppet::ParseError, "str2saltedsha512(): Wrong number of arguments " +
"passed (#{arguments.size} but we require 1)") if arguments.size != 1

password = arguments[0]

unless password.is_a?(String)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's a good idea to use is_a?(String) here. We might pass in something that's a kind of String like object but isn't a String itself.

It'll probably be more dynamic if we just use the object's to_s method to convert it to a string representation, whatever the heck it is.

The spec tests should then cover our expectations about how different input objects behave.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, on second thought the explicit catching of the String is probably a better UX if the user accidentally passes in a Hash or Array.

raise(Puppet::ParseError, 'str2saltedsha512(): Requires a ' +
"String argument, you passed: #{password.class}")
end

seedint = rand(2**31 - 1)
seedstring = Array(seedint).pack("L")
saltedpass = Digest::SHA512.digest(seedstring + password)
(seedstring + saltedpass).unpack('H*')[0]
end
end

# vim: set ts=2 sw=2 et :
51 changes: 51 additions & 0 deletions spec/unit/puppet/parser/functions/str2saltedsha512_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env rspec
require 'spec_helper'

describe "the str2saltedsha512 function" do
before :all do
Puppet::Parser::Functions.autoloader.loadall
end

before :each do
@scope = Puppet::Parser::Scope.new
end

it "should exist" do
Puppet::Parser::Functions.function("str2saltedsha512").should == "function_str2saltedsha512"
end

it "should raise a ParseError if there is less than 1 argument" do
expect { @scope.function_str2saltedsha512([]) }.should( raise_error(Puppet::ParseError) )
end

it "should raise a ParseError if there is more than 1 argument" do
expect { @scope.function_str2saltedsha512(['foo', 'bar', 'baz']) }.should( raise_error(Puppet::ParseError) )
end

it "should return a salted-sha512 password hash 136 characters in length" do
result = @scope.function_str2saltedsha512(["password"])
result.length.should(eq(136))
end

it "should raise an error if you pass a non-string password" do
expect { @scope.function_str2saltedsha512([1234]) }.should( raise_error(Puppet::ParseError) )
end

it "should generate a valid password" do
# Allow the function to generate a password based on the string 'password'
password_hash = @scope.function_str2saltedsha512(["password"])

# Separate the Salt and Password from the Password Hash
salt = password_hash[0..7]
password = password_hash[8..-1]

# Convert the Salt and Password from Hex to Binary Data
str_salt = Array(salt.lines).pack('H*')
str_password = Array(password.lines).pack('H*')

# Combine the Binary Salt with 'password' and compare the end result
saltedpass = Digest::SHA512.digest(str_salt + 'password')
result = (str_salt + saltedpass).unpack('H*')[0]
result.should == password_hash
end
end