Skip to content
Open
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
111 changes: 111 additions & 0 deletions lib/puppet/functions/secretbox.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# This is an autogenerated function, ported from the original legacy version.
# It /should work/ as is, but will not have all the benefits of the modern
# function API. You should see the function docs to learn how to add function
# signatures for type safety and to document this function using puppet-strings.
#
# https://puppet.com/docs/puppet/latest/custom_functions_ruby.html
#
# ---- original file header ----
# Define the secretbox method; see README for more information and usage.
# ---- original file header ----
#
# @summary
# Requires an index be passed as the first parameter. This index, along with the
#node's FQDN will be used to uniquely identify a secret. If the secret doesn't
#exist prior to the call, it will be generated. In this instance, secretbox can
#accept a second argument, which specifies the length of the randomly generated
#value. If the second value is left unspecified, it defaults to 32 characters
#long.
#
#The generated value can contain any printable ASCII value (character codes 32
#through 126), excluding single quote ('), double quotes ("), forward slash (/)
#and hash (#).
#
#Upon generation, the value is saved to a file named the passed index. The file
#is stored in a directory named after the FQDN. This directory is then stored
#within the 'secretbox' directory, underneath Puppet's 'vardir'. In practice,
#a given index has it's value stored in `/var/lib/puppet/secretbox/FQDN/index`.
#
#
Puppet::Functions.create_function(:'secretbox') do
# @param args
# The original array of arguments. Port this to individually managed params
# to get the full benefit of the modern function API.
#
# @return [Data type]
# Describe what the function returns here
#
dispatch :default_impl do
# Call the method named 'default_impl' when this is matched
# Port this to match individual params for better type safety
repeated_param 'Any', :args
end


def default_impl(*args)

# There must be a key passed as the first argument, which is used to index
# the generated value
fail Puppet::ParseError, 'Missing persistance index' if args[0].nil?

# Where the files are stored between runs. This directory should be
# locked-down, permissions-wise.
persistance_dir = File.join(Puppet[:vardir], 'secretbox', lookupvar('fqdn'))
# The individual file that actually stores the contents
persistance_file = File.join(persistance_dir, args[0])

# Make the directory if it doesn't already exist.
unless File.directory? persistance_dir
require 'fileutils'
FileUtils.mkdir_p persistance_dir
end

if File.exist? persistance_file
# The file exists, assume it's contents are what we wanted.
open(persistance_file).read.chomp
else
# Otherwise, generate a string
require 'securerandom'
if args[1] == nil
length = 32
else
length = args[1].to_i
end
if args[2] == nil
sr_method = 'legacy'
else
sr_method = args[2]
end
if sr_method == 'legacy'
# We use all ASCII values 32 through 126, excluding a few: ' " # /
characters = (32..126).to_a - [35, 34, 39, 47]
password = SecureRandom.random_bytes(length).each_char.map do |char|
begin
character_code = char.ord
rescue NoMethodError # For Ruby 1.8 compatibility, which stock puppet on RHEL runs on :(
character_code = char[0]
end
characters[(character_code % characters.length)].chr
end.join
else
begin
password = SecureRandom.send(sr_method, length)
rescue NoMethodError # They specified a SecureRandom method that doesn't exist
raise NoMethodError, "Please see You have specified a SecureRandom method that doesn't exist"
end
end

# Save the generated string to a file so that it's the same in subsequent
# calls (assuming the same index)
save_file = open(persistance_file, 'w')
save_file.puts(password)
save_file.close

# And return the value so that the puppet variable/parameter is, of
# course, set.
p password
password
end

end
end
41 changes: 41 additions & 0 deletions spec/functions/_secretbox_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require 'spec_helper'

describe 'secretbox' do
# without knowing details about the implementation, this is the only test
# case that we can autogenerate. You should add more examples below!
it { is_expected.not_to eq(nil) }

#################################
# Below are some example test cases. You may uncomment and modify them to match
# your needs. Notice that they all expect the base error class of `StandardError`.
# This is because the autogenerated function uses an untyped array for parameters
# and relies on your implementation to do the validation. As you convert your
# function to proper dispatches and typed signatures, you should change the
# expected error of the argument validation examples to `ArgumentError`.
#
# Other error types you might encounter include
#
# * StandardError
# * ArgumentError
# * Puppet::ParseError
#
# Read more about writing function unit tests at https://rspec-puppet.com/documentation/functions/
#
# it 'raises an error if called with no argument' do
# is_expected.to run.with_params.and_raise_error(StandardError)
# end
#
# it 'raises an error if there is more than 1 arguments' do
# is_expected.to run.with_params({ 'foo' => 1 }, 'bar' => 2).and_raise_error(StandardError)
# end
#
# it 'raises an error if argument is not the proper type' do
# is_expected.to run.with_params('foo').and_raise_error(StandardError)
# end
#
# it 'returns the proper output' do
# is_expected.to run.with_params(123).and_return('the expected output')
# end
#################################

end