diff options
-rw-r--r-- | CHANGELOG.md | 9 | ||||
-rw-r--r-- | Gemfile.lock | 16 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | appveyor.yml | 31 | ||||
-rw-r--r-- | chef-config/lib/chef-config/version.rb | 2 | ||||
-rw-r--r-- | chef-universal-mingw32.gemspec | 1 | ||||
-rw-r--r-- | lib/chef/provider/windows_task.rb | 17 | ||||
-rw-r--r-- | lib/chef/resource/timezone.rb | 92 | ||||
-rw-r--r-- | lib/chef/resource/windows_certificate.rb | 269 | ||||
-rw-r--r-- | lib/chef/resource/windows_share.rb | 315 | ||||
-rw-r--r-- | lib/chef/resource_inspector.rb | 2 | ||||
-rw-r--r-- | lib/chef/resources.rb | 2 | ||||
-rw-r--r-- | lib/chef/version.rb | 2 | ||||
-rw-r--r-- | omnibus/Gemfile.lock | 69 | ||||
-rwxr-xr-x | omnibus/package-scripts/angrychef/postrm | 14 | ||||
-rwxr-xr-x | omnibus/package-scripts/chef-fips/postrm | 14 | ||||
-rwxr-xr-x | omnibus/package-scripts/chef/postrm | 14 | ||||
-rw-r--r-- | spec/support/platform_helpers.rb | 3 | ||||
-rw-r--r-- | spec/unit/resource/windows_certificate.rb | 46 | ||||
-rw-r--r-- | spec/unit/resource/windows_share.rb | 39 |
20 files changed, 859 insertions, 100 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index f70dfcdad5..77dfaa987c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,18 @@ <!-- usage documentation: http://expeditor-docs.es.chef.io/configuration/changelog/ --> -<!-- latest_release 14.7.0 --> -## [v14.7.0](https://github.com/chef/chef/tree/v14.7.0) (2018-10-31) +<!-- latest_release 14.7.3 --> +## [v14.7.3](https://github.com/chef/chef/tree/v14.7.3) (2018-10-31) #### Merged Pull Requests -- Automated resource documentation improvements [#7811](https://github.com/chef/chef/pull/7811) ([tas50](https://github.com/tas50)) +- Backport: Fix SLES 15 upgrades removing the symlinks [#7827](https://github.com/chef/chef/pull/7827) ([tas50](https://github.com/tas50)) <!-- latest_release --> <!-- release_rollup since=14.6.47 --> ### Changes since 14.6.47 release #### Merged Pull Requests +- Backport: Fix SLES 15 upgrades removing the symlinks [#7827](https://github.com/chef/chef/pull/7827) ([tas50](https://github.com/tas50)) <!-- 14.7.3 --> +- Backport: Fix inspector to properly handle defaults that are symbols [#7826](https://github.com/chef/chef/pull/7826) ([tas50](https://github.com/tas50)) <!-- 14.7.2 --> +- Backport: Add macOS support to the timezone resource [#7830](https://github.com/chef/chef/pull/7830) ([tas50](https://github.com/tas50)) <!-- 14.7.1 --> - Automated resource documentation improvements [#7811](https://github.com/chef/chef/pull/7811) ([tas50](https://github.com/tas50)) <!-- 14.7.0 --> - Allow passing array to supports in mount resource again [#7809](https://github.com/chef/chef/pull/7809) ([tas50](https://github.com/tas50)) <!-- 14.6.48 --> <!-- release_rollup --> diff --git a/Gemfile.lock b/Gemfile.lock index c1a5da61f5..a768efaa76 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,10 +9,10 @@ GIT PATH remote: . specs: - chef (14.7.0) + chef (14.7.3) addressable bundler (>= 1.10) - chef-config (= 14.7.0) + chef-config (= 14.7.3) chef-zero (>= 13.0) diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) @@ -39,10 +39,10 @@ PATH specinfra (~> 2.10) syslog-logger (~> 1.6) uuidtools (~> 2.1.5) - chef (14.7.0-universal-mingw32) + chef (14.7.3-universal-mingw32) addressable bundler (>= 1.10) - chef-config (= 14.7.0) + chef-config (= 14.7.3) chef-zero (>= 13.0) diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) @@ -71,6 +71,7 @@ PATH syslog-logger (~> 1.6) uuidtools (~> 2.1.5) win32-api (~> 1.5.3) + win32-certstore (>= 0.1.8) win32-dir (~> 0.5.0) win32-event (~> 0.6.1) win32-eventlog (= 0.6.3) @@ -85,7 +86,7 @@ PATH PATH remote: chef-config specs: - chef-config (14.7.0) + chef-config (14.7.3) addressable fuzzyurl mixlib-config (>= 2.2.12, < 3.0) @@ -219,7 +220,7 @@ GEM systemu (~> 2.6.4) wmi-lite (~> 1.0) parallel (1.12.1) - parser (2.5.1.2) + parser (2.5.3.0) ast (~> 2.4.0) parslet (1.8.2) plist (3.4.0) @@ -324,6 +325,9 @@ GEM hashdiff websocket (1.2.8) win32-api (1.5.3-universal-mingw32) + win32-certstore (0.1.11) + ffi + mixlib-shellout win32-dir (0.5.1) ffi (>= 1.0.0) win32-event (0.6.3) @@ -1 +1 @@ -14.7.0
\ No newline at end of file +14.7.3
\ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index 378574103f..e5ad88b8a3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,7 +5,12 @@ platform: - x64 cache: -- vendor/bundle + - vendor/bundle + +configuration: + - integration + - functional + - unit environment: matrix: @@ -41,6 +46,7 @@ install: - SET BUNDLE_WITHOUT=server:docgen:maintenance:pry:travis:integration:ci - bundle config --local path vendor/bundle # use the cache we define above - bundle install || bundle install || bundle install + - SET SPEC_OPTS=--format progress build: off @@ -50,6 +56,23 @@ before_test: - bundler --version - bundle env -test_script: - - SET SPEC_OPTS=--format progress - - bundle exec rake spec +for: + - + matrix: + only: + - configuration: integration + build_script: + - bundle exec rake spec:integration + - + matrix: + only: + - configuration: functional + build_script: + - bundle exec rake spec:functional + - + matrix: + only: + - configuration: unit + build_script: + - bundle exec rake spec:unit + - bundle exec rake component_specs diff --git a/chef-config/lib/chef-config/version.rb b/chef-config/lib/chef-config/version.rb index f6aa45e515..f01a275a7d 100644 --- a/chef-config/lib/chef-config/version.rb +++ b/chef-config/lib/chef-config/version.rb @@ -21,7 +21,7 @@ module ChefConfig CHEFCONFIG_ROOT = File.expand_path("../..", __FILE__) - VERSION = "14.7.0".freeze + VERSION = "14.7.3".freeze end # diff --git a/chef-universal-mingw32.gemspec b/chef-universal-mingw32.gemspec index f663153159..7e1a9d1551 100644 --- a/chef-universal-mingw32.gemspec +++ b/chef-universal-mingw32.gemspec @@ -16,6 +16,7 @@ gemspec.add_dependency "windows-api", "~> 0.4.4" gemspec.add_dependency "wmi-lite", "~> 1.0" gemspec.add_dependency "win32-taskscheduler", "~> 2.0" gemspec.add_dependency "iso8601", "~> 0.12.1" +gemspec.add_dependency "win32-certstore", ">= 0.1.8" gemspec.extensions << "ext/win32-eventlog/Rakefile" gemspec.files += Dir.glob("{distro,ext}/**/*") diff --git a/lib/chef/provider/windows_task.rb b/lib/chef/provider/windows_task.rb index cd8e07e24c..20709fa527 100644 --- a/lib/chef/provider/windows_task.rb +++ b/lib/chef/provider/windows_task.rb @@ -135,10 +135,10 @@ class Chef converge_by("#{new_resource} task created") do task = TaskScheduler.new if new_resource.frequency == :none - task.new_work_item(new_resource.task_name, {}, { user: new_resource.user, password: new_resource.password }) + task.new_work_item(new_resource.task_name, {}, { user: new_resource.user, password: new_resource.password, interactive: new_resource.interactive_enabled }) task.activate(new_resource.task_name) else - task.new_work_item(new_resource.task_name, trigger, { user: new_resource.user, password: new_resource.password }) + task.new_work_item(new_resource.task_name, trigger, { user: new_resource.user, password: new_resource.password, interactive: new_resource.interactive_enabled }) end task.application_name = new_resource.command task.parameters = new_resource.command_arguments if new_resource.command_arguments @@ -349,7 +349,7 @@ class Chef current_task_trigger[:type] != new_task_trigger[:type] || current_task_trigger[:random_minutes_interval].to_i != new_task_trigger[:random_minutes_interval].to_i || current_task_trigger[:minutes_interval].to_i != new_task_trigger[:minutes_interval].to_i || - task.account_information != new_resource.user || + task.account_information.to_s.casecmp(new_resource.user.to_s) != 0 || task.application_name != new_resource.command || task.parameters != new_resource.command_arguments.to_s || task.working_directory != new_resource.cwd.to_s || @@ -570,13 +570,18 @@ class Chef def logon_type # Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/aa383566(v=vs.85).aspx # if nothing is passed as logon_type the TASK_LOGON_SERVICE_ACCOUNT is getting set as default so using that for comparision. - user_id = new_resource.user + user_id = new_resource.user.to_s + password = new_resource.password.to_s if Chef::ReservedNames::Win32::Security::SID.service_account_user?(user_id) TaskScheduler::TASK_LOGON_SERVICE_ACCOUNT elsif Chef::ReservedNames::Win32::Security::SID.group_user?(user_id) TaskScheduler::TASK_LOGON_GROUP - elsif !new_resource.password.to_s.empty? # password is present - TaskScheduler::TASK_LOGON_PASSWORD + elsif !user_id.empty? && !password.empty? + if new_resource.interactive_enabled + TaskScheduler::TASK_LOGON_INTERACTIVE_TOKEN + else + TaskScheduler::TASK_LOGON_PASSWORD + end else TaskScheduler::TASK_LOGON_INTERACTIVE_TOKEN end diff --git a/lib/chef/resource/timezone.rb b/lib/chef/resource/timezone.rb index 8834e2404f..4a19001b99 100644 --- a/lib/chef/resource/timezone.rb +++ b/lib/chef/resource/timezone.rb @@ -25,7 +25,7 @@ class Chef preview_resource true resource_name :timezone - description "Use the timezone resource to change the system timezone." + description "Use the timezone resource to change the system timezone on Linux and macOS hosts. Timezones are specified in tz database format, with a complete list of available TZ values here https://en.wikipedia.org/wiki/List_of_tz_database_time_zones." introduced "14.6" property :timezone, String, @@ -35,12 +35,15 @@ class Chef action :set do description "Set the timezone." - package "tzdata" do - package_name platform_family?("suse") ? "timezone" : "tzdata" + # some linux systems may be missing the timezone data + if node["os"] == "linux" + package "tzdata" do + package_name platform_family?("suse") ? "timezone" : "tzdata" + end end + # Modern Amazon, Fedora, RHEL, Ubuntu & Debian if node["init_package"] == "systemd" - # Modern Amazon, Fedora, CentOS, RHEL, Ubuntu & Debian cmd_set_tz = "/usr/bin/timedatectl --no-ask-password set-timezone #{new_resource.timezone}" cmd_check_if_set = "/usr/bin/timedatectl status" @@ -51,38 +54,63 @@ class Chef action :run not_if cmd_check_if_set end - elsif platform_family?("rhel", "amazon") - # Old version of RHEL & CentOS - file "/etc/sysconfig/clock" do - owner "root" - group "root" - mode "0644" - action :create - content %{ZONE="#{new_resource.timezone}"\nUTC="true"\n} - end + else + case node["platform_family"] + # Old version of RHEL < 7 and Amazon 201X + when "rhel", "amazon" + file "/etc/sysconfig/clock" do + owner "root" + group "root" + mode "0644" + action :create + content %{ZONE="#{new_resource.timezone}"\nUTC="true"\n} + end - execute "tzdata-update" do - command "/usr/sbin/tzdata-update" - action :nothing - only_if { ::File.executable?("/usr/sbin/tzdata-update") } - subscribes :run, "file[/etc/sysconfig/clock]", :immediately - end + execute "tzdata-update" do + command "/usr/sbin/tzdata-update" + action :nothing + only_if { ::File.executable?("/usr/sbin/tzdata-update") } + subscribes :run, "file[/etc/sysconfig/clock]", :immediately + end - link "/etc/localtime" do - to "/usr/share/zoneinfo/#{new_resource.timezone}" - not_if { ::File.executable?("/usr/sbin/tzdata-update") } - end - elsif platform_family?("debian") - file "/etc/timezone" do - action :create - content "#{new_resource.timezone}\n" + link "/etc/localtime" do + to "/usr/share/zoneinfo/#{new_resource.timezone}" + not_if { ::File.executable?("/usr/sbin/tzdata-update") } + end + # debian < 8 and Ubuntu < 16.04 + when "debian" + file "/etc/timezone" do + action :create + content "#{new_resource.timezone}\n" + end + + bash "dpkg-reconfigure tzdata" do + user "root" + code "/usr/sbin/dpkg-reconfigure -f noninteractive tzdata" + action :nothing + subscribes :run, "file[/etc/timezone]", :immediately + end + when "mac_os_x" + unless current_darwin_tz == new_resource.timezone + converge_by("set timezone to #{new_resource.timezone}") do + shell_out!("sudo systemsetup -settimezone #{new_resource.timezone}") + end + end end + end + end - bash "dpkg-reconfigure tzdata" do - user "root" - code "/usr/sbin/dpkg-reconfigure -f noninteractive tzdata" - action :nothing - subscribes :run, "file[/etc/timezone]", :immediately + action_class do + # detect the current TZ on darwin hosts + # + # @since 14.7 + # @return [String] TZ database value + def current_darwin_tz + tz_shellout = shell_out!("systemsetup -gettimezone") + if /You need administrator access/.match?(tz_shellout.stdout) + raise "The timezone resource requires adminstrative priveleges to run on macOS hosts!" + else + /Time Zone: (.*)/.match(tz_shellout.stdout)[1] end end end diff --git a/lib/chef/resource/windows_certificate.rb b/lib/chef/resource/windows_certificate.rb new file mode 100644 index 0000000000..a5058f07a8 --- /dev/null +++ b/lib/chef/resource/windows_certificate.rb @@ -0,0 +1,269 @@ +# +# Author:: Richard Lavey (richard.lavey@calastone.com) +# +# Copyright:: 2015-2017, Calastone Ltd. +# Copyright:: 2018, Chef Software, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "chef/resource" +require "win32-certstore" if Chef::Platform.windows? +require "openssl" + +class Chef + class Resource + class WindowsCertificate < Chef::Resource + preview_resource true + resource_name :windows_certificate + + description "Use the windows_certificate resource to install a certificate into the Windows certificate store from a file. The resource grants read-only access to the private key for designated accounts. Due to current limitations in WinRM, installing certificates remotely may not work if the operation requires a user profile. Operations on the local machine store should still work." + introduced "14.7" + + property :source, String, + description: "The source file (for create and acl_add), thumbprint (for delete and acl_add) or subject (for delete).", + name_property: true + + property :pfx_password, String, + description: "The password to access the source if it is a pfx file." + + property :private_key_acl, Array, + description: "An array of 'domain\account' entries to be granted read-only access to the certificate's private key. Not idempotent." + + property :store_name, String, + description: "The certificate store to manipulate.", + default: "MY", equal_to: ["TRUSTEDPUBLISHER", "TrustedPublisher", "CLIENTAUTHISSUER", "REMOTE DESKTOP", "ROOT", "TRUSTEDDEVICES", "WEBHOSTING", "CA", "AUTHROOT", "TRUSTEDPEOPLE", "MY", "SMARTCARDROOT", "TRUST", "DISALLOWED"] + + property :user_store, [TrueClass, FalseClass], + description: "Use the user store of the local machine store if set to false.", + default: false + + property :cert_path, String, + description: "" + + # lazy used to set default value of sensitive to true if password is set + property :sensitive, [ TrueClass, FalseClass ], + description: "Ensure that sensitive resource data is not logged by the chef-client.", + default: lazy { |r| r.pfx_password ? true : false }, skip_docs: true + + action :create do + description "Creates or updates a certificate." + + add_cert(OpenSSL::X509::Certificate.new(raw_source)) + end + + # acl_add is a modify-if-exists operation : not idempotent + action :acl_add do + description "Adds read-only entries to a certificate's private key ACL." + + if ::File.exist?(new_resource.source) + hash = "$cert.GetCertHashString()" + code_script = cert_script(false) + guard_script = cert_script(false) + else + # make sure we have no spaces in the hash string + hash = "\"#{new_resource.source.gsub(/\s/, '')}\"" + code_script = "" + guard_script = "" + end + code_script << acl_script(hash) + guard_script << cert_exists_script(hash) + + powershell_script "setting the acls on #{new_resource.source} in #{cert_location}\\#{new_resource.store_name}" do + guard_interpreter :powershell_script + convert_boolean_return true + code code_script + only_if guard_script + sensitive if new_resource.sensitive + end + end + + action :delete do + description "Deletes a certificate." + + delete_cert + end + + action :fetch do + description "Fetches a certificate." + + cert_obj = fetch_cert + if cert_obj + show_or_store_cert(cert_obj) + else + Chef::Log.info("Certificate not found") + end + end + + action :verify do + description "" + + out = verify_cert + if !!out == out + out = out ? "Certificate is valid" : "Certificate not valid" + end + Chef::Log.info(out.to_s) + end + + action_class do + def add_cert(cert_obj) + store = ::Win32::Certstore.open(new_resource.store_name) + store.add(cert_obj) + end + + def delete_cert + store = ::Win32::Certstore.open(new_resource.store_name) + store.delete(new_resource.source) + end + + def fetch_cert + store = ::Win32::Certstore.open(new_resource.store_name) + store.get(new_resource.source) + end + + def verify_cert + store = ::Win32::Certstore.open(new_resource.store_name) + store.valid?(new_resource.source) + end + + def show_or_store_cert(cert_obj) + if new_resource.cert_path + export_cert(cert_obj, new_resource.cert_path) + if ::File.size(new_resource.cert_path) > 0 + Chef::Log.info("Certificate export in #{new_resource.cert_path}") + else + ::File.delete(new_resource.cert_path) + end + else + Chef::Log.info(cert_obj.display) + end + end + + def export_cert(cert_obj, cert_path) + out_file = ::File.new(cert_path, "w+") + case ::File.extname(cert_path) + when ".pem" + out_file.puts(cert_obj.to_pem) + when ".der" + out_file.puts(cert_obj.to_der) + when ".cer" + cert_out = powershell_out("openssl x509 -text -inform DER -in #{cert_obj.to_pem} -outform CER").stdout + out_file.puts(cert_out) + when ".crt" + cert_out = powershell_out("openssl x509 -text -inform DER -in #{cert_obj.to_pem} -outform CRT").stdout + out_file.puts(cert_out) + when ".pfx" + cert_out = powershell_out("openssl pkcs12 -export -nokeys -in #{cert_obj.to_pem} -outform PFX").stdout + out_file.puts(cert_out) + when ".p7b" + cert_out = powershell_out("openssl pkcs7 -export -nokeys -in #{cert_obj.to_pem} -outform P7B").stdout + out_file.puts(cert_out) + else + Chef::Log.info("Supported certificate format .pem, .der, .cer, .crt, .pfx and .p7b") + end + out_file.close + end + + def cert_location + @location ||= new_resource.user_store ? "CurrentUser" : "LocalMachine" + end + + def cert_script(persist) + cert_script = "$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2" + file = win_friendly_path(new_resource.source) + cert_script << " \"#{file}\"" + if ::File.extname(file.downcase) == ".pfx" + cert_script << ", \"#{new_resource.pfx_password}\"" + if persist && new_resource.user_store + cert_script << ", ([System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet)" + elsif persist + cert_script << ", ([System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeyset)" + end + end + cert_script << "\n" + end + + def cert_exists_script(hash) + <<-EOH + $hash = #{hash} + Test-Path "Cert:\\#{cert_location}\\#{new_resource.store_name}\\$hash" + EOH + end + + def within_store_script + inner_script = yield "$store" + <<-EOH + $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "#{new_resource.store_name}", ([System.Security.Cryptography.X509Certificates.StoreLocation]::#{cert_location}) + $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite) + #{inner_script} + $store.Close() + EOH + end + + def acl_script(hash) + return "" if new_resource.private_key_acl.nil? || new_resource.private_key_acl.empty? + # this PS came from http://blogs.technet.com/b/operationsguy/archive/2010/11/29/provide-access-to-private-keys-commandline-vs-powershell.aspx + # and from https://msdn.microsoft.com/en-us/library/windows/desktop/bb204778(v=vs.85).aspx + set_acl_script = <<-EOH + $hash = #{hash} + $storeCert = Get-ChildItem "cert:\\#{cert_location}\\#{new_resource.store_name}\\$hash" + if ($storeCert -eq $null) { throw 'no key exists.' } + $keyname = $storeCert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName + if ($keyname -eq $null) { throw 'no private key exists.' } + if ($storeCert.PrivateKey.CspKeyContainerInfo.MachineKeyStore) + { + $fullpath = "$Env:ProgramData\\Microsoft\\Crypto\\RSA\\MachineKeys\\$keyname" + } + else + { + $currentUser = New-Object System.Security.Principal.NTAccount($Env:UserDomain, $Env:UserName) + $userSID = $currentUser.Translate([System.Security.Principal.SecurityIdentifier]).Value + $fullpath = "$Env:ProgramData\\Microsoft\\Crypto\\RSA\\$userSID\\$keyname" + } + EOH + new_resource.private_key_acl.each do |name| + set_acl_script << "$uname='#{name}'; icacls $fullpath /grant $uname`:RX\n" + end + set_acl_script + end + + def raw_source + ext = ::File.extname(new_resource.source) + convert_pem(ext, new_resource.source) + end + + def convert_pem(ext, source) + out = case ext + when ".crt", ".der" + powershell_out("openssl x509 -text -inform DER -in #{source} -outform PEM").stdout + when ".cer" + powershell_out("openssl x509 -text -inform DER -in #{source} -outform PEM").stdout + when ".pfx" + powershell_out("openssl pkcs12 -in #{source} -nodes -passin pass:#{new_resource.pfx_password}").stdout + when ".p7b" + powershell_out("openssl pkcs7 -print_certs -in #{source} -outform PEM").stdout + end + out = ::File.read(source) if out.nil? || out.empty? + format_raw_out(out) + end + + def format_raw_out(out) + begin_cert = "-----BEGIN CERTIFICATE-----" + end_cert = "-----END CERTIFICATE-----" + begin_cert + out[/#{begin_cert}(.*?)#{end_cert}/m, 1] + end_cert + end + end + + end + end +end diff --git a/lib/chef/resource/windows_share.rb b/lib/chef/resource/windows_share.rb new file mode 100644 index 0000000000..b84be3dc36 --- /dev/null +++ b/lib/chef/resource/windows_share.rb @@ -0,0 +1,315 @@ +# +# Author:: Sölvi Páll Ásgeirsson (<solvip@gmail.com>) +# Author:: Richard Lavey (richard.lavey@calastone.com) +# Author:: Tim Smith (tsmith@chef.io) +# +# Copyright:: 2014-2017, Sölvi Páll Ásgeirsson. +# Copyright:: 2018, Chef Software, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "chef/resource" +require "chef/json_compat" + +class Chef + class Resource + class WindowsShare < Chef::Resource + preview_resource true + resource_name :windows_share + + description "Use the windows_share resource to create, modify and remove Windows shares." + introduced "14.7" + + # Specifies a name for the SMB share. The name may be composed of any valid file name characters, but must be less than 80 characters long. The names pipe and mailslot are reserved for use by the computer. + property :share_name, String, + description: "The name to assign to the share.", + name_property: true + + # Specifies the path of the location of the folder to share. The path must be fully qualified. Relative paths or paths that contain wildcard characters are not permitted. + property :path, String, + description: "The path of the folder to share. Required when creating. If the share already exists on a different path then it is deleted and re-created." + + # Specifies an optional description of the SMB share. A description of the share is displayed by running the Get-SmbShare cmdlet. The description may not contain more than 256 characters. + property :description, String, + description: "The description to be applied to the share.", + default: "" + + # Specifies which accounts are granted full permission to access the share. Use a comma-separated list to specify multiple accounts. An account may not be specified more than once in the FullAccess, ChangeAccess, or ReadAccess parameter lists, but may be specified once in the FullAccess, ChangeAccess, or ReadAccess parameter list and once in the NoAccess parameter list. + property :full_users, Array, + description: "The users that should have 'Full control' permissions on the share in domain\\username format.", + default: [], coerce: proc { |u| u.sort } + + # Specifies which users are granted modify permission to access the share + property :change_users, Array, + description: "The users that should have 'modify' permission on the share in domain\\username format.", + default: [], coerce: proc { |u| u.sort } + + # Specifies which users are granted read permission to access the share. Multiple users can be specified by supplying a comma-separated list. + property :read_users, Array, + description: "The users that should have 'read' permission on the share in domain\\username format.", + default: [], coerce: proc { |u| u.sort } + + # Specifies the lifetime of the new SMB share. A temporary share does not persist beyond the next restart of the computer. By default, new SMB shares are persistent, and non-temporary. + property :temporary, [TrueClass, FalseClass], + description: "The lifetime of the new SMB share. A temporary share does not persist beyond the next restart of the computer.", + default: false + + # Specifies the scope name of the share. + property :scope_name, String, + description: "The scope name of the share.", + default: "*" + + # Specifies the continuous availability time-out for the share. + property :ca_timeout, Integer, + description: "The continuous availability time-out for the share.", + default: 0 + + # Indicates that the share is continuously available. + property :continuously_available, [TrueClass, FalseClass], + description: "Indicates that the share is continuously available.", + default: false + + # Specifies the caching mode of the offline files for the SMB share. + # property :caching_mode, String, equal_to: %w(None Manual Documents Programs BranchCache) + + # Specifies the maximum number of concurrently connected users that the new SMB share may accommodate. If this parameter is set to zero (0), then the number of users is unlimited. + property :concurrent_user_limit, Integer, + description: "The maximum number of concurrently connected users the share can accommodate.", + default: 0 + + # Indicates that the share is encrypted. + property :encrypt_data, [TrueClass, FalseClass], + description: "Indicates that the share is encrypted.", + default: false + + # Specifies which files and folders in the SMB share are visible to users. AccessBased: SMB does not the display the files and folders for a share to a user unless that user has rights to access the files and folders. By default, access-based enumeration is disabled for new SMB shares. Unrestricted: SMB displays files and folders to a user even when the user does not have permission to access the items. + # property :folder_enumeration_mode, String, equal_to: %(AccessBased Unrestricted) + + include Chef::Mixin::PowershellOut + + load_current_value do |desired| + # this command selects individual objects because EncryptData & CachingMode have underlying + # types that get converted to their Integer values by ConvertTo-Json & we need to make sure + # those get written out as strings + share_state_cmd = "Get-SmbShare -Name '#{desired.share_name}' | Select-Object Name,Path, Description, Temporary, CATimeout, ContinuouslyAvailable, ConcurrentUserLimit, EncryptData | ConvertTo-Json" + + Chef::Log.debug("Running '#{share_state_cmd}' to determine share state'") + ps_results = powershell_out(share_state_cmd) + + # detect a failure without raising and then set current_resource to nil + if ps_results.error? + Chef::Log.debug("Error fetching share state: #{ps_results.stderr}") + current_value_does_not_exist! + end + + Chef::Log.debug("The Get-SmbShare results were #{ps_results.stdout}") + results = Chef::JSONCompat.from_json(ps_results.stdout) + + path results["Path"] + description results["Description"] + temporary results["Temporary"] + ca_timeout results["CATimeout"] + continuously_available results["ContinuouslyAvailable"] + # caching_mode results['CachingMode'] + concurrent_user_limit results["ConcurrentUserLimit"] + encrypt_data results["EncryptData"] + # folder_enumeration_mode results['FolderEnumerationMode'] + + perm_state_cmd = %{Get-SmbShareAccess -Name "#{desired.share_name}" | Select-Object AccountName,AccessControlType,AccessRight | ConvertTo-Json} + + Chef::Log.debug("Running '#{perm_state_cmd}' to determine share permissions state'") + ps_perm_results = powershell_out(perm_state_cmd) + + # we raise here instead of warning like above because we'd only get here if the above Get-SmbShare + # command was successful and that continuing would leave us with 1/2 known state + raise "Could not determine #{desired.share_name} share permissions by running '#{perm_state_cmd}'" if ps_perm_results.error? + + Chef::Log.debug("The Get-SmbShareAccess results were #{ps_perm_results.stdout}") + + f_users, c_users, r_users = parse_permissions(ps_perm_results.stdout) + + full_users f_users + change_users c_users + read_users r_users + end + + def after_created + raise "The windows_share resource relies on PowerShell cmdlets not present in Windows releases prior to 8/2012. Cannot continue!" if node["platform_version"].to_f < 6.3 + end + +# given the string output of Get-SmbShareAccess parse out +# arrays of full access users, change users, and read only users + def parse_permissions(results_string) + json_results = Chef::JSONCompat.from_json(results_string) + json_results = [json_results] unless json_results.is_a?(Array) # single result is not an array + + f_users = [] + c_users = [] + r_users = [] + + json_results.each do |perm| + next unless perm["AccessControlType"] == 0 # allow + case perm["AccessRight"] + when 0 then f_users << stripped_account(perm["AccountName"]) # 0 full control + when 1 then c_users << stripped_account(perm["AccountName"]) # 1 == change + when 2 then r_users << stripped_account(perm["AccountName"]) # 2 == read + end + end + [f_users, c_users, r_users] + end + +# local names are returned from Get-SmbShareAccess in the full format MACHINE\\NAME +# but users of this resource would simply say NAME so we need to strip the values for comparison + def stripped_account(name) + name.slice!("#{node['hostname']}\\") + name + end + + action :create do + description "Create and modify Windows shares." + + # we do this here instead of requiring the property because :delete doesn't need path set + raise "No path property set" unless new_resource.path + + converge_if_changed do + # you can't actually change the path so you have to delete the old share first + delete_share if different_path? + + # powershell cmdlet for create is different than updates + if current_resource.nil? + Chef::Log.debug("The current resource is nil so we will create a new share") + create_share + else + Chef::Log.debug("The current resource was not nil so we will update an existing share") + update_share + end + + # creating the share does not set permissions so we need to update + update_permissions + end + end + + action :delete do + description "Delete an existing Windows share." + + if current_resource.nil? + Chef::Log.debug("#{new_resource.share_name} does not exist - nothing to do") + else + converge_by("delete #{new_resource.share_name}") do + delete_share + end + end + end + + action_class do + def different_path? + return false if current_resource.nil? # going from nil to something isn't different for our concerns + return false if current_resource.path == new_resource.path + true + end + + def delete_share + delete_command = "Remove-SmbShare -Name '#{new_resource.share_name}' -Force" + + Chef::Log.debug("Running '#{delete_command}' to remove the share") + powershell_out!(delete_command) + end + + def update_share + update_command = "Set-SmbShare -Name '#{new_resource.share_name}' -Description '#{new_resource.description}' -Force" + + Chef::Log.debug("Running '#{update_command}' to update the share") + powershell_out!(update_command) + end + + def create_share + raise "#{new_resource.path} is missing or not a directory. Shares cannot be created if the path doesn't first exist." unless ::File.directory? new_resource.path + + share_cmd = "New-SmbShare -Name '#{new_resource.share_name}' -Path '#{new_resource.path}' -Description '#{new_resource.description}' -ConcurrentUserLimit #{new_resource.concurrent_user_limit} -CATimeout #{new_resource.ca_timeout} -EncryptData:#{bool_string(new_resource.encrypt_data)} -ContinuouslyAvailable:#{bool_string(new_resource.continuously_available)}" + share_cmd << " -ScopeName #{new_resource.scope_name}" unless new_resource.scope_name == "*" # passing * causes the command to fail + share_cmd << " -Temporary:#{bool_string(new_resource.temporary)}" if new_resource.temporary # only set true + + Chef::Log.debug("Running '#{share_cmd}' to create the share") + powershell_out!(share_cmd) + end + + # determine what users in the current state don't exist in the desired state + # users/groups will have their permissions updated with the same command that + # sets it, but removes must be performed with Revoke-SmbShareAccess + def users_to_revoke + @users_to_revoke ||= begin + # if the resource doesn't exist then nothing needs to be revoked + if current_resource.nil? + [] + else # if it exists then calculate the current to new resource diffs + (current_resource.full_users + current_resource.change_users + current_resource.read_users) - (new_resource.full_users + new_resource.change_users + new_resource.read_users) + end + end + end + + # update existing permissions on a share + def update_permissions + # revoke any users that had something, but now has nothing + revoke_user_permissions(users_to_revoke) unless users_to_revoke.empty? + + # set permissions for each of the permission types + %w{full read change}.each do |perm_type| + # set permissions for a brand new share OR + # update permissions if the current state and desired state differ + next unless permissions_need_update?(perm_type) + grant_command = "Grant-SmbShareAccess -Name '#{new_resource.share_name}' -AccountName \"#{new_resource.send("#{perm_type}_users").join('","')}\" -Force -AccessRight #{perm_type}" + + Chef::Log.debug("Running '#{grant_command}' to update the share permissions") + powershell_out!(grant_command) + end + end + + # determine if permissions need to be updated. + # Brand new share with no permissions defined: no + # Brand new share with permissions defined: yes + # Existing share with differing permissions: yes + # + # @param [String] type the permissions type (Full, Read, or Change) + def permissions_need_update?(type) + property_name = "#{type}_users" + + # brand new share, but nothing to set + return false if current_resource.nil? && new_resource.send(property_name).empty? + + # brand new share with new permissions to set + return true if current_resource.nil? && !new_resource.send(property_name).empty? + + # there's a difference between the current and desired state + return true unless (new_resource.send(property_name) - current_resource.send(property_name)).empty? + + # anything else + false + end + + def revoke_user_permissions(users) + revoke_command = "Revoke-SmbShareAccess -Name '#{new_resource.share_name}' -AccountName \"#{users.join(',')}\" -Force" + Chef::Log.debug("Running '#{revoke_command}' to revoke share permissions") + powershell_out!(revoke_command) + end + + # convert True/False into "$True" & "$False" + def bool_string(bool) + # bool ? 1 : 0 + bool ? "$true" : "$false" + end + end + + end + end +end diff --git a/lib/chef/resource_inspector.rb b/lib/chef/resource_inspector.rb index 1a49f05b72..1888f9a8bc 100644 --- a/lib/chef/resource_inspector.rb +++ b/lib/chef/resource_inspector.rb @@ -31,7 +31,7 @@ module ResourceInspector # code for the resource ourselves and just no "lazy default" else - default + default.inspect # inspect properly returns symbols end end diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb index 6324ce5b66..805d278cc6 100644 --- a/lib/chef/resources.rb +++ b/lib/chef/resources.rb @@ -130,6 +130,7 @@ require "chef/resource/powershell_package" require "chef/resource/msu_package" require "chef/resource/windows_ad_join" require "chef/resource/windows_auto_run" +require "chef/resource/windows_certificate" require "chef/resource/windows_feature" require "chef/resource/windows_feature_dism" require "chef/resource/windows_feature_powershell" @@ -138,6 +139,7 @@ require "chef/resource/windows_pagefile" require "chef/resource/windows_path" require "chef/resource/windows_printer" require "chef/resource/windows_printer_port" +require "chef/resource/windows_share" require "chef/resource/windows_shortcut" require "chef/resource/windows_task" require "chef/resource/windows_workgroup" diff --git a/lib/chef/version.rb b/lib/chef/version.rb index 88c13d24ca..d2b0bac8a7 100644 --- a/lib/chef/version.rb +++ b/lib/chef/version.rb @@ -23,7 +23,7 @@ require "chef/version_string" class Chef CHEF_ROOT = File.expand_path("../..", __FILE__) - VERSION = Chef::VersionString.new("14.7.0") + VERSION = Chef::VersionString.new("14.7.3") end # diff --git a/omnibus/Gemfile.lock b/omnibus/Gemfile.lock index 36e0961c4e..a0c4ceeba7 100644 --- a/omnibus/Gemfile.lock +++ b/omnibus/Gemfile.lock @@ -18,7 +18,7 @@ GIT GIT remote: https://github.com/chef/omnibus-software - revision: 1d0fbaba922b31444c91f8e5a440ca3ac50aae67 + revision: 3b6ef4c42d8c2f9f9e1c6e285b2ea48e0d0f0fdd branch: master specs: omnibus-software (4.0.0) @@ -32,8 +32,8 @@ GEM public_suffix (>= 2.0.2, < 4.0) awesome_print (1.8.0) aws-eventstream (1.0.1) - aws-partitions (1.106.0) - aws-sdk-core (3.35.0) + aws-partitions (1.107.0) + aws-sdk-core (3.36.0) aws-eventstream (~> 1.0) aws-partitions (~> 1.0) aws-sigv4 (~> 1.0) @@ -41,7 +41,7 @@ GEM aws-sdk-kms (1.11.0) aws-sdk-core (~> 3, >= 3.26.0) aws-sigv4 (~> 1.0) - aws-sdk-s3 (1.23.0) + aws-sdk-s3 (1.23.1) aws-sdk-core (~> 3, >= 3.26.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.0) @@ -63,10 +63,10 @@ GEM debug_inspector (>= 0.0.1) builder (3.2.3) byebug (10.0.2) - chef (14.5.33) + chef (14.6.47) addressable bundler (>= 1.10) - chef-config (= 14.5.33) + chef-config (= 14.6.47) chef-zero (>= 13.0) diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) @@ -74,7 +74,6 @@ GEM ffi-yajl (~> 2.2) highline (~> 1.6, >= 1.6.9) iniparse (~> 1.4) - iso8601 (~> 0.11.0) mixlib-archive (~> 0.4) mixlib-authentication (~> 2.1) mixlib-cli (~> 1.7) @@ -86,18 +85,18 @@ GEM ohai (~> 14.0) plist (~> 3.2) proxifier (~> 1.0) - rspec-core (~> 3.5, < 3.8) - rspec-expectations (~> 3.5, < 3.8) - rspec-mocks (~> 3.5, < 3.8) + rspec-core (~> 3.5) + rspec-expectations (~> 3.5) + rspec-mocks (~> 3.5) rspec_junit_formatter (~> 0.2.0) serverspec (~> 2.7) specinfra (~> 2.10) syslog-logger (~> 1.6) uuidtools (~> 2.1.5) - chef (14.5.33-universal-mingw32) + chef (14.6.47-universal-mingw32) addressable bundler (>= 1.10) - chef-config (= 14.5.33) + chef-config (= 14.6.47) chef-zero (>= 13.0) diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) @@ -105,7 +104,7 @@ GEM ffi-yajl (~> 2.2) highline (~> 1.6, >= 1.6.9) iniparse (~> 1.4) - iso8601 (~> 0.11.0) + iso8601 (~> 0.12.1) mixlib-archive (~> 0.4) mixlib-authentication (~> 2.1) mixlib-cli (~> 1.7) @@ -117,9 +116,9 @@ GEM ohai (~> 14.0) plist (~> 3.2) proxifier (~> 1.0) - rspec-core (~> 3.5, < 3.8) - rspec-expectations (~> 3.5, < 3.8) - rspec-mocks (~> 3.5, < 3.8) + rspec-core (~> 3.5) + rspec-expectations (~> 3.5) + rspec-mocks (~> 3.5) rspec_junit_formatter (~> 0.2.0) serverspec (~> 2.7) specinfra (~> 2.10) @@ -133,10 +132,10 @@ GEM win32-mutex (~> 0.4.2) win32-process (~> 0.8.2) win32-service (~> 1.0) - win32-taskscheduler (~> 1.0.0) + win32-taskscheduler (~> 2.0) windows-api (~> 0.4.4) wmi-lite (~> 1.0) - chef-config (14.5.33) + chef-config (14.6.47) addressable fuzzyurl mixlib-config (>= 2.2.12, < 3.0) @@ -152,7 +151,7 @@ GEM citrus (3.0.2) cleanroom (1.0.0) coderay (1.1.2) - concurrent-ruby (1.0.5) + concurrent-ruby (1.1.0) debug_inspector (0.0.3) diff-lcs (1.3) erubis (2.7.0) @@ -176,9 +175,9 @@ GEM iniparse (1.4.4) iostruct (0.0.4) ipaddress (0.8.3) - iso8601 (0.11.0) + iso8601 (0.12.1) jmespath (1.4.0) - kitchen-vagrant (1.3.5) + kitchen-vagrant (1.3.6) test-kitchen (~> 1.4) libyajl2 (1.2.0) license_scout (1.0.16) @@ -190,7 +189,7 @@ GEM little-plugger (~> 1.1) multi_json (~> 1.10) method_source (0.9.0) - minitar (0.6.1) + minitar (0.7) mixlib-archive (0.4.18) mixlib-log mixlib-archive (0.4.18-universal-mingw32) @@ -259,22 +258,22 @@ GEM public_suffix (3.0.3) rack (2.0.5) retryable (2.0.4) - rspec (3.7.0) - rspec-core (~> 3.7.0) - rspec-expectations (~> 3.7.0) - rspec-mocks (~> 3.7.0) - rspec-core (3.7.1) - rspec-support (~> 3.7.0) - rspec-expectations (3.7.0) + rspec (3.8.0) + rspec-core (~> 3.8.0) + rspec-expectations (~> 3.8.0) + rspec-mocks (~> 3.8.0) + rspec-core (3.8.0) + rspec-support (~> 3.8.0) + rspec-expectations (3.8.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.7.0) + rspec-support (~> 3.8.0) rspec-its (1.2.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.7.0) + rspec-mocks (3.8.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.7.0) - rspec-support (3.7.1) + rspec-support (~> 3.8.0) + rspec-support (3.8.0) rspec_junit_formatter (0.2.3) builder (< 4) rspec-core (>= 2, < 4, != 2.12.0) @@ -335,7 +334,7 @@ GEM win32-service (1.0.1) ffi ffi-win32-extensions - win32-taskscheduler (1.0.12) + win32-taskscheduler (2.0.0) ffi structured_warnings windows-api (0.4.4) @@ -379,4 +378,4 @@ DEPENDENCIES winrm-fs (~> 1.0) BUNDLED WITH - 1.16.6 + 1.17.1 diff --git a/omnibus/package-scripts/angrychef/postrm b/omnibus/package-scripts/angrychef/postrm index 247688074e..a153da7102 100755 --- a/omnibus/package-scripts/angrychef/postrm +++ b/omnibus/package-scripts/angrychef/postrm @@ -11,11 +11,19 @@ is_smartos() { uname -v | grep "^joyent" 2>&1 >/dev/null } -is_darwin() -{ +is_darwin() { uname -v | grep "^Darwin" 2>&1 >/dev/null } +is_suse() { + if [ -f /etc/os-release ]; then + . /etc/os-release + [ "$ID_LIKE" = "sles" ] || [ "$ID_LIKE" = "suse" ] + else + [ -f /etc/SuSE-release ] + fi +} + if is_smartos; then PREFIX="/opt/local" elif is_darwin; then @@ -33,7 +41,7 @@ cleanup_symlinks() { # Clean up binary symlinks if they exist # see: http://tickets.opscode.com/browse/CHEF-3022 -if [ ! -f /etc/redhat-release -a ! -f /etc/fedora-release -a ! -f /etc/system-release -a ! -f /etc/SuSE-release ]; then +if [ ! -f /etc/redhat-release -a ! -f /etc/fedora-release -a ! -f /etc/system-release -a ! is_suse ]; then # not a redhat-ish RPM-based system cleanup_symlinks elif [ "x$1" = "x0" ]; then diff --git a/omnibus/package-scripts/chef-fips/postrm b/omnibus/package-scripts/chef-fips/postrm index 247688074e..a153da7102 100755 --- a/omnibus/package-scripts/chef-fips/postrm +++ b/omnibus/package-scripts/chef-fips/postrm @@ -11,11 +11,19 @@ is_smartos() { uname -v | grep "^joyent" 2>&1 >/dev/null } -is_darwin() -{ +is_darwin() { uname -v | grep "^Darwin" 2>&1 >/dev/null } +is_suse() { + if [ -f /etc/os-release ]; then + . /etc/os-release + [ "$ID_LIKE" = "sles" ] || [ "$ID_LIKE" = "suse" ] + else + [ -f /etc/SuSE-release ] + fi +} + if is_smartos; then PREFIX="/opt/local" elif is_darwin; then @@ -33,7 +41,7 @@ cleanup_symlinks() { # Clean up binary symlinks if they exist # see: http://tickets.opscode.com/browse/CHEF-3022 -if [ ! -f /etc/redhat-release -a ! -f /etc/fedora-release -a ! -f /etc/system-release -a ! -f /etc/SuSE-release ]; then +if [ ! -f /etc/redhat-release -a ! -f /etc/fedora-release -a ! -f /etc/system-release -a ! is_suse ]; then # not a redhat-ish RPM-based system cleanup_symlinks elif [ "x$1" = "x0" ]; then diff --git a/omnibus/package-scripts/chef/postrm b/omnibus/package-scripts/chef/postrm index 247688074e..a153da7102 100755 --- a/omnibus/package-scripts/chef/postrm +++ b/omnibus/package-scripts/chef/postrm @@ -11,11 +11,19 @@ is_smartos() { uname -v | grep "^joyent" 2>&1 >/dev/null } -is_darwin() -{ +is_darwin() { uname -v | grep "^Darwin" 2>&1 >/dev/null } +is_suse() { + if [ -f /etc/os-release ]; then + . /etc/os-release + [ "$ID_LIKE" = "sles" ] || [ "$ID_LIKE" = "suse" ] + else + [ -f /etc/SuSE-release ] + fi +} + if is_smartos; then PREFIX="/opt/local" elif is_darwin; then @@ -33,7 +41,7 @@ cleanup_symlinks() { # Clean up binary symlinks if they exist # see: http://tickets.opscode.com/browse/CHEF-3022 -if [ ! -f /etc/redhat-release -a ! -f /etc/fedora-release -a ! -f /etc/system-release -a ! -f /etc/SuSE-release ]; then +if [ ! -f /etc/redhat-release -a ! -f /etc/fedora-release -a ! -f /etc/system-release -a ! is_suse ]; then # not a redhat-ish RPM-based system cleanup_symlinks elif [ "x$1" = "x0" ]; then diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb index 6ae052ba1d..5ae9c01722 100644 --- a/spec/support/platform_helpers.rb +++ b/spec/support/platform_helpers.rb @@ -220,7 +220,8 @@ def selinux_enabled? end def suse? - File.exists?("/etc/SuSE-release") + ::File.exists?("/etc/SuSE-release") || + ( ::File.exists?("/etc/os-release") && /sles|suse/.match?(File.read("/etc/os-release")) ) end def root? diff --git a/spec/unit/resource/windows_certificate.rb b/spec/unit/resource/windows_certificate.rb new file mode 100644 index 0000000000..4a60be6e87 --- /dev/null +++ b/spec/unit/resource/windows_certificate.rb @@ -0,0 +1,46 @@ +# +# Copyright:: Copyright 2018, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" + +describe Chef::Resource::WindowsCertificate do + let(:resource) { Chef::Resource::WindowsCertificate.new("foobar") } + + it "sets resource name as :windows_certificate" do + expect(resource.resource_name).to eql(:windows_certificate) + end + + it "the source property is the name_property" do + expect(resource.source).to eql("foobar") + end + + it "sets the default action as :create" do + expect(resource.action).to eql([:create]) + end + + it "supports :create, :acl_add, :delete, and :verify actions" do + expect { resource.action :create }.not_to raise_error + expect { resource.action :acl_add }.not_to raise_error + expect { resource.action :delete }.not_to raise_error + expect { resource.action :verify }.not_to raise_error + end + + it "sets sensitive to true if the pfx_password property is set" do + resource.pfx_password "foo" + expect(resource.sensitive).to be_truthy + end +end diff --git a/spec/unit/resource/windows_share.rb b/spec/unit/resource/windows_share.rb new file mode 100644 index 0000000000..ee1c24529f --- /dev/null +++ b/spec/unit/resource/windows_share.rb @@ -0,0 +1,39 @@ +# +# Copyright:: Copyright 2018, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" + +describe Chef::Resource::WindowsShare do + let(:resource) { Chef::Resource::WindowsShare.new("foobar") } + + it "sets resource name as :windows_share" do + expect(resource.resource_name).to eql(:windows_share) + end + + it "the share_name property is the name_property" do + expect(resource.share_name).to eql("foobar") + end + + it "sets the default action as :create" do + expect(resource.action).to eql([:create]) + end + + it "supports :create and :delete actions" do + expect { resource.action :create }.not_to raise_error + expect { resource.action :delete }.not_to raise_error + end +end |