diff --git a/lib/puppet/provider/f5.rb b/lib/puppet/provider/f5.rb index 5607080..942f574 100644 --- a/lib/puppet/provider/f5.rb +++ b/lib/puppet/provider/f5.rb @@ -40,6 +40,10 @@ def self.post(url, message) def self.put(url, message) transport.put(url, message) end + + def self.patch(url, message) + transport.patch(url, message) + end def self.delete(url) transport.delete(url) diff --git a/lib/puppet/provider/f5_profilehttp/rest.rb b/lib/puppet/provider/f5_profilehttp/rest.rb index c7cc042..7082c28 100644 --- a/lib/puppet/provider/f5_profilehttp/rest.rb +++ b/lib/puppet/provider/f5_profilehttp/rest.rb @@ -1,4 +1,4 @@ -require 'puppet/provider/f5' +require File.join(File.dirname(__FILE__), '../f5') require 'json' Puppet::Type.type(:f5_profilehttp).provide(:rest, parent: Puppet::Provider::F5) do @@ -12,11 +12,18 @@ def self.instances full_path_uri = profile['fullPath'].gsub('/','~') instances << new( - ensure: :present, - name: profile['fullPath'], - description: profile['description'], - fallback_host: profile['fallbackHost'], - fallback_status_codes: profile['fallbackStatusCodes'], + ensure: :present, + name: profile['fullPath'], + description: profile['description'], + fallback_host: profile['fallbackHost'], + fallback_status_codes: profile['fallbackStatusCodes'], + defaults_from: profile['defaultsFrom'], + encrypt_cookies: profile['encryptCookies'], + encrypt_cookie_secret: profile['encryptCookieSecret'], + hsts_mode: profile['hsts']['mode'], + hsts_maximum_age:profile['hsts']['maximumAge'], + hsts_preload:profile['hsts']['preload'], + hsts_include_subdomains:profile['hsts']['includeSubdomains'], ) end @@ -26,16 +33,17 @@ def self.instances def self.prefetch(resources) profiles = instances resources.keys.each do |name| - if provider = profiles.find { |profile| profile.name == name } + if provider = profiles.find {|profile| profile.name == name} resources[name].provider = provider end end end - def create_message(basename, hash) + def create_message(basename, partition, hash) # Create the message by stripping :present. - new_hash = hash.reject { |k, _| [:ensure, :provider, Puppet::Type.metaparams].flatten.include?(k) } - new_hash[:name] = basename + new_hash = hash.reject {|k, _| [:ensure, :provider, Puppet::Type.metaparams].flatten.include?(k)} + new_hash[:name] = basename + new_hash[:partition] = partition return new_hash end @@ -48,23 +56,40 @@ def message(object) # Map for conversion in the message. map = { - :'fallback-host' => :fallbackHost, - :'fallback-status-codes' => :fallbackStatusCodes, + :'fallback-host' => :fallbackHost, + :'fallback-status-codes' => :fallbackStatusCodes, + :'encrypt-cookies' => :encryptCookies, + :'encrypt-cookie-secret' => :encryptCookieSecret, + :'defaults-from' => :defaultsFrom, } message = strip_nil_values(message) + message = convert_hsts(message) message = convert_underscores(message) - message = create_message(basename, message) + message = create_message(basename, partition, message) message = rename_keys(map, message) message = string_to_integer(message) message.to_json end + def convert_hsts(hash) + hash[:hsts] = + rename_keys( + { + :hsts_maximum_age => 'maximum-age', + :hsts_include_subdomains => 'include-subdomains', + :hsts_mode => 'mode', + :hsts_preload => 'preload', + }, + strip_nil_values(hash.select {|key, value| key.to_s.start_with?('hsts_')})) + hash.reject {|key, value| key.to_s.start_with?('hsts_')} + end + def flush if @property_hash != {} full_path_uri = resource[:name].gsub('/','~') - result = Puppet::Provider::F5.put("/mgmt/tm/ltm/profile/http/#{full_path_uri}", message(resource)) + result = Puppet::Provider::F5.patch("/mgmt/tm/ltm/profile/http/#{full_path_uri}", message(resource)) end return result end diff --git a/lib/puppet/type/f5_profilehttp.rb b/lib/puppet/type/f5_profilehttp.rb index 074e3d3..d4f3600 100644 --- a/lib/puppet/type/f5_profilehttp.rb +++ b/lib/puppet/type/f5_profilehttp.rb @@ -18,6 +18,13 @@ newproperty(:description, :parent => Puppet::Property::F5Description) + newproperty(:defaults_from) do + desc "Specifies the profile that you want to use as the parent profile. Your new profile inherits all settings and values from the parent profile specified." + validate do |value| + fail ArgumentError, "Values must take the form /Partition/name; #{value} does not" unless value =~ /^\/[\w\.-]+\/[\w|\.-]+$/ + end + end + newproperty(:fallback_host) do desc "fallbackHost" end @@ -26,4 +33,44 @@ desc "fallback_status_codes" end + newproperty(:hsts_include_subdomains) do + desc "Specifies whether to include the includeSubdomains directive in the HSTS header." + newvalue("enabled") + newvalue("disabled") + end + + newproperty(:hsts_mode) do + desc "Specifies whether to include the HSTS response header." + newvalue("enabled") + newvalue("disabled") + end + + newproperty(:hsts_maximum_age) do + desc "Specifies the maximum age to assume the connection should remain secure." + validate do |value| + fail ArgumentError, "Valid options: " unless value =~ /^\d+$/ || value.is_a?(Integer) + end + munge do |value| + Integer(value) + end + end + + newproperty(:hsts_preload) do + desc "Specifies whether to include the preload directive in the HSTS header." + newvalue("enabled") + newvalue("disabled") + end + + newproperty(:encrypt_cookies, :array_matching => :all) do + desc "Encrypts specified cookies that the BIG-IP system sends to a client system." + end + + newproperty(:encrypt_cookie_secret) do + desc "Specifies a passphrase for the cookie encryption." + + # hide plain text secret value in puppet output + def should_to_s(requested) + "******" + end + end end diff --git a/lib/puppet/util/network_device/transport/f5.rb b/lib/puppet/util/network_device/transport/f5.rb index 1e9d97e..3cc6f10 100644 --- a/lib/puppet/util/network_device/transport/f5.rb +++ b/lib/puppet/util/network_device/transport/f5.rb @@ -52,6 +52,20 @@ def put(url, json) end end + def patch(url, json) + if valid_json?(json) + result = connection.patch do |req| + req.url url + req.headers['Content-Type'] = 'application/json' + req.body = json + end + failure?(result) + return result + else + fail('Invalid JSON detected.') + end + end + def delete(url) result = connection.delete(url) failure?(result)