puppet 6.22.1-universal-darwin → 6.23.0-universal-darwin

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +14 -14
  3. data/ext/osx/puppet.plist +2 -0
  4. data/lib/puppet/application/agent.rb +12 -5
  5. data/lib/puppet/application/apply.rb +2 -1
  6. data/lib/puppet/application/device.rb +2 -1
  7. data/lib/puppet/application/resource.rb +2 -1
  8. data/lib/puppet/application/script.rb +2 -1
  9. data/lib/puppet/configurer/downloader.rb +2 -1
  10. data/lib/puppet/defaults.rb +5 -3
  11. data/lib/puppet/file_serving/fileset.rb +14 -2
  12. data/lib/puppet/functions/all.rb +1 -1
  13. data/lib/puppet/functions/camelcase.rb +1 -1
  14. data/lib/puppet/functions/capitalize.rb +2 -2
  15. data/lib/puppet/functions/downcase.rb +2 -2
  16. data/lib/puppet/functions/get.rb +5 -5
  17. data/lib/puppet/functions/group_by.rb +13 -5
  18. data/lib/puppet/functions/lest.rb +1 -1
  19. data/lib/puppet/functions/new.rb +100 -100
  20. data/lib/puppet/functions/partition.rb +4 -4
  21. data/lib/puppet/functions/require.rb +5 -5
  22. data/lib/puppet/functions/sort.rb +3 -3
  23. data/lib/puppet/functions/tree_each.rb +7 -9
  24. data/lib/puppet/functions/type.rb +4 -4
  25. data/lib/puppet/functions/upcase.rb +2 -2
  26. data/lib/puppet/http/resolver/server_list.rb +15 -4
  27. data/lib/puppet/http/service/compiler.rb +69 -0
  28. data/lib/puppet/http/service/file_server.rb +2 -1
  29. data/lib/puppet/indirector/catalog/compiler.rb +1 -0
  30. data/lib/puppet/indirector/file_metadata/rest.rb +1 -0
  31. data/lib/puppet/parser/functions/fqdn_rand.rb +14 -6
  32. data/lib/puppet/pops/types/p_sem_ver_type.rb +8 -2
  33. data/lib/puppet/pops/types/p_sensitive_type.rb +10 -0
  34. data/lib/puppet/provider/package/nim.rb +11 -6
  35. data/lib/puppet/provider/service/systemd.rb +13 -3
  36. data/lib/puppet/provider/service/windows.rb +38 -0
  37. data/lib/puppet/provider/user/directoryservice.rb +25 -12
  38. data/lib/puppet/reference/configuration.rb +1 -1
  39. data/lib/puppet/transaction/additional_resource_generator.rb +1 -1
  40. data/lib/puppet/type/file/selcontext.rb +1 -1
  41. data/lib/puppet/type/file.rb +19 -1
  42. data/lib/puppet/type/service.rb +18 -38
  43. data/lib/puppet/type/tidy.rb +21 -2
  44. data/lib/puppet/type/user.rb +38 -20
  45. data/lib/puppet/util/selinux.rb +30 -4
  46. data/lib/puppet/version.rb +1 -1
  47. data/locales/puppet.pot +109 -101
  48. data/man/man5/puppet.conf.5 +272 -252
  49. data/man/man8/puppet-agent.8 +1 -1
  50. data/man/man8/puppet-apply.8 +1 -1
  51. data/man/man8/puppet-catalog.8 +1 -1
  52. data/man/man8/puppet-config.8 +1 -1
  53. data/man/man8/puppet-describe.8 +1 -1
  54. data/man/man8/puppet-device.8 +1 -1
  55. data/man/man8/puppet-doc.8 +1 -1
  56. data/man/man8/puppet-epp.8 +1 -1
  57. data/man/man8/puppet-facts.8 +1 -1
  58. data/man/man8/puppet-filebucket.8 +1 -1
  59. data/man/man8/puppet-generate.8 +1 -1
  60. data/man/man8/puppet-help.8 +1 -1
  61. data/man/man8/puppet-key.8 +1 -1
  62. data/man/man8/puppet-lookup.8 +1 -1
  63. data/man/man8/puppet-man.8 +1 -1
  64. data/man/man8/puppet-module.8 +1 -1
  65. data/man/man8/puppet-node.8 +1 -1
  66. data/man/man8/puppet-parser.8 +1 -1
  67. data/man/man8/puppet-plugin.8 +1 -1
  68. data/man/man8/puppet-report.8 +1 -1
  69. data/man/man8/puppet-resource.8 +1 -1
  70. data/man/man8/puppet-script.8 +1 -1
  71. data/man/man8/puppet-ssl.8 +1 -1
  72. data/man/man8/puppet-status.8 +1 -1
  73. data/man/man8/puppet.8 +2 -2
  74. data/spec/fixtures/ssl/127.0.0.1-key.pem +107 -57
  75. data/spec/fixtures/ssl/127.0.0.1.pem +52 -31
  76. data/spec/fixtures/ssl/bad-basic-constraints.pem +57 -35
  77. data/spec/fixtures/ssl/bad-int-basic-constraints.pem +57 -35
  78. data/spec/fixtures/ssl/ca.pem +57 -35
  79. data/spec/fixtures/ssl/crl.pem +28 -18
  80. data/spec/fixtures/ssl/ec-key.pem +11 -11
  81. data/spec/fixtures/ssl/ec.pem +33 -24
  82. data/spec/fixtures/ssl/encrypted-ec-key.pem +12 -12
  83. data/spec/fixtures/ssl/encrypted-key.pem +108 -58
  84. data/spec/fixtures/ssl/intermediate-agent-crl.pem +28 -19
  85. data/spec/fixtures/ssl/intermediate-agent.pem +57 -36
  86. data/spec/fixtures/ssl/intermediate-crl.pem +31 -21
  87. data/spec/fixtures/ssl/intermediate.pem +57 -36
  88. data/spec/fixtures/ssl/pluto-key.pem +107 -57
  89. data/spec/fixtures/ssl/pluto.pem +52 -30
  90. data/spec/fixtures/ssl/request-key.pem +107 -57
  91. data/spec/fixtures/ssl/request.pem +47 -26
  92. data/spec/fixtures/ssl/revoked-key.pem +107 -57
  93. data/spec/fixtures/ssl/revoked.pem +52 -30
  94. data/spec/fixtures/ssl/signed-key.pem +107 -57
  95. data/spec/fixtures/ssl/signed.pem +52 -30
  96. data/spec/fixtures/ssl/tampered-cert.pem +52 -30
  97. data/spec/fixtures/ssl/tampered-csr.pem +47 -26
  98. data/spec/fixtures/ssl/unknown-127.0.0.1-key.pem +107 -57
  99. data/spec/fixtures/ssl/unknown-127.0.0.1.pem +50 -29
  100. data/spec/fixtures/ssl/unknown-ca-key.pem +107 -57
  101. data/spec/fixtures/ssl/unknown-ca.pem +55 -33
  102. data/spec/integration/application/resource_spec.rb +30 -0
  103. data/spec/lib/puppet/test_ca.rb +2 -2
  104. data/spec/unit/application/agent_spec.rb +7 -2
  105. data/spec/unit/configurer/downloader_spec.rb +6 -0
  106. data/spec/unit/configurer_spec.rb +23 -0
  107. data/spec/unit/file_serving/fileset_spec.rb +60 -0
  108. data/spec/unit/gettext/config_spec.rb +12 -0
  109. data/spec/unit/http/service/compiler_spec.rb +123 -0
  110. data/spec/unit/indirector/catalog/compiler_spec.rb +14 -10
  111. data/spec/unit/parser/functions/fqdn_rand_spec.rb +15 -1
  112. data/spec/unit/pops/types/p_sem_ver_type_spec.rb +18 -0
  113. data/spec/unit/pops/types/p_sensitive_type_spec.rb +18 -0
  114. data/spec/unit/provider/package/nim_spec.rb +42 -0
  115. data/spec/unit/provider/service/init_spec.rb +1 -0
  116. data/spec/unit/provider/service/openwrt_spec.rb +3 -1
  117. data/spec/unit/provider/service/systemd_spec.rb +42 -8
  118. data/spec/unit/provider/service/windows_spec.rb +202 -0
  119. data/spec/unit/provider/user/directoryservice_spec.rb +67 -35
  120. data/spec/unit/ssl/state_machine_spec.rb +19 -5
  121. data/spec/unit/transaction/additional_resource_generator_spec.rb +0 -2
  122. data/spec/unit/transaction_spec.rb +18 -20
  123. data/spec/unit/type/file/selinux_spec.rb +3 -3
  124. data/spec/unit/type/service_spec.rb +59 -188
  125. data/spec/unit/type/tidy_spec.rb +17 -7
  126. data/spec/unit/type/user_spec.rb +45 -0
  127. data/spec/unit/util/selinux_spec.rb +87 -16
  128. data/tasks/generate_cert_fixtures.rake +2 -2
  129. metadata +4 -2
@@ -106,7 +106,7 @@ class Puppet::HTTP::Service::FileServer < Puppet::HTTP::Service
106
106
  # An array with the request response and an array of the deserialized
107
107
  # metadata for each file returned from the server
108
108
  #
109
- def get_file_metadatas(path: nil, environment:, recurse: :false, recurselimit: nil, ignore: nil, links: :manage, checksum_type: Puppet[:digest_algorithm], source_permissions: :ignore)
109
+ def get_file_metadatas(path: nil, environment:, recurse: :false, recurselimit: nil, max_files: nil, ignore: nil, links: :manage, checksum_type: Puppet[:digest_algorithm], source_permissions: :ignore)
110
110
  validate_path(path)
111
111
 
112
112
  headers = add_puppet_headers('Accept' => get_mime_types(Puppet::FileServing::Metadata).join(', '))
@@ -117,6 +117,7 @@ class Puppet::HTTP::Service::FileServer < Puppet::HTTP::Service
117
117
  params: {
118
118
  recurse: recurse,
119
119
  recurselimit: recurselimit,
120
+ max_files: max_files,
120
121
  ignore: ignore,
121
122
  links: links,
122
123
  checksum_type: checksum_type,
@@ -194,6 +194,7 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code
194
194
  :source_permissions => resource[:source_permissions] ? resource[:source_permissions].to_sym : :ignore,
195
195
  :recurse => true,
196
196
  :recurselimit => resource[:recurselimit],
197
+ :max_files => resource[:max_files],
197
198
  :ignore => resource[:ignore],
198
199
  }
199
200
 
@@ -46,6 +46,7 @@ class Puppet::Indirector::FileMetadata::Rest < Puppet::Indirector::REST
46
46
  environment: request.environment.to_s,
47
47
  recurse: request.options[:recurse],
48
48
  recurselimit: request.options[:recurselimit],
49
+ max_files: request.options[:max_files],
49
50
  ignore: request.options[:ignore],
50
51
  links: request.options[:links],
51
52
  checksum_type: request.options[:checksum_type],
@@ -2,13 +2,16 @@ require 'digest/md5'
2
2
  require 'digest/sha2'
3
3
 
4
4
  Puppet::Parser::Functions::newfunction(:fqdn_rand, :arity => -2, :type => :rvalue, :doc =>
5
- "Usage: `fqdn_rand(MAX, [SEED])`. MAX is required and must be a positive
6
- integer; SEED is optional and may be any number or string.
5
+ "Usage: `fqdn_rand(MAX, [SEED], [DOWNCASE])`. MAX is required and must be a positive
6
+ integer; SEED is optional and may be any number or string; DOWNCASE is optional
7
+ and should be a boolean true or false.
7
8
 
8
9
  Generates a random Integer number greater than or equal to 0 and less than MAX,
9
10
  combining the `$fqdn` fact and the value of SEED for repeatable randomness.
10
11
  (That is, each node will get a different random number from this function, but
11
- a given node's result will be the same every time unless its hostname changes.)
12
+ a given node's result will be the same every time unless its hostname changes.) If
13
+ DOWNCASE is true, then the `fqdn` fact will be downcased when computing the value
14
+ so that the result is not sensitive to the case of the `fqdn` fact.
12
15
 
13
16
  This function is usually used for spacing out runs of resource-intensive cron
14
17
  tasks that run on many nodes, which could cause a thundering herd or degrade
@@ -17,7 +20,12 @@ Puppet::Parser::Functions::newfunction(:fqdn_rand, :arity => -2, :type => :rvalu
17
20
  node. (For example, `fqdn_rand(30)`, `fqdn_rand(30, 'expensive job 1')`, and
18
21
  `fqdn_rand(30, 'expensive job 2')` will produce totally different numbers.)") do |args|
19
22
  max = args.shift.to_i
20
-
23
+ initial_seed = args.shift
24
+ downcase = !!args.shift
25
+
26
+ fqdn = self['::fqdn']
27
+ fqdn = fqdn.downcase if downcase
28
+
21
29
  # Puppet 5.4's fqdn_rand function produces a different value than earlier versions
22
30
  # for the same set of inputs.
23
31
  # This causes problems because the values are often written into service configuration files.
@@ -27,9 +35,9 @@ Puppet::Parser::Functions::newfunction(:fqdn_rand, :arity => -2, :type => :rvalu
27
35
  # when running on a non-FIPS enabled platform and only using SHA256 on FIPS enabled
28
36
  # platforms.
29
37
  if Puppet::Util::Platform.fips_enabled?
30
- seed = Digest::SHA256.hexdigest([self['::fqdn'],max,args].join(':')).hex
38
+ seed = Digest::SHA256.hexdigest([fqdn,max,initial_seed].join(':')).hex
31
39
  else
32
- seed = Digest::MD5.hexdigest([self['::fqdn'],max,args].join(':')).hex
40
+ seed = Digest::MD5.hexdigest([fqdn,max,initial_seed].join(':')).hex
33
41
  end
34
42
 
35
43
  Puppet::Util.deterministic_rand_int(seed,max)
@@ -95,16 +95,22 @@ class PSemVerType < PScalarType
95
95
  end
96
96
 
97
97
  def from_args(major, minor, patch, prerelease = nil, build = nil)
98
- SemanticPuppet::Version.new(major, minor, patch, prerelease, build)
98
+ SemanticPuppet::Version.new(major, minor, patch, to_array(prerelease), to_array(build))
99
99
  end
100
100
 
101
101
  def from_hash(hash)
102
- SemanticPuppet::Version.new(hash['major'], hash['minor'], hash['patch'], hash['prerelease'], hash['build'])
102
+ SemanticPuppet::Version.new(hash['major'], hash['minor'], hash['patch'], to_array(hash['prerelease']), to_array(hash['build']))
103
103
  end
104
104
 
105
105
  def on_error(str)
106
106
  _("The string '%{str}' cannot be converted to a SemVer") % { str: str }
107
107
  end
108
+
109
+ private
110
+
111
+ def to_array(component)
112
+ component ? [component] : nil
113
+ end
108
114
  end
109
115
  end
110
116
 
@@ -24,6 +24,16 @@ class PSensitiveType < PTypeWithContainedType
24
24
  def inspect
25
25
  "#<#{self}>"
26
26
  end
27
+
28
+ def hash
29
+ @value.hash
30
+ end
31
+
32
+ def ==(other)
33
+ other.is_a?(Sensitive) &&
34
+ other.hash == hash
35
+ end
36
+ alias eql? ==
27
37
  end
28
38
 
29
39
  def self.register_ptype(loader, ir)
@@ -154,20 +154,25 @@ Puppet::Type.type(:package).provide :nim, :parent => :aix, :source => :aix do
154
154
  # I spent a lot of time trying to figure out a solution that didn't
155
155
  # require parsing the `nimclient -o showres` output and was unable to
156
156
  # do so.
157
- self::HEADER_LINE_REGEX = /^([^\s]+)\s+[^@]+@@(I|R):(\1)\s+[^\s]+$/
158
- self::PACKAGE_LINE_REGEX = /^.*@@(I|R):(.*)$/
159
- self::RPM_PACKAGE_REGEX = /^(.*)-(.*-\d+) \2$/
157
+ self::HEADER_LINE_REGEX = /^([^\s]+)\s+[^@]+@@(I|R|S):(\1)\s+[^\s]+$/
158
+ self::PACKAGE_LINE_REGEX = /^.*@@(I|R|S):(.*)$/
159
+ self::RPM_PACKAGE_REGEX = /^(.*)-(.*-\d+\w*) \2$/
160
160
  self::INSTALLP_PACKAGE_REGEX = /^(.*) (.*)$/
161
161
 
162
162
  # Here is some sample output that shows what the above regexes will be up
163
163
  # against:
164
- # FOR AN INSTALLP PACKAGE:
164
+ # FOR AN INSTALLP(bff) PACKAGE:
165
165
  #
166
166
  # mypackage.foo ALL @@I:mypackage.foo _all_filesets
167
- # @ 1.2.3.1 MyPackage Runtime Environment @@I:mypackage.foo 1.2.3.1
168
167
  # + 1.2.3.4 MyPackage Runtime Environment @@I:mypackage.foo 1.2.3.4
169
168
  # + 1.2.3.8 MyPackage Runtime Environment @@I:mypackage.foo 1.2.3.8
170
169
  #
170
+ # FOR AN INSTALLP(bff) PACKAGE with security update:
171
+ #
172
+ # bos.net ALL @@S:bos.net _all_filesets
173
+ # + 7.2.0.1 TCP/IP ntp Applications @@S:bos.net.tcp.ntp 7.2.0.1
174
+ # + 7.2.0.2 TCP/IP ntp Applications @@S:bos.net.tcp.ntp 7.2.0.2
175
+ #
171
176
  # FOR AN RPM PACKAGE:
172
177
  #
173
178
  # mypackage.foo ALL @@R:mypackage.foo _all_filesets
@@ -243,7 +248,7 @@ Puppet::Type.type(:package).provide :nim, :parent => :aix, :source => :aix do
243
248
  package_string = match.captures[1]
244
249
 
245
250
  case package_type_flag
246
- when "I"
251
+ when "I","S"
247
252
  parse_installp_package_string(package_string)
248
253
  when "R"
249
254
  parse_rpm_package_string(package_string)
@@ -45,8 +45,13 @@ Puppet::Type.type(:service).provide :systemd, :parent => :base do
45
45
  def enabled_insync?(current)
46
46
  case cached_enabled?[:output]
47
47
  when 'static'
48
- Puppet.debug("Unable to enable or disable static service #{@resource[:name]}")
49
- return true
48
+ # masking static services is OK, but enabling/disabling them is not
49
+ if @resource[:enable] == :mask
50
+ current == @resource[:enable]
51
+ else
52
+ Puppet.debug("Unable to enable or disable static service #{@resource[:name]}")
53
+ return true
54
+ end
50
55
  when 'indirect'
51
56
  Puppet.debug("Service #{@resource[:name]} is in 'indirect' state and cannot be enabled/disabled")
52
57
  return true
@@ -159,10 +164,15 @@ Puppet::Type.type(:service).provide :systemd, :parent => :base do
159
164
  end
160
165
 
161
166
  def mask
162
- self.disable
167
+ disable if exist?
163
168
  systemctl_change_enable(:mask)
164
169
  end
165
170
 
171
+ def exist?
172
+ result = execute([command(:systemctl), 'cat', '--', @resource[:name]], :failonfail => false)
173
+ result.exitstatus == 0
174
+ end
175
+
166
176
  def unmask
167
177
  systemctl_change_enable(:unmask)
168
178
  end
@@ -128,17 +128,55 @@ Puppet::Type.type(:service).provide :windows, :parent => :service do
128
128
  services
129
129
  end
130
130
 
131
+ def logonaccount_insync?(current)
132
+ @normalized_logon_account ||= normalize_logonaccount
133
+ @resource[:logonaccount] = @normalized_logon_account
134
+
135
+ insync = @resource[:logonaccount] == current
136
+ self.logonpassword = @resource[:logonpassword] if insync
137
+ insync
138
+ end
139
+
131
140
  def logonaccount
132
141
  return unless Puppet::Util::Windows::Service.exists?(@resource[:name])
133
142
  Puppet::Util::Windows::Service.logon_account(@resource[:name])
134
143
  end
135
144
 
136
145
  def logonaccount=(value)
146
+ validate_logon_credentials
137
147
  Puppet::Util::Windows::Service.set_startup_configuration(@resource[:name], options: {logon_account: value, logon_password: @resource[:logonpassword]})
138
148
  restart if @resource[:ensure] == :running && [:running, :paused].include?(status)
139
149
  end
140
150
 
141
151
  def logonpassword=(value)
152
+ validate_logon_credentials
142
153
  Puppet::Util::Windows::Service.set_startup_configuration(@resource[:name], options: {logon_password: value})
143
154
  end
155
+
156
+ private
157
+
158
+ def normalize_logonaccount
159
+ logon_account = @resource[:logonaccount].sub(/^\.\\/, "#{Puppet::Util::Windows::ADSI.computer_name}\\")
160
+ return 'LocalSystem' if Puppet::Util::Windows::User::localsystem?(logon_account)
161
+
162
+ @logonaccount_information ||= Puppet::Util::Windows::SID.name_to_principal(logon_account)
163
+ return logon_account unless @logonaccount_information
164
+ return ".\\#{@logonaccount_information.account}" if @logonaccount_information.domain == Puppet::Util::Windows::ADSI.computer_name
165
+ @logonaccount_information.domain_account
166
+ end
167
+
168
+ def validate_logon_credentials
169
+ unless Puppet::Util::Windows::User::localsystem?(@normalized_logon_account)
170
+ raise Puppet::Error.new("\"#{@normalized_logon_account}\" is not a valid account") unless @logonaccount_information && [:SidTypeUser, :SidTypeWellKnownGroup].include?(@logonaccount_information.account_type)
171
+
172
+ user_rights = Puppet::Util::Windows::User::get_rights(@logonaccount_information.domain_account) unless Puppet::Util::Windows::User::default_system_account?(@normalized_logon_account)
173
+ raise Puppet::Error.new("\"#{@normalized_logon_account}\" has the 'Log On As A Service' right set to denied.") if user_rights =~ /SeDenyServiceLogonRight/
174
+ raise Puppet::Error.new("\"#{@normalized_logon_account}\" is missing the 'Log On As A Service' right.") unless user_rights.nil? || user_rights =~ /SeServiceLogonRight/
175
+ end
176
+
177
+ is_a_predefined_local_account = Puppet::Util::Windows::User::default_system_account?(@normalized_logon_account) || @normalized_logon_account == 'LocalSystem'
178
+ account_info = @normalized_logon_account.split("\\")
179
+ able_to_logon = Puppet::Util::Windows::User.password_is?(account_info[1], @resource[:logonpassword], account_info[0]) unless is_a_predefined_local_account
180
+ raise Puppet::Error.new("The given password is invalid for user '#{@normalized_logon_account}'.") unless is_a_predefined_local_account || able_to_logon
181
+ end
144
182
  end
@@ -435,7 +435,7 @@ Puppet::Type.type(:user).provide :directoryservice do
435
435
  ['home', 'uid', 'gid', 'comment', 'shell'].each do |setter_method|
436
436
  define_method("#{setter_method}=") do |value|
437
437
  if @property_hash[setter_method.intern]
438
- if self.class.get_os_version.split('.').last.to_i >= 14 && %w(home uid).include?(setter_method)
438
+ if %w(home uid).include?(setter_method)
439
439
  raise Puppet::Error, "OS X version #{self.class.get_os_version} does not allow changing #{setter_method} using puppet"
440
440
  end
441
441
  begin
@@ -536,6 +536,14 @@ Puppet::Type.type(:user).provide :directoryservice do
536
536
  if (shadow_hash_data.class == Hash) && (shadow_hash_data.has_key?('SALTED-SHA512'))
537
537
  shadow_hash_data.delete('SALTED-SHA512')
538
538
  end
539
+
540
+ # Starting with macOS 11 Big Sur, the AuthenticationAuthority field
541
+ # could be missing entirely and without it the managed user cannot log in
542
+ if needs_sha512_pbkdf2_authentication_authority_to_be_added?(users_plist)
543
+ Puppet.debug("Adding 'SALTED-SHA512-PBKDF2' AuthenticationAuthority key for ShadowHash to user '#{@resource.name}'")
544
+ merge_attribute_with_dscl('Users', @resource.name, 'AuthenticationAuthority', ERB::Util.html_escape(SHA512_PBKDF2_AUTHENTICATION_AUTHORITY))
545
+ end
546
+
539
547
  set_salted_pbkdf2(users_plist, shadow_hash_data, 'entropy', value)
540
548
  end
541
549
  end
@@ -562,6 +570,17 @@ Puppet::Type.type(:user).provide :directoryservice do
562
570
  end
563
571
  end
564
572
 
573
+ # This method will check if authentication_authority key of a user's plist
574
+ # needs SALTED_SHA512_PBKDF2 to be added. This is a valid case for macOS 11 (Big Sur)
575
+ # where users created with `dscl` started to have this field missing
576
+ def needs_sha512_pbkdf2_authentication_authority_to_be_added?(users_plist)
577
+ authority = users_plist['authentication_authority']
578
+ return false if Puppet::Util::Package.versioncmp(self.class.get_os_version, '11.0.0') < 0 && authority && authority.include?(SHA512_PBKDF2_AUTHENTICATION_AUTHORITY)
579
+
580
+ Puppet.debug("User '#{@resource.name}' is missing the 'SALTED-SHA512-PBKDF2' AuthenticationAuthority key for ShadowHash")
581
+ true
582
+ end
583
+
565
584
  # This method will embed the binary plist data comprising the user's
566
585
  # password hash (and Salt/Iterations value if the OS is 10.8 or greater)
567
586
  # into the ShadowHashData key of the user's plist.
@@ -572,11 +591,7 @@ Puppet::Type.type(:user).provide :directoryservice do
572
591
  else
573
592
  users_plist['ShadowHashData'] = [binary_plist]
574
593
  end
575
- if Puppet::Util::Package.versioncmp(self.class.get_os_version, '10.15') < 0
576
- write_users_plist_to_disk(users_plist)
577
- else
578
- write_and_import_shadow_hash_data(users_plist['ShadowHashData'].first)
579
- end
594
+ write_and_import_shadow_hash_data(users_plist['ShadowHashData'].first)
580
595
  end
581
596
 
582
597
  # This method writes the ShadowHashData plist in a temporary file,
@@ -652,12 +667,6 @@ Puppet::Type.type(:user).provide :directoryservice do
652
667
  set_shadow_hash_data(users_plist, binary_plist)
653
668
  end
654
669
 
655
- # This method will accept a plist in XML format, save it to disk, convert
656
- # the plist to a binary format, and flush the dscl cache.
657
- def write_users_plist_to_disk(users_plist)
658
- Puppet::Util::Plist.write_plist_file(users_plist, "#{users_plist_dir}/#{@resource.name}.plist", :binary)
659
- end
660
-
661
670
  # This is a simple wrapper method for writing values to a file.
662
671
  def write_to_file(filename, value)
663
672
  Puppet.deprecation_warning("Puppet::Type.type(:user).provider(:directoryservice).write_to_file is deprecated and will be removed in Puppet 5.")
@@ -667,4 +676,8 @@ Puppet::Type.type(:user).provide :directoryservice do
667
676
  raise Puppet::Error, "Could not write to file #{filename}: #{detail}", detail.backtrace
668
677
  end
669
678
  end
679
+
680
+ private
681
+
682
+ SHA512_PBKDF2_AUTHENTICATION_AUTHORITY = ';ShadowHash;HASHLIST:<SALTED-SHA512-PBKDF2,SRP-RFC5054-4096-SHA512-PBKDF2>'
670
683
  end
@@ -41,7 +41,7 @@ config = Puppet::Util::Reference.newreference(:configuration, :depth => 1, :doc
41
41
  # Leave out the section information; it was apparently confusing people.
42
42
  #str << "- **Section**: #{object.section}\n"
43
43
  unless val == ""
44
- str << "- *Default*: #{val}\n"
44
+ str << "- *Default*: `#{val}`\n"
45
45
  end
46
46
  str << "\n"
47
47
  end
@@ -137,7 +137,7 @@ class Puppet::Transaction::AdditionalResourceGenerator
137
137
  else
138
138
  @catalog.add_resource_after(parent_resource, res)
139
139
  end
140
- @catalog.add_edge(@catalog.container_of(parent_resource), res)
140
+ @catalog.add_edge(@catalog.container_of(parent_resource), res) if @catalog.container_of(parent_resource)
141
141
  if @relationship_graph && priority
142
142
  # If we have a relationship_graph we should add the resource
143
143
  # to it (this is an eval_generate). If we don't, then the
@@ -42,7 +42,7 @@ module Puppet
42
42
  return nil
43
43
  end
44
44
 
45
- context = self.get_selinux_default_context(@resource[:path])
45
+ context = self.get_selinux_default_context(@resource[:path], @resource[:ensure])
46
46
  unless context
47
47
  return nil
48
48
  end
@@ -220,6 +220,23 @@ Puppet::Type.newtype(:file) do
220
220
  end
221
221
  end
222
222
 
223
+ newparam(:max_files) do
224
+ desc "In case the resource is a directory and the recursion is enabled, puppet will
225
+ generate a new resource for each file file found, possible leading to
226
+ an excessive number of resources generated without any control.
227
+
228
+ Setting `max_files` will check the number of file resources that
229
+ will eventually be created and will raise a resource argument error if the
230
+ limit will be exceeded.
231
+
232
+ Use value `0` to log a warning instead of raising an error.
233
+
234
+ Use value `-1` to disable errors and warnings due to max files."
235
+
236
+ defaultto 0
237
+ newvalues(/^[0-9]+$/, /^-1$/)
238
+ end
239
+
223
240
  newparam(:replace, :boolean => true, :parent => Puppet::Parameter::Boolean) do
224
241
  desc "Whether to replace a file or symlink that already exists on the local system but
225
242
  whose content doesn't match what the `source` or `content` attribute
@@ -576,7 +593,7 @@ Puppet::Type.newtype(:file) do
576
593
  options = @original_parameters.merge(:path => full_path).reject { |param, value| value.nil? }
577
594
 
578
595
  # These should never be passed to our children.
579
- [:parent, :ensure, :recurse, :recurselimit, :target, :alias, :source].each do |param|
596
+ [:parent, :ensure, :recurse, :recurselimit, :max_files, :target, :alias, :source].each do |param|
580
597
  options.delete(param) if options.include?(param)
581
598
  end
582
599
 
@@ -753,6 +770,7 @@ Puppet::Type.newtype(:file) do
753
770
  :links => self[:links],
754
771
  :recurse => (self[:recurse] == :remote ? true : self[:recurse]),
755
772
  :recurselimit => self[:recurselimit],
773
+ :max_files => self[:max_files],
756
774
  :source_permissions => self[:source_permissions],
757
775
  :ignore => self[:ignore],
758
776
  :checksum_type => (self[:source] || self[:content]) ? self[:checksum] : :none,
@@ -38,6 +38,12 @@ module Puppet
38
38
  feature :enableable, "The provider can enable and disable the service.",
39
39
  :methods => [:disable, :enable, :enabled?]
40
40
 
41
+ feature :delayed_startable, "The provider can set service to delayed start",
42
+ :methods => [:delayed_start]
43
+
44
+ feature :manual_startable, "The provider can set service to manual start",
45
+ :methods => [:manual_start]
46
+
41
47
  feature :controllable, "The provider uses a control variable."
42
48
 
43
49
  feature :flaggable, "The provider can pass flags to the service."
@@ -67,7 +73,7 @@ module Puppet
67
73
  provider.disable
68
74
  end
69
75
 
70
- newvalue(:manual, :event => :service_manual_start) do
76
+ newvalue(:manual, :event => :service_manual_start, :required_features => :manual_startable) do
71
77
  provider.manual_start
72
78
  end
73
79
 
@@ -81,8 +87,7 @@ module Puppet
81
87
  provider.enabled?
82
88
  end
83
89
 
84
- # This only works on Windows systems.
85
- newvalue(:delayed, :event => :service_delayed_start) do
90
+ newvalue(:delayed, :event => :service_delayed_start, :required_features => :delayed_startable) do
86
91
  provider.delayed_start
87
92
  end
88
93
 
@@ -90,12 +95,6 @@ module Puppet
90
95
  return provider.enabled_insync?(current) if provider.respond_to?(:enabled_insync?)
91
96
  super(current)
92
97
  end
93
-
94
- validate do |value|
95
- if (value == :manual || value == :delayed) && !Puppet::Util::Platform.windows?
96
- raise Puppet::Error.new(_("Setting enable to %{value} is only supported on Microsoft Windows.") % { value: value.to_s} )
97
- end
98
- end
99
98
  end
100
99
 
101
100
  # Handle whether the service should actually be running right now.
@@ -139,23 +138,9 @@ module Puppet
139
138
  newproperty(:logonaccount, :required_features => :manages_logon_credentials) do
140
139
  desc "Specify an account for service logon"
141
140
 
142
- munge do |value|
143
- return value unless Puppet::Util::Platform.windows?
144
- return 'LocalSystem' if Puppet::Util::Windows::User::localsystem?(value)
145
-
146
- value.sub!(/^\.\\/, "#{Puppet::Util::Windows::ADSI.computer_name}\\")
147
- user_information = Puppet::Util::Windows::SID.name_to_principal(value)
148
- raise Puppet::Error.new("\"#{value}\" is not a valid account") unless user_information && [:SidTypeUser, :SidTypeWellKnownGroup].include?(user_information.account_type)
149
-
150
- user_rights = Puppet::Util::Windows::User::get_rights(user_information.domain_account) unless Puppet::Util::Windows::User::default_system_account?(value)
151
- raise Puppet::Error.new("\"#{user_information.domain_account}\" has the 'Log On As A Service' right set to denied.") if user_rights =~ /SeDenyServiceLogonRight/
152
- raise Puppet::Error.new("\"#{user_information.domain_account}\" is missing the 'Log On As A Service' right.") unless user_rights.nil? || user_rights =~ /SeServiceLogonRight/
153
-
154
- if user_information.domain == Puppet::Util::Windows::ADSI.computer_name
155
- ".\\#{user_information.account}"
156
- else
157
- user_information.domain_account
158
- end
141
+ def insync?(current)
142
+ return provider.logonaccount_insync?(current) if provider.respond_to?(:logonaccount_insync?)
143
+ super(current)
159
144
  end
160
145
  end
161
146
 
@@ -163,18 +148,7 @@ module Puppet
163
148
  desc "Specify a password for service logon. Default value is an empty string (when logonaccount is specified)."
164
149
 
165
150
  validate do |value|
166
- raise Puppet::Error.new(_"The 'logonaccount' parameter is mandatory when setting 'logonpassword'.") unless @resource[:logonaccount]
167
- raise ArgumentError, _("Passwords cannot include ':'") if value.is_a?(String) and value.include?(":")
168
- return unless Puppet::Util::Platform.windows?
169
-
170
- is_a_predefined_local_account = Puppet::Util::Windows::User::default_system_account?(@resource[:logonaccount]) || @resource[:logonaccount] == 'LocalSystem'
171
-
172
- account_info = @resource[:logonaccount].split("\\")
173
- able_to_logon = Puppet::Util::Windows::User.password_is?(account_info[1], value, account_info[0]) unless is_a_predefined_local_account
174
-
175
- raise Puppet::Error.new("The given password is invalid for user '#{@resource[:logonaccount]}'.") unless is_a_predefined_local_account || able_to_logon
176
-
177
- provider.logonpassword=(value)
151
+ raise ArgumentError, _("Passwords cannot include ':'") if value.is_a?(String) && value.include?(":")
178
152
  end
179
153
 
180
154
  sensitive true
@@ -320,5 +294,11 @@ module Puppet
320
294
  def self.needs_ensure_retrieved
321
295
  false
322
296
  end
297
+
298
+ validate do
299
+ if @parameters[:logonpassword] && @parameters[:logonaccount].nil?
300
+ raise Puppet::Error.new(_"The 'logonaccount' parameter is mandatory when setting 'logonpassword'.")
301
+ end
302
+ end
323
303
  end
324
304
  end
@@ -50,6 +50,22 @@ Puppet::Type.newtype(:tidy) do
50
50
  end
51
51
  end
52
52
 
53
+ newparam(:max_files) do
54
+ desc "In case the resource is a directory and the recursion is enabled, puppet will
55
+ generate a new resource for each file file found, possible leading to
56
+ an excessive number of resources generated without any control.
57
+
58
+ Setting `max_files` will check the number of file resources that
59
+ will eventually be created and will raise a resource argument error if the
60
+ limit will be exceeded.
61
+
62
+ Use value `0` to disable the check. In this case, a warning is logged if
63
+ the number of files exceeds 1000."
64
+
65
+ defaultto 0
66
+ newvalues(/^[0-9]+$/)
67
+ end
68
+
53
69
  newparam(:matches) do
54
70
  desc <<-'EOT'
55
71
  One or more (shell type) file glob patterns, which restrict
@@ -256,9 +272,12 @@ Puppet::Type.newtype(:tidy) do
256
272
 
257
273
  case self[:recurse]
258
274
  when Integer, /^\d+$/
259
- parameter = { :recurse => true, :recurselimit => self[:recurse] }
275
+ parameter = { :max_files => self[:max_files],
276
+ :recurse => true,
277
+ :recurselimit => self[:recurse] }
260
278
  when true, :true, :inf
261
- parameter = { :recurse => true }
279
+ parameter = { :max_files => self[:max_files],
280
+ :recurse => true }
262
281
  end
263
282
 
264
283
  if parameter
@@ -67,6 +67,7 @@ module Puppet
67
67
  newproperty(:ensure, :parent => Puppet::Property::Ensure) do
68
68
  newvalue(:present, :event => :user_created) do
69
69
  provider.create
70
+ @resource.generate
70
71
  end
71
72
 
72
73
  newvalue(:absent, :event => :user_removed) do
@@ -695,6 +696,7 @@ module Puppet
695
696
 
696
697
  def generate
697
698
  if !self[:purge_ssh_keys].empty?
699
+ return [] if self[:ensure] == :present && !provider.exists?
698
700
  if Puppet::Type.type(:ssh_authorized_key).nil?
699
701
  warning _("Ssh_authorized_key type is not available. Cannot purge SSH keys.")
700
702
  else
@@ -743,25 +745,6 @@ module Puppet
743
745
  end
744
746
  raise ArgumentError, _("purge_ssh_keys must be true, false, or an array of file names, not %{value}") % { value: value.inspect }
745
747
  end
746
-
747
- munge do |value|
748
- # Resolve string, boolean and symbol forms of true and false to a
749
- # single representation.
750
- test_sym = value.to_s.intern
751
- value = test_sym if [:true, :false].include? test_sym
752
-
753
- return [] if value == :false
754
- home = resource[:home] || Dir.home(resource[:name])
755
-
756
- return [ "#{home}/.ssh/authorized_keys" ] if value == :true
757
- # value is an array - munge each value
758
- [ value ].flatten.map do |entry|
759
- # make sure frozen value is duplicated by using a gsub, second mutating gsub! is then ok
760
- entry = entry.gsub(/^~\//, "#{home}/")
761
- entry.gsub!(/^%h\//, "#{home}/")
762
- entry
763
- end
764
- end
765
748
  end
766
749
 
767
750
  newproperty(:loginclass, :required_features => :manages_loginclass) do
@@ -783,7 +766,7 @@ module Puppet
783
766
  # @see generate
784
767
  # @api private
785
768
  def find_unmanaged_keys
786
- self[:purge_ssh_keys].
769
+ munged_unmanaged_keys.
787
770
  select { |f| File.readable?(f) }.
788
771
  map { |f| unknown_keys_in_file(f) }.
789
772
  flatten.each do |res|
@@ -795,6 +778,41 @@ module Puppet
795
778
  end
796
779
  end
797
780
 
781
+ def munged_unmanaged_keys
782
+ value = self[:purge_ssh_keys]
783
+
784
+ # Resolve string, boolean and symbol forms of true and false to a
785
+ # single representation.
786
+ test_sym = value.to_s.intern
787
+ value = test_sym if [:true, :false].include? test_sym
788
+
789
+ return [] if value == :false
790
+
791
+ home = self[:home]
792
+ begin
793
+ home ||= provider.home
794
+ rescue
795
+ Puppet.debug("User '#{self[:name]}' does not exist")
796
+ end
797
+
798
+ if home.to_s.empty? || !Dir.exist?(home.to_s)
799
+ if value == :true || [ value ].flatten.any? { |v| v.start_with?('~/', '%h/') }
800
+ Puppet.debug("User '#{self[:name]}' has no home directory set to purge ssh keys from.")
801
+ return []
802
+ end
803
+ end
804
+
805
+ return [ "#{home}/.ssh/authorized_keys" ] if value == :true
806
+
807
+ # value is an array - munge each value
808
+ [ value ].flatten.map do |entry|
809
+ # make sure frozen value is duplicated by using a gsub, second mutating gsub! is then ok
810
+ entry = entry.gsub(/^~\//, "#{home}/")
811
+ entry.gsub!(/^%h\//, "#{home}/")
812
+ entry
813
+ end
814
+ end
815
+
798
816
  # Parse an ssh authorized keys file superficially, extract the comments
799
817
  # on the keys. These are considered names of possible ssh_authorized_keys
800
818
  # resources. Keys that are managed by the present catalog are ignored.