diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/chef/application.rb | 2 | ||||
-rw-r--r-- | lib/chef/file_content_management/deploy/mv_unix.rb | 9 | ||||
-rw-r--r-- | lib/chef/mixin/params_validate.rb | 6 | ||||
-rw-r--r-- | lib/chef/node/attribute.rb | 8 | ||||
-rw-r--r-- | lib/chef/provider/registry_key.rb | 21 | ||||
-rw-r--r-- | lib/chef/provider/windows_task.rb | 11 | ||||
-rw-r--r-- | lib/chef/resource/timezone.rb | 91 | ||||
-rw-r--r-- | lib/chef/resource/windows_task.rb | 478 | ||||
-rw-r--r-- | lib/chef/resources.rb | 1 | ||||
-rw-r--r-- | lib/chef/version.rb | 2 | ||||
-rw-r--r-- | lib/chef/win32/security/sid.rb | 39 |
11 files changed, 411 insertions, 257 deletions
diff --git a/lib/chef/application.rb b/lib/chef/application.rb index 81637eabb8..a63d8218f4 100644 --- a/lib/chef/application.rb +++ b/lib/chef/application.rb @@ -205,7 +205,7 @@ class Chef # Based on config and whether or not STDOUT is a tty, should we setup a # secondary logger for stdout? def want_additional_logger? - Chef::Config.is_default?(:log_location) && Chef::Config[:log_location].tty? && !Chef::Config[:daemonize] + ( Chef::Config[:log_location].class != IO ) && STDOUT.tty? && !Chef::Config[:daemonize] end def configure_stdout_logger diff --git a/lib/chef/file_content_management/deploy/mv_unix.rb b/lib/chef/file_content_management/deploy/mv_unix.rb index cbc9b903a8..dbf58914d5 100644 --- a/lib/chef/file_content_management/deploy/mv_unix.rb +++ b/lib/chef/file_content_management/deploy/mv_unix.rb @@ -1,6 +1,6 @@ # # Author:: Lamont Granquist (<lamont@chef.io>) -# Copyright:: Copyright 2013-2016, Chef Software Inc. +# Copyright:: Copyright 2013-2018, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -44,9 +44,6 @@ class Chef Chef::Log.trace("Applying mode = #{mode.to_s(8)}, uid = #{uid}, gid = #{gid} to #{src}") - # i own the inode, so should be able to at least chmod it - ::File.chmod(mode, src) - # we may be running as non-root in which case because we are doing an mv we cannot preserve # the file modes. after the mv we have a different inode and if we don't have rights to # chown/chgrp on the inode then we can't fix the ownership. @@ -67,6 +64,10 @@ class Chef Chef::Log.warn("Could not set gid = #{gid} on #{src}, file modes not preserved") end + # i own the inode, so should be able to at least chmod it + # NOTE: this must come last due to POSIX stripping sticky mode bits on chown/chgrp + ::File.chmod(mode, src) + Chef::Log.trace("Moving temporary file #{src} into place at #{dst}") FileUtils.mv(src, dst) end diff --git a/lib/chef/mixin/params_validate.rb b/lib/chef/mixin/params_validate.rb index f77adecd60..a71bec6958 100644 --- a/lib/chef/mixin/params_validate.rb +++ b/lib/chef/mixin/params_validate.rb @@ -174,7 +174,9 @@ class Chef to_be.each do |tb| return true if value == tb end - raise Exceptions::ValidationFailed, _validation_message(key, "Option #{key} must be equal to one of: #{to_be.join(", ")}! You passed #{value.inspect}.") + # Ruby will print :something as something, which confuses users so make sure to print them as symbols + # by inspecting the value instead of just printing it + raise Exceptions::ValidationFailed, _validation_message(key, "Option #{key} must be equal to one of: #{to_be.map { |v| v.inspect }.join(", ")}! You passed #{value.inspect}.") end end @@ -300,7 +302,7 @@ class Chef Array(regex).flatten.each do |r| return true if r.match(value.to_s) end - raise Exceptions::ValidationFailed, _validation_message(key, "Option #{key}'s value #{value} does not match regular expression #{regex.inspect}") + raise Exceptions::ValidationFailed, _validation_message(key, "Property #{key}'s value #{value} does not match regular expression #{regex.inspect}") end end diff --git a/lib/chef/node/attribute.rb b/lib/chef/node/attribute.rb index 46683d9405..05b625dd02 100644 --- a/lib/chef/node/attribute.rb +++ b/lib/chef/node/attribute.rb @@ -403,11 +403,11 @@ class Chef end def combined_override(*path) - immutablize(merge_overrides(path)) + merge_overrides(path) end def combined_default(*path) - immutablize(merge_defaults(path)) + merge_defaults(path) end def normal_unless(*args) @@ -599,9 +599,9 @@ class Chef # In all other cases, replace merge_onto with merge_with else if merge_with.kind_of?(Hash) - Chef::Node::VividMash.new(merge_with) + Chef::Node::ImmutableMash.new(merge_with) elsif merge_with.kind_of?(Array) - Chef::Node::AttrArray.new(merge_with) + Chef::Node::ImmutableArray.new(merge_with) else merge_with end diff --git a/lib/chef/provider/registry_key.rb b/lib/chef/provider/registry_key.rb index 7c7b190b95..6b226cc991 100644 --- a/lib/chef/provider/registry_key.rb +++ b/lib/chef/provider/registry_key.rb @@ -126,16 +126,22 @@ class Chef value[:data] = value[:data].to_i end unless current_value[:type] == value[:type] && current_value[:data] == value[:data] - converge_by_value = value - converge_by_value[:data] = "*sensitive value suppressed*" if new_resource.sensitive + converge_by_value = if new_resource.sensitive + value.merge(data: "*sensitive value suppressed*") + else + value + end converge_by("set value #{converge_by_value}") do registry.set_value(new_resource.key, value) end end else - converge_by_value = value - converge_by_value[:data] = "*sensitive value suppressed*" if new_resource.sensitive + converge_by_value = if new_resource.sensitive + value.merge(data: "*sensitive value suppressed*") + else + value + end converge_by("set value #{converge_by_value}") do registry.set_value(new_resource.key, value) @@ -152,8 +158,11 @@ class Chef end new_resource.unscrubbed_values.each do |value| unless @name_hash.key?(value[:name].downcase) - converge_by_value = value - converge_by_value[:data] = "*sensitive value suppressed*" if new_resource.sensitive + converge_by_value = if new_resource.sensitive + value.merge(data: "*sensitive value suppressed*") + else + value + end converge_by("create value #{converge_by_value}") do registry.set_value(new_resource.key, value) diff --git a/lib/chef/provider/windows_task.rb b/lib/chef/provider/windows_task.rb index 98dd8795fa..d2ac9fdc59 100644 --- a/lib/chef/provider/windows_task.rb +++ b/lib/chef/provider/windows_task.rb @@ -570,7 +570,16 @@ 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. - new_resource.password.nil? ? TaskScheduler::TASK_LOGON_SERVICE_ACCOUNT : TaskScheduler::TASK_LOGON_PASSWORD + user_id = new_resource.user + 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 + else + TaskScheduler::TASK_LOGON_INTERACTIVE_TOKEN + end end # This method checks if task and command properties exist since those two are mandatory properties to create a schedules task. diff --git a/lib/chef/resource/timezone.rb b/lib/chef/resource/timezone.rb new file mode 100644 index 0000000000..8834e2404f --- /dev/null +++ b/lib/chef/resource/timezone.rb @@ -0,0 +1,91 @@ +# +# Author:: Kirill Kouznetsov <agon.smith@gmail.com> +# +# Copyright 2018, Kirill Kouznetsov. +# 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" + +class Chef + class Resource + class Timezone < Chef::Resource + preview_resource true + resource_name :timezone + + description "Use the timezone resource to change the system timezone." + introduced "14.6" + + property :timezone, String, + description: "The timezone value to set.", + name_property: true + + action :set do + description "Set the timezone." + + package "tzdata" do + package_name platform_family?("suse") ? "timezone" : "tzdata" + end + + 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" + cmd_check_if_set += " | /usr/bin/awk '/Time.*zone/{print}'" + cmd_check_if_set += " | grep -q #{new_resource.timezone}" + + execute cmd_set_tz do + 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 + + 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" + 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 + end + end + end + end +end diff --git a/lib/chef/resource/windows_task.rb b/lib/chef/resource/windows_task.rb index 0232fe3064..405846cb9e 100644 --- a/lib/chef/resource/windows_task.rb +++ b/lib/chef/resource/windows_task.rb @@ -17,318 +17,320 @@ # require "chef/resource" +require "chef/win32/security" if Chef::Platform.windows? class Chef class Resource class WindowsTask < Chef::Resource - resource_name :windows_task - provides(:windows_task) { true } + if Chef::Platform.windows? + resource_name :windows_task + provides(:windows_task) { true } - description "Use the windows_task resource to create, delete or run a Windows scheduled task. Requires Windows Server 2008 or later due to API usage." - introduced "13.0" + description "Use the windows_task resource to create, delete or run a Windows scheduled task. Requires Windows Server 2008 or later due to API usage." + introduced "13.0" - allowed_actions :create, :delete, :run, :end, :enable, :disable, :change - default_action :create + allowed_actions :create, :delete, :run, :end, :enable, :disable, :change + default_action :create - property :task_name, String, regex: [/\A[^\/\:\*\?\<\>\|]+\z/], - description: "The task name, such as 'Task Name' or '/Task Name'", - name_property: true + property :task_name, String, regex: [/\A[^\/\:\*\?\<\>\|]+\z/], + description: "The task name, such as 'Task Name' or '/Task Name'", + name_property: true - property :command, String, - description: "The command to be executed by the windows scheduled task." + property :command, String, + description: "The command to be executed by the windows scheduled task." - property :cwd, String, - description: "The directory the task will be run from." + property :cwd, String, + description: "The directory the task will be run from." - property :user, String, - description: "The user to run the task as.", - default: "SYSTEM" + property :user, String, + description: "The user to run the task as.", + default: Chef::ReservedNames::Win32::Security::SID.LocalSystem.account_simple_name - property :password, String, - description: "The user’s password. The user property must be set if using this property." + property :password, String, + description: "The user’s password. The user property must be set if using this property." - property :run_level, Symbol, equal_to: [:highest, :limited], - description: "Run with ':limited' or ':highest' privileges.", - default: :limited + property :run_level, Symbol, equal_to: [:highest, :limited], + description: "Run with ':limited' or ':highest' privileges.", + default: :limited - property :force, [TrueClass, FalseClass], - description: "When used with create, will update the task.", - default: false + property :force, [TrueClass, FalseClass], + description: "When used with create, will update the task.", + default: false - property :interactive_enabled, [TrueClass, FalseClass], - description: "Allow task to run interactively or non-interactively. Requires user and password to also be set.", - default: false + property :interactive_enabled, [TrueClass, FalseClass], + description: "Allow task to run interactively or non-interactively. Requires user and password to also be set.", + default: false - property :frequency_modifier, [Integer, String], - default: 1 + property :frequency_modifier, [Integer, String], + default: 1 - property :frequency, Symbol, equal_to: [:minute, - :hourly, - :daily, - :weekly, - :monthly, - :once, - :on_logon, - :onstart, - :on_idle, - :none], - description: "The frequency with which to run the task." + property :frequency, Symbol, equal_to: [:minute, + :hourly, + :daily, + :weekly, + :monthly, + :once, + :on_logon, + :onstart, + :on_idle, + :none], + description: "The frequency with which to run the task." - property :start_day, String, - description: "Specifies the first date on which the task runs in MM/DD/YYYY format." + property :start_day, String, + description: "Specifies the first date on which the task runs in MM/DD/YYYY format." - property :start_time, String, - description: "Specifies the start time to run the task, in HH:mm format." + property :start_time, String, + description: "Specifies the start time to run the task, in HH:mm format." - property :day, [String, Integer], - description: "The day(s) on which the task runs." + property :day, [String, Integer], + description: "The day(s) on which the task runs." - property :months, String, - description: "The Months of the year on which the task runs, such as: 'JAN, FEB' or '\*'. Multiple months should be comma delimited. e.g. 'Jan, Feb, Mar, Dec'." + property :months, String, + description: "The Months of the year on which the task runs, such as: 'JAN, FEB' or '\*'. Multiple months should be comma delimited. e.g. 'Jan, Feb, Mar, Dec'." - property :idle_time, Integer, - description: "For :on_idle frequency, the time (in minutes) without user activity that must pass to trigger the task, from 1 - 999." + property :idle_time, Integer, + description: "For :on_idle frequency, the time (in minutes) without user activity that must pass to trigger the task, from 1 - 999." - property :random_delay, [String, Integer], - description: "Delays the task up to a given time (in seconds)." + property :random_delay, [String, Integer], + description: "Delays the task up to a given time (in seconds)." - property :execution_time_limit, [String, Integer], - description: "The maximum time (in seconds) the task will run.", - default: "PT72H" # 72 hours in ISO8601 duration format + property :execution_time_limit, [String, Integer], + description: "The maximum time (in seconds) the task will run.", + default: "PT72H" # 72 hours in ISO8601 duration format - property :minutes_duration, [String, Integer], - description: "" + property :minutes_duration, [String, Integer], + description: "" - property :minutes_interval, [String, Integer], - description: "" + property :minutes_interval, [String, Integer], + description: "" - property :priority, Integer, - description: "Use to set Priority Levels range from 0 to 10.", - default: 7, callbacks: { "should be in range of 0 to 10" => proc { |v| v >= 0 && v <= 10 } } + property :priority, Integer, + description: "Use to set Priority Levels range from 0 to 10.", + default: 7, callbacks: { "should be in range of 0 to 10" => proc { |v| v >= 0 && v <= 10 } } - property :disallow_start_if_on_batteries, [TrueClass, FalseClass], - introduced: "14.4", default: false, - description: "Disallow start of the task if the system is running on battery power." + property :disallow_start_if_on_batteries, [TrueClass, FalseClass], + introduced: "14.4", default: false, + description: "Disallow start of the task if the system is running on battery power." - property :stop_if_going_on_batteries, [TrueClass, FalseClass], - introduced: "14.4", default: false, - description: "Scheduled task option when system is switching on battery." + property :stop_if_going_on_batteries, [TrueClass, FalseClass], + introduced: "14.4", default: false, + description: "Scheduled task option when system is switching on battery." - attr_accessor :exists, :task, :command_arguments + attr_accessor :exists, :task, :command_arguments - SYSTEM_USERS = ['NT AUTHORITY\SYSTEM', "SYSTEM", 'NT AUTHORITY\LOCALSERVICE', 'NT AUTHORITY\NETWORKSERVICE', 'BUILTIN\USERS', "USERS"].freeze - VALID_WEEK_DAYS = %w{ mon tue wed thu fri sat sun * }.freeze - VALID_DAYS_OF_MONTH = ("1".."31").to_a << "last" << "lastday" - VALID_MONTHS = %w{JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC *}.freeze - VALID_WEEKS = %w{FIRST SECOND THIRD FOURTH LAST LASTDAY}.freeze + VALID_WEEK_DAYS = %w{ mon tue wed thu fri sat sun * }.freeze + VALID_DAYS_OF_MONTH = ("1".."31").to_a << "last" << "lastday" + VALID_MONTHS = %w{JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC *}.freeze + VALID_WEEKS = %w{FIRST SECOND THIRD FOURTH LAST LASTDAY}.freeze - def after_created - if random_delay - validate_random_delay(random_delay, frequency) - random_delay(sec_to_min(random_delay)) - end + def after_created + if random_delay + validate_random_delay(random_delay, frequency) + random_delay(sec_to_min(random_delay)) + end - if execution_time_limit - execution_time_limit(259200) if execution_time_limit == "PT72H" - raise ArgumentError, "Invalid value passed for `execution_time_limit`. Please pass seconds as an Integer (e.g. 60) or a String with numeric values only (e.g. '60')." unless numeric_value_in_string?(execution_time_limit) - execution_time_limit(sec_to_min(execution_time_limit)) - end + if execution_time_limit + execution_time_limit(259200) if execution_time_limit == "PT72H" + raise ArgumentError, "Invalid value passed for `execution_time_limit`. Please pass seconds as an Integer (e.g. 60) or a String with numeric values only (e.g. '60')." unless numeric_value_in_string?(execution_time_limit) + execution_time_limit(sec_to_min(execution_time_limit)) + end - validate_frequency(frequency) if action.include?(:create) || action.include?(:change) - validate_start_time(start_time, frequency) - validate_start_day(start_day, frequency) if start_day - validate_user_and_password(user, password) - validate_interactive_setting(interactive_enabled, password) - validate_create_frequency_modifier(frequency, frequency_modifier) if frequency_modifier - validate_create_day(day, frequency, frequency_modifier) if day - validate_create_months(months, frequency) if months - validate_frequency_monthly(frequency_modifier, months, day) if frequency == :monthly - validate_idle_time(idle_time, frequency) - idempotency_warning_for_frequency_weekly(day, start_day) if frequency == :weekly - end + validate_frequency(frequency) if action.include?(:create) || action.include?(:change) + validate_start_time(start_time, frequency) + validate_start_day(start_day, frequency) if start_day + validate_user_and_password(user, password) + validate_interactive_setting(interactive_enabled, password) + validate_create_frequency_modifier(frequency, frequency_modifier) if frequency_modifier + validate_create_day(day, frequency, frequency_modifier) if day + validate_create_months(months, frequency) if months + validate_frequency_monthly(frequency_modifier, months, day) if frequency == :monthly + validate_idle_time(idle_time, frequency) + idempotency_warning_for_frequency_weekly(day, start_day) if frequency == :weekly + end - private + private - ## Resource is not idempotent when day, start_day is not provided with frequency :weekly - ## we set start_day when not given by user as current date based on which we set the day property for current current date day is monday .. - ## we set the monday as the day so at next run when new_resource.day is nil and current_resource day is monday due to which udpate gets called - def idempotency_warning_for_frequency_weekly(day, start_day) - if start_day.nil? && day.nil? - logger.warn "To maintain idempotency for frequency :weekly provide start_day, start_time and day." + ## Resource is not idempotent when day, start_day is not provided with frequency :weekly + ## we set start_day when not given by user as current date based on which we set the day property for current current date day is monday .. + ## we set the monday as the day so at next run when new_resource.day is nil and current_resource day is monday due to which udpate gets called + def idempotency_warning_for_frequency_weekly(day, start_day) + if start_day.nil? && day.nil? + logger.warn "To maintain idempotency for frequency :weekly provide start_day, start_time and day." + end end - end - # Validate the passed value is numeric values only if it is a string - def numeric_value_in_string?(val) - return true if Integer(val) - rescue ArgumentError - false - end + # Validate the passed value is numeric values only if it is a string + def numeric_value_in_string?(val) + return true if Integer(val) + rescue ArgumentError + false + end - def validate_frequency(frequency) - if frequency.nil? || !([:minute, :hourly, :daily, :weekly, :monthly, :once, :on_logon, :onstart, :on_idle, :none].include?(frequency)) - raise ArgumentError, "Frequency needs to be provided. Valid frequencies are :minute, :hourly, :daily, :weekly, :monthly, :once, :on_logon, :onstart, :on_idle, :none." + def validate_frequency(frequency) + if frequency.nil? || !([:minute, :hourly, :daily, :weekly, :monthly, :once, :on_logon, :onstart, :on_idle, :none].include?(frequency)) + raise ArgumentError, "Frequency needs to be provided. Valid frequencies are :minute, :hourly, :daily, :weekly, :monthly, :once, :on_logon, :onstart, :on_idle, :none." + end end - end - def validate_frequency_monthly(frequency_modifier, months, day) - # validates the frequency :monthly and raises error if frequency_modifier is first, second, thrid etc and day is not provided - if (frequency_modifier != 1) && (frequency_modifier_includes_days_of_weeks?(frequency_modifier)) && !(day) - raise ArgumentError, "Please select day on which you want to run the task e.g. 'Mon, Tue'. Multiple values must be seprated by comma." + def validate_frequency_monthly(frequency_modifier, months, day) + # validates the frequency :monthly and raises error if frequency_modifier is first, second, thrid etc and day is not provided + if (frequency_modifier != 1) && (frequency_modifier_includes_days_of_weeks?(frequency_modifier)) && !(day) + raise ArgumentError, "Please select day on which you want to run the task e.g. 'Mon, Tue'. Multiple values must be seprated by comma." + end + + # frequency_modifer 2-12 is used to set every (n) months, so using :months propety with frequency_modifer is not valid since they both used to set months. + # Not checking value 1 here for frequecy_modifier since we are setting that as default value it won't break anything since preference is given to months property + if (frequency_modifier.to_i.between?(2, 12)) && !(months.nil?) + raise ArgumentError, "For frequency :monthly either use property months or frequency_modifier to set months." + end end - # frequency_modifer 2-12 is used to set every (n) months, so using :months propety with frequency_modifer is not valid since they both used to set months. - # Not checking value 1 here for frequecy_modifier since we are setting that as default value it won't break anything since preference is given to months property - if (frequency_modifier.to_i.between?(2, 12)) && !(months.nil?) - raise ArgumentError, "For frequency :monthly either use property months or frequency_modifier to set months." + # returns true if frequency_modifer has values First, second, third, fourth, last, lastday + def frequency_modifier_includes_days_of_weeks?(frequency_modifier) + frequency_modifier = frequency_modifier.to_s.split(",") + frequency_modifier.map! { |value| value.strip.upcase } + (frequency_modifier - VALID_WEEKS).empty? end - end - # returns true if frequency_modifer has values First, second, third, fourth, last, lastday - def frequency_modifier_includes_days_of_weeks?(frequency_modifier) - frequency_modifier = frequency_modifier.to_s.split(",") - frequency_modifier.map! { |value| value.strip.upcase } - (frequency_modifier - VALID_WEEKS).empty? - end + def validate_random_delay(random_delay, frequency) + if [:on_logon, :onstart, :on_idle, :none].include? frequency + raise ArgumentError, "`random_delay` property is supported only for frequency :once, :minute, :hourly, :daily, :weekly and :monthly" + end - def validate_random_delay(random_delay, frequency) - if [:on_logon, :onstart, :on_idle, :none].include? frequency - raise ArgumentError, "`random_delay` property is supported only for frequency :once, :minute, :hourly, :daily, :weekly and :monthly" + raise ArgumentError, "Invalid value passed for `random_delay`. Please pass seconds as an Integer (e.g. 60) or a String with numeric values only (e.g. '60')." unless numeric_value_in_string?(random_delay) end - raise ArgumentError, "Invalid value passed for `random_delay`. Please pass seconds as an Integer (e.g. 60) or a String with numeric values only (e.g. '60')." unless numeric_value_in_string?(random_delay) - end + # @todo when we drop ruby 2.3 support this should be converted to .match?() instead of =~f + def validate_start_day(start_day, frequency) + if start_day && frequency == :none + raise ArgumentError, "`start_day` property is not supported with frequency: #{frequency}" + end - # @todo when we drop ruby 2.3 support this should be converted to .match?() instead of =~f - def validate_start_day(start_day, frequency) - if start_day && frequency == :none - raise ArgumentError, "`start_day` property is not supported with frequency: #{frequency}" + # make sure the start_day is in MM/DD/YYYY format: http://rubular.com/r/cgjHemtWl5 + if start_day + raise ArgumentError, "`start_day` property must be in the MM/DD/YYYY format." unless /^(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.](19|20)\d\d$/ =~ start_day + end end - # make sure the start_day is in MM/DD/YYYY format: http://rubular.com/r/cgjHemtWl5 - if start_day - raise ArgumentError, "`start_day` property must be in the MM/DD/YYYY format." unless /^(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.](19|20)\d\d$/ =~ start_day + # @todo when we drop ruby 2.3 support this should be converted to .match?() instead of =~ + def validate_start_time(start_time, frequency) + if start_time + raise ArgumentError, "`start_time` property is not supported with `frequency :none`" if frequency == :none + raise ArgumentError, "`start_time` property must be in the HH:mm format (e.g. 6:20pm -> 18:20)." unless /^[0-2][0-9]:[0-5][0-9]$/ =~ start_time + else + raise ArgumentError, "`start_time` needs to be provided with `frequency :once`" if frequency == :once + end end - end - # @todo when we drop ruby 2.3 support this should be converted to .match?() instead of =~ - def validate_start_time(start_time, frequency) - if start_time - raise ArgumentError, "`start_time` property is not supported with `frequency :none`" if frequency == :none - raise ArgumentError, "`start_time` property must be in the HH:mm format (e.g. 6:20pm -> 18:20)." unless /^[0-2][0-9]:[0-5][0-9]$/ =~ start_time - else - raise ArgumentError, "`start_time` needs to be provided with `frequency :once`" if frequency == :once + def validate_user_and_password(user, password) + if password_required?(user) && password.nil? + raise ArgumentError, "Cannot specify a user other than the system users without specifying a password!. Valid passwordless users: '#{Chef::ReservedNames::Win32::Security::SID::SYSTEM_USER.join("', '")}'" + end end - end - def validate_user_and_password(user, password) - if password_required?(user) && password.nil? - raise ArgumentError, %q{Cannot specify a user other than the system users without specifying a password!. Valid passwordless users: 'NT AUTHORITY\SYSTEM', 'SYSTEM', 'NT AUTHORITY\LOCALSERVICE', 'NT AUTHORITY\NETWORKSERVICE', 'BUILTIN\USERS', 'USERS'} + def password_required?(user) + return false if user.nil? + @password_required ||= !Chef::ReservedNames::Win32::Security::SID.system_user?(user) end - end - - def password_required?(user) - return false if user.nil? - @password_required ||= !SYSTEM_USERS.include?(user.upcase) - end - def validate_interactive_setting(interactive_enabled, password) - raise ArgumentError, "Please provide the password when attempting to set interactive/non-interactive." if interactive_enabled && password.nil? - end - - def validate_create_frequency_modifier(frequency, frequency_modifier) - if ([:on_logon, :onstart, :on_idle, :none].include?(frequency)) && ( frequency_modifier != 1) - raise ArgumentError, "frequency_modifier property not supported with frequency :#{frequency}" + def validate_interactive_setting(interactive_enabled, password) + raise ArgumentError, "Please provide the password when attempting to set interactive/non-interactive." if interactive_enabled && password.nil? end - if frequency == :monthly - unless (1..12).cover?(frequency_modifier.to_i) || frequency_modifier_includes_days_of_weeks?(frequency_modifier) - raise ArgumentError, "frequency_modifier value #{frequency_modifier} is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST'." + def validate_create_frequency_modifier(frequency, frequency_modifier) + if ([:on_logon, :onstart, :on_idle, :none].include?(frequency)) && ( frequency_modifier != 1) + raise ArgumentError, "frequency_modifier property not supported with frequency :#{frequency}" end - else - unless frequency.nil? || frequency_modifier.nil? - frequency_modifier = frequency_modifier.to_i - min = 1 - max = case frequency - when :minute - 1439 - when :hourly - 23 - when :daily - 365 - when :weekly - 52 - else - min - end - unless frequency_modifier.between?(min, max) - raise ArgumentError, "frequency_modifier value #{frequency_modifier} is invalid. Valid values for :#{frequency} frequency are #{min} - #{max}." + + if frequency == :monthly + unless (1..12).cover?(frequency_modifier.to_i) || frequency_modifier_includes_days_of_weeks?(frequency_modifier) + raise ArgumentError, "frequency_modifier value #{frequency_modifier} is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST'." + end + else + unless frequency.nil? || frequency_modifier.nil? + frequency_modifier = frequency_modifier.to_i + min = 1 + max = case frequency + when :minute + 1439 + when :hourly + 23 + when :daily + 365 + when :weekly + 52 + else + min + end + unless frequency_modifier.between?(min, max) + raise ArgumentError, "frequency_modifier value #{frequency_modifier} is invalid. Valid values for :#{frequency} frequency are #{min} - #{max}." + end end end end - end - - def validate_create_day(day, frequency, frequency_modifier) - raise ArgumentError, "day property is only valid for tasks that run monthly or weekly" unless [:weekly, :monthly].include?(frequency) - # This has been verified with schtask.exe https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/schtasks#d-dayday-- - # verified with earlier code if day "*" is given with frequency it raised exception Invalid value for /D option - raise ArgumentError, "day wild card (*) is only valid with frequency :weekly" if frequency == :monthly && day == "*" - - if day.is_a?(String) && day.to_i.to_s != day - days = day.split(",") - if days_includes_days_of_months?(days) - # Following error will be raise if day is set as 1-31 and frequency is selected as :weekly since those values are valid with only frequency :monthly - raise ArgumentError, "day values 1-31 or last is only valid with frequency :monthly" if frequency == :weekly - else - days.map! { |day| day.to_s.strip.downcase } - unless (days - VALID_WEEK_DAYS).empty? - raise ArgumentError, "day property invalid. Only valid values are: #{VALID_WEEK_DAYS.map(&:upcase).join(', ')}. Multiple values must be separated by a comma." + def validate_create_day(day, frequency, frequency_modifier) + raise ArgumentError, "day property is only valid for tasks that run monthly or weekly" unless [:weekly, :monthly].include?(frequency) + + # This has been verified with schtask.exe https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/schtasks#d-dayday-- + # verified with earlier code if day "*" is given with frequency it raised exception Invalid value for /D option + raise ArgumentError, "day wild card (*) is only valid with frequency :weekly" if frequency == :monthly && day == "*" + + if day.is_a?(String) && day.to_i.to_s != day + days = day.split(",") + if days_includes_days_of_months?(days) + # Following error will be raise if day is set as 1-31 and frequency is selected as :weekly since those values are valid with only frequency :monthly + raise ArgumentError, "day values 1-31 or last is only valid with frequency :monthly" if frequency == :weekly + else + days.map! { |day| day.to_s.strip.downcase } + unless (days - VALID_WEEK_DAYS).empty? + raise ArgumentError, "day property invalid. Only valid values are: #{VALID_WEEK_DAYS.map(&:upcase).join(', ')}. Multiple values must be separated by a comma." + end end end end - end - def validate_create_months(months, frequency) - raise ArgumentError, "months property is only valid for tasks that run monthly" if frequency != :monthly - if months.is_a?(String) - months = months.split(",") - months.map! { |month| month.strip.upcase } - unless (months - VALID_MONTHS).empty? - raise ArgumentError, "months property invalid. Only valid values are: #{VALID_MONTHS.join(', ')}. Multiple values must be separated by a comma." + def validate_create_months(months, frequency) + raise ArgumentError, "months property is only valid for tasks that run monthly" if frequency != :monthly + if months.is_a?(String) + months = months.split(",") + months.map! { |month| month.strip.upcase } + unless (months - VALID_MONTHS).empty? + raise ArgumentError, "months property invalid. Only valid values are: #{VALID_MONTHS.join(', ')}. Multiple values must be separated by a comma." + end end end - end - - # This method returns true if day has values from 1-31 which is a days of moths and used with frequency :monthly - def days_includes_days_of_months?(days) - days.map! { |day| day.to_s.strip.downcase } - (days - VALID_DAYS_OF_MONTH).empty? - end - def validate_idle_time(idle_time, frequency) - if !idle_time.nil? && frequency != :on_idle - raise ArgumentError, "idle_time property is only valid for tasks that run on_idle" + # This method returns true if day has values from 1-31 which is a days of moths and used with frequency :monthly + def days_includes_days_of_months?(days) + days.map! { |day| day.to_s.strip.downcase } + (days - VALID_DAYS_OF_MONTH).empty? end - if idle_time.nil? && frequency == :on_idle - raise ArgumentError, "idle_time value should be set for :on_idle frequency." - end - unless idle_time.nil? || idle_time > 0 && idle_time <= 999 - raise ArgumentError, "idle_time value #{idle_time} is invalid. Valid values for :on_idle frequency are 1 - 999." + + def validate_idle_time(idle_time, frequency) + if !idle_time.nil? && frequency != :on_idle + raise ArgumentError, "idle_time property is only valid for tasks that run on_idle" + end + if idle_time.nil? && frequency == :on_idle + raise ArgumentError, "idle_time value should be set for :on_idle frequency." + end + unless idle_time.nil? || idle_time > 0 && idle_time <= 999 + raise ArgumentError, "idle_time value #{idle_time} is invalid. Valid values for :on_idle frequency are 1 - 999." + end end - end - # Converts the number of seconds to an ISO8601 duration format and returns it. - # Ref : https://github.com/arnau/ISO8601/blob/master/lib/iso8601/duration.rb#L18-L23 - # e.g. - # ISO8601::Duration.new(65707200).to_s - # returns 'PT65707200S' - def sec_to_dur(seconds) - ISO8601::Duration.new(seconds.to_i).to_s - end + # Converts the number of seconds to an ISO8601 duration format and returns it. + # Ref : https://github.com/arnau/ISO8601/blob/master/lib/iso8601/duration.rb#L18-L23 + # e.g. + # ISO8601::Duration.new(65707200).to_s + # returns 'PT65707200S' + def sec_to_dur(seconds) + ISO8601::Duration.new(seconds.to_i).to_s + end - def sec_to_min(seconds) - seconds.to_i / 60 + def sec_to_min(seconds) + seconds.to_i / 60 + end end end end diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb index d99aeed3ad..6324ce5b66 100644 --- a/lib/chef/resources.rb +++ b/lib/chef/resources.rb @@ -141,3 +141,4 @@ require "chef/resource/windows_printer_port" require "chef/resource/windows_shortcut" require "chef/resource/windows_task" require "chef/resource/windows_workgroup" +require "chef/resource/timezone" diff --git a/lib/chef/version.rb b/lib/chef/version.rb index 274f6323bc..4626d341e2 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.6.17") + VERSION = Chef::VersionString.new("14.6.43") end # diff --git a/lib/chef/win32/security/sid.rb b/lib/chef/win32/security/sid.rb index b551cbd2e3..43efc6e3fe 100644 --- a/lib/chef/win32/security/sid.rb +++ b/lib/chef/win32/security/sid.rb @@ -246,6 +246,45 @@ class Chef SID.from_account("#{::ENV['USERDOMAIN']}\\#{::ENV['USERNAME']}") end + SERVICE_ACCOUNT_USERS = [self.LocalSystem, + self.NtLocal, + self.NtNetwork].flat_map do |user_type| + [user_type.account_simple_name.upcase, + user_type.account_name.upcase] + end.freeze + + BUILT_IN_GROUPS = [self.BuiltinAdministrators, + self.BuiltinUsers, self.Guests].flat_map do |user_type| + [user_type.account_simple_name.upcase, + user_type.account_name.upcase] + end.freeze + + SYSTEM_USER = SERVICE_ACCOUNT_USERS + BUILT_IN_GROUPS + + # Сheck if the user belongs to service accounts category + # + # @return [Boolean] True or False + # + def self.service_account_user?(user) + SERVICE_ACCOUNT_USERS.include?(user.to_s.upcase) + end + + # Сheck if the user is in builtin system group + # + # @return [Boolean] True or False + # + def self.group_user?(user) + BUILT_IN_GROUPS.include?(user.to_s.upcase) + end + + # Сheck if the user belongs to system users category + # + # @return [Boolean] True or False + # + def self.system_user?(user) + SYSTEM_USER.include?(user.to_s.upcase) + end + # See https://technet.microsoft.com/en-us/library/cc961992.aspx # In practice, this is SID.Administrators if the current_user is an admin (even if not # running elevated), and is current_user otherwise. |