puppet 2.7.9 → 2.7.11

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 (187) hide show
  1. data/CHANGELOG +413 -0
  2. data/README_DEVELOPER.md +28 -0
  3. data/conf/redhat/puppet.spec +10 -1
  4. data/conf/solaris/pkginfo +1 -1
  5. data/conf/suse/puppet.spec +7 -4
  6. data/ext/envpuppet.bat +13 -0
  7. data/ext/rack/files/apache2.conf +4 -0
  8. data/install.rb +4 -8
  9. data/lib/puppet.rb +1 -1
  10. data/lib/puppet/agent.rb +7 -0
  11. data/lib/puppet/agent/disabler.rb +27 -0
  12. data/lib/puppet/agent/locker.rb +0 -10
  13. data/lib/puppet/application.rb +3 -0
  14. data/lib/puppet/application/agent.rb +13 -3
  15. data/lib/puppet/application/apply.rb +6 -6
  16. data/lib/puppet/application/cert.rb +5 -5
  17. data/lib/puppet/application/instrumentation_data.rb +4 -0
  18. data/lib/puppet/application/instrumentation_listener.rb +4 -0
  19. data/lib/puppet/application/instrumentation_probe.rb +4 -0
  20. data/lib/puppet/configurer.rb +3 -1
  21. data/lib/puppet/configurer/downloader.rb +4 -2
  22. data/lib/puppet/configurer/fact_handler.rb +0 -21
  23. data/lib/puppet/daemon.rb +3 -4
  24. data/lib/puppet/defaults.rb +2 -2
  25. data/lib/puppet/face/instrumentation_data.rb +28 -0
  26. data/lib/puppet/face/instrumentation_listener.rb +96 -0
  27. data/lib/puppet/face/instrumentation_probe.rb +77 -0
  28. data/lib/puppet/face/module/list.rb +64 -0
  29. data/lib/puppet/face/module/uninstall.rb +50 -0
  30. data/lib/puppet/face/node/clean.rb +1 -4
  31. data/lib/puppet/feature/base.rb +1 -0
  32. data/lib/puppet/file_serving/content.rb +1 -1
  33. data/lib/puppet/indirector/facts/facter.rb +20 -7
  34. data/lib/puppet/indirector/facts/inventory_active_record.rb +14 -11
  35. data/lib/puppet/indirector/indirection.rb +7 -0
  36. data/lib/puppet/indirector/instrumentation_data.rb +3 -0
  37. data/lib/puppet/indirector/instrumentation_data/local.rb +19 -0
  38. data/lib/puppet/indirector/instrumentation_data/rest.rb +5 -0
  39. data/lib/puppet/indirector/instrumentation_listener.rb +3 -0
  40. data/lib/puppet/indirector/instrumentation_listener/local.rb +23 -0
  41. data/lib/puppet/indirector/instrumentation_listener/rest.rb +5 -0
  42. data/lib/puppet/indirector/instrumentation_probe.rb +3 -0
  43. data/lib/puppet/indirector/instrumentation_probe/local.rb +24 -0
  44. data/lib/puppet/indirector/instrumentation_probe/rest.rb +5 -0
  45. data/lib/puppet/indirector/rest.rb +1 -1
  46. data/lib/puppet/module.rb +13 -17
  47. data/lib/puppet/module_tool/applications.rb +1 -0
  48. data/lib/puppet/module_tool/applications/uninstaller.rb +33 -0
  49. data/lib/puppet/module_tool/contents_description.rb +1 -1
  50. data/lib/puppet/network/server.rb +2 -3
  51. data/lib/puppet/node/environment.rb +16 -3
  52. data/lib/puppet/parser/ast/leaf.rb +1 -1
  53. data/lib/puppet/parser/functions/create_resources.rb +1 -1
  54. data/lib/puppet/parser/type_loader.rb +1 -1
  55. data/lib/puppet/property.rb +46 -14
  56. data/lib/puppet/provider.rb +13 -4
  57. data/lib/puppet/provider/augeas/augeas.rb +6 -4
  58. data/lib/puppet/provider/group/pw.rb +24 -10
  59. data/lib/puppet/provider/nameservice/directoryservice.rb +146 -37
  60. data/lib/puppet/provider/package/pip.rb +1 -1
  61. data/lib/puppet/provider/package/yum.rb +1 -2
  62. data/lib/puppet/provider/service/debian.rb +14 -0
  63. data/lib/puppet/provider/service/launchd.rb +1 -1
  64. data/lib/puppet/provider/service/smf.rb +2 -2
  65. data/lib/puppet/provider/user/pw.rb +56 -2
  66. data/lib/puppet/provider/user/user_role_add.rb +32 -22
  67. data/lib/puppet/provider/user/windows_adsi.rb +1 -0
  68. data/lib/puppet/rails/benchmark.rb +1 -1
  69. data/lib/puppet/reports/store.rb +8 -1
  70. data/lib/puppet/resource/catalog.rb +5 -1
  71. data/lib/puppet/simple_graph.rb +11 -14
  72. data/lib/puppet/transaction.rb +10 -4
  73. data/lib/puppet/transaction/report.rb +9 -3
  74. data/lib/puppet/type.rb +19 -7
  75. data/lib/puppet/type/exec.rb +1 -1
  76. data/lib/puppet/type/file.rb +4 -1
  77. data/lib/puppet/type/file/ensure.rb +5 -1
  78. data/lib/puppet/type/file/mode.rb +45 -10
  79. data/lib/puppet/type/file/source.rb +4 -0
  80. data/lib/puppet/type/host.rb +17 -3
  81. data/lib/puppet/type/k5login.rb +3 -2
  82. data/lib/puppet/type/schedule.rb +3 -2
  83. data/lib/puppet/util.rb +83 -27
  84. data/lib/puppet/util/anonymous_filelock.rb +36 -0
  85. data/lib/puppet/util/docs.rb +18 -2
  86. data/lib/puppet/util/instrumentation.rb +173 -0
  87. data/lib/puppet/util/instrumentation/data.rb +34 -0
  88. data/lib/puppet/util/instrumentation/indirection_probe.rb +29 -0
  89. data/lib/puppet/util/instrumentation/instrumentable.rb +143 -0
  90. data/lib/puppet/util/instrumentation/listener.rb +60 -0
  91. data/lib/puppet/util/instrumentation/listeners/log.rb +29 -0
  92. data/lib/puppet/util/instrumentation/listeners/performance.rb +30 -0
  93. data/lib/puppet/util/monkey_patches.rb +8 -0
  94. data/lib/puppet/util/pidlock.rb +21 -25
  95. data/lib/puppet/util/rdoc/parser.rb +2 -2
  96. data/lib/puppet/util/reference.rb +8 -23
  97. data/lib/puppet/util/retryaction.rb +48 -0
  98. data/lib/puppet/util/suidmanager.rb +70 -39
  99. data/lib/puppet/util/symbolic_file_mode.rb +140 -0
  100. data/spec/integration/configurer_spec.rb +5 -0
  101. data/spec/integration/indirector/direct_file_server_spec.rb +1 -1
  102. data/spec/integration/indirector/file_content/file_server_spec.rb +7 -7
  103. data/spec/integration/provider/package_spec.rb +7 -0
  104. data/spec/unit/agent/disabler_spec.rb +60 -0
  105. data/spec/unit/agent/locker_spec.rb +0 -12
  106. data/spec/unit/agent_spec.rb +8 -0
  107. data/spec/unit/application/agent_spec.rb +38 -1
  108. data/spec/unit/application/apply_spec.rb +34 -40
  109. data/spec/unit/application/cert_spec.rb +1 -1
  110. data/spec/unit/application_spec.rb +6 -0
  111. data/spec/unit/configurer/downloader_spec.rb +29 -10
  112. data/spec/unit/configurer/fact_handler_spec.rb +5 -29
  113. data/spec/unit/configurer_spec.rb +8 -8
  114. data/spec/unit/daemon_spec.rb +12 -26
  115. data/spec/unit/face/instrumentation_data.rb +7 -0
  116. data/spec/unit/face/instrumentation_listener.rb +38 -0
  117. data/spec/unit/face/instrumentation_probe.rb +21 -0
  118. data/spec/unit/face/node_spec.rb +111 -111
  119. data/spec/unit/file_serving/content_spec.rb +2 -2
  120. data/spec/unit/indirector/facts/facter_spec.rb +25 -3
  121. data/spec/unit/indirector/facts/inventory_active_record_spec.rb +14 -4
  122. data/spec/unit/indirector/instrumentation_data/local_spec.rb +52 -0
  123. data/spec/unit/indirector/instrumentation_data/rest_spec.rb +11 -0
  124. data/spec/unit/indirector/instrumentation_listener/local_spec.rb +65 -0
  125. data/spec/unit/indirector/instrumentation_listener/rest_spec.rb +11 -0
  126. data/spec/unit/indirector/instrumentation_probe/local_spec.rb +65 -0
  127. data/spec/unit/indirector/instrumentation_probe/rest_spec.rb +11 -0
  128. data/spec/unit/module_spec.rb +39 -125
  129. data/spec/unit/module_tool/uninstaller_spec.rb +44 -0
  130. data/spec/unit/network/server_spec.rb +2 -20
  131. data/spec/unit/node/environment_spec.rb +76 -58
  132. data/spec/unit/parser/ast/asthash_spec.rb +1 -2
  133. data/spec/unit/parser/ast/leaf_spec.rb +16 -0
  134. data/spec/unit/property/keyvalue_spec.rb +5 -2
  135. data/spec/unit/property_spec.rb +260 -159
  136. data/spec/unit/provider/augeas/augeas_spec.rb +2 -2
  137. data/spec/unit/provider/group/pw_spec.rb +81 -0
  138. data/spec/unit/provider/nameservice/directoryservice_spec.rb +102 -0
  139. data/spec/unit/provider/package/pip_spec.rb +7 -0
  140. data/spec/unit/provider/package/yum_spec.rb +45 -1
  141. data/spec/unit/provider/service/debian_spec.rb +15 -0
  142. data/spec/unit/provider/service/launchd_spec.rb +48 -43
  143. data/spec/unit/provider/service/smf_spec.rb +3 -3
  144. data/spec/unit/provider/user/pw_spec.rb +183 -0
  145. data/spec/unit/provider/user/user_role_add_spec.rb +46 -39
  146. data/spec/unit/provider/user/windows_adsi_spec.rb +1 -0
  147. data/spec/unit/provider_spec.rb +32 -0
  148. data/spec/unit/reports/store_spec.rb +19 -1
  149. data/spec/unit/simple_graph_spec.rb +34 -19
  150. data/spec/unit/ssl/certificate_factory_spec.rb +3 -3
  151. data/spec/unit/transaction/report_spec.rb +29 -1
  152. data/spec/unit/transaction_spec.rb +32 -46
  153. data/spec/unit/type/file/mode_spec.rb +1 -1
  154. data/spec/unit/type/file/source_spec.rb +28 -3
  155. data/spec/unit/type/file_spec.rb +17 -16
  156. data/spec/unit/type/host_spec.rb +527 -0
  157. data/spec/unit/type/k5login_spec.rb +115 -0
  158. data/spec/unit/type/schedule_spec.rb +6 -6
  159. data/spec/unit/type_spec.rb +51 -0
  160. data/spec/unit/util/anonymous_filelock_spec.rb +78 -0
  161. data/spec/unit/util/execution_stub_spec.rb +2 -1
  162. data/spec/unit/util/instrumentation/data_spec.rb +44 -0
  163. data/spec/unit/util/instrumentation/indirection_probe_spec.rb +19 -0
  164. data/spec/unit/util/instrumentation/instrumentable_spec.rb +186 -0
  165. data/spec/unit/util/instrumentation/listener_spec.rb +100 -0
  166. data/spec/unit/util/instrumentation/listeners/log_spec.rb +34 -0
  167. data/spec/unit/util/instrumentation/listeners/performance_spec.rb +36 -0
  168. data/spec/unit/util/instrumentation_spec.rb +181 -0
  169. data/spec/unit/util/pidlock_spec.rb +208 -0
  170. data/spec/unit/util/rdoc/parser_spec.rb +1 -1
  171. data/spec/unit/util/reference_spec.rb +16 -6
  172. data/spec/unit/util/retryaction_spec.rb +62 -0
  173. data/spec/unit/util/suidmanager_spec.rb +101 -83
  174. data/spec/unit/util/symbolic_file_mode_spec.rb +182 -0
  175. data/spec/unit/util_spec.rb +126 -0
  176. data/tasks/rake/apple.rake +176 -0
  177. data/tasks/rake/templates/prototype.plist.erb +38 -0
  178. metadata +61 -13
  179. data/lib/puppet/application/module.rb +0 -3
  180. data/lib/puppet/face/module.rb +0 -12
  181. data/spec/unit/face/module/build_spec.rb +0 -30
  182. data/spec/unit/face/module/changes_spec.rb +0 -30
  183. data/spec/unit/face/module/clean_spec.rb +0 -30
  184. data/spec/unit/face/module/generate_spec.rb +0 -30
  185. data/spec/unit/face/module/install_spec.rb +0 -75
  186. data/spec/unit/face/module/search_spec.rb +0 -40
  187. data/test/util/pidlock.rb +0 -126
@@ -17,10 +17,12 @@ require 'augeas' if Puppet.features.augeas?
17
17
  require 'strscan'
18
18
  require 'puppet/util'
19
19
  require 'puppet/util/diff'
20
+ require 'puppet/util/package'
20
21
 
21
22
  Puppet::Type.type(:augeas).provide(:augeas) do
22
23
  include Puppet::Util
23
24
  include Puppet::Util::Diff
25
+ include Puppet::Util::Package
24
26
 
25
27
  confine :true => Puppet.features.augeas?
26
28
 
@@ -149,7 +151,7 @@ Puppet::Type.type(:augeas).provide(:augeas) do
149
151
  debug("Opening augeas with root #{root}, lens path #{load_path}, flags #{flags}")
150
152
  @aug = Augeas::open(root, load_path,flags)
151
153
 
152
- debug("Augeas version #{get_augeas_version} is installed") if get_augeas_version >= "0.3.6"
154
+ debug("Augeas version #{get_augeas_version} is installed") if versioncmp(get_augeas_version, "0.3.6") >= 0
153
155
 
154
156
  if resource[:incl]
155
157
  aug.set("/augeas/load/Xfm/lens", resource[:lens])
@@ -285,7 +287,7 @@ Puppet::Type.type(:augeas).provide(:augeas) do
285
287
  # If we have a verison of augeas which is at least 0.3.6 then we
286
288
  # can make the changes now, see if changes were made, and
287
289
  # actually do the save.
288
- if return_value and get_augeas_version >= "0.3.6"
290
+ if return_value and versioncmp(get_augeas_version, "0.3.6") >= 0
289
291
  debug("Will attempt to save and only run if files changed")
290
292
  set_augeas_save_mode(SAVE_NEWFILE)
291
293
  do_execute_changes
@@ -325,7 +327,7 @@ Puppet::Type.type(:augeas).provide(:augeas) do
325
327
  begin
326
328
  open_augeas
327
329
  saved_files = @aug.match("/augeas/events/saved")
328
- if saved_files
330
+ unless saved_files.empty?
329
331
  saved_files.each do |key|
330
332
  root = resource[:root].sub(/^\/$/, "")
331
333
  saved_file = @aug.get(key).to_s.sub(/^\/files/, root)
@@ -337,7 +339,7 @@ Puppet::Type.type(:augeas).provide(:augeas) do
337
339
  end
338
340
  else
339
341
  debug("No saved files, re-executing augeas")
340
- set_augeas_save_mode(SAVE_OVERWRITE) if get_augeas_version >= "0.3.6"
342
+ set_augeas_save_mode(SAVE_OVERWRITE) if versioncmp(get_augeas_version, "0.3.6") >= 0
341
343
  do_execute_changes
342
344
  success = @aug.save
343
345
  fail("Save failed with return code #{success}") if success != true
@@ -1,34 +1,48 @@
1
1
  require 'puppet/provider/nameservice/pw'
2
2
 
3
3
  Puppet::Type.type(:group).provide :pw, :parent => Puppet::Provider::NameService::PW do
4
- desc "Group management via `pw`.
4
+ desc "Group management via `pw` on FreeBSD."
5
5
 
6
- Only works on FreeBSD.
6
+ commands :pw => "pw"
7
+ has_features :manages_members
7
8
 
8
- "
9
-
10
- commands :pw => "/usr/sbin/pw"
11
9
  defaultfor :operatingsystem => :freebsd
12
10
 
11
+ options :members, :flag => "-M", :method => :mem
12
+
13
13
  verify :gid, "GID must be an integer" do |value|
14
14
  value.is_a? Integer
15
15
  end
16
16
 
17
17
  def addcmd
18
18
  cmd = [command(:pw), "groupadd", @resource[:name]]
19
+
19
20
  if gid = @resource.should(:gid)
20
21
  unless gid == :absent
21
22
  cmd << flag(:gid) << gid
22
23
  end
23
24
  end
24
25
 
25
- # Apparently, contrary to the man page, groupadd does
26
- # not accept -o.
27
- #if @parent[:allowdupe] == :true
28
- # cmd << "-o"
29
- #end
26
+ if members = @resource.should(:members)
27
+ unless members == :absent
28
+ if members.is_a?(Array)
29
+ members = members.join(",")
30
+ end
31
+ cmd << "-M" << members
32
+ end
33
+ end
34
+
35
+ cmd << "-o" if @resource.allowdupe?
30
36
 
31
37
  cmd
32
38
  end
39
+
40
+ def modifycmd(param, value)
41
+ # members may be an array, need a comma separated list
42
+ if param == :members and value.is_a?(Array)
43
+ value = value.join(",")
44
+ end
45
+ super(param, value)
46
+ end
33
47
  end
34
48
 
@@ -2,7 +2,7 @@ require 'puppet'
2
2
  require 'puppet/provider/nameservice'
3
3
  require 'facter/util/plist'
4
4
  require 'cgi'
5
-
5
+ require 'fileutils'
6
6
 
7
7
  class Puppet::Provider::NameService
8
8
  class DirectoryService < Puppet::Provider::NameService
@@ -21,6 +21,7 @@ class DirectoryService < Puppet::Provider::NameService
21
21
  commands :dscl => "/usr/bin/dscl"
22
22
  commands :dseditgroup => "/usr/sbin/dseditgroup"
23
23
  commands :sw_vers => "/usr/bin/sw_vers"
24
+ commands :plutil => '/usr/bin/plutil'
24
25
  confine :operatingsystem => :darwin
25
26
  defaultfor :operatingsystem => :darwin
26
27
 
@@ -60,6 +61,8 @@ class DirectoryService < Puppet::Provider::NameService
60
61
  }
61
62
 
62
63
  @@password_hash_dir = "/var/db/shadow/hash"
64
+ @@users_plist_dir = '/var/db/dslocal/nodes/Default/users'
65
+
63
66
 
64
67
  def self.instances
65
68
  # JJM Class method that provides an array of instance objects of this
@@ -193,7 +196,7 @@ class DirectoryService < Puppet::Provider::NameService
193
196
  # stored in the user record. It is stored at a path that involves the
194
197
  # UUID of the user record for non-Mobile local acccounts.
195
198
  # Mobile Accounts are out of scope for this provider for now
196
- attribute_hash[:password] = self.get_password(attribute_hash[:guid]) if @resource_type.validproperties.include?(:password) and Puppet.features.root?
199
+ attribute_hash[:password] = self.get_password(attribute_hash[:guid], attribute_hash[:name]) if @resource_type.validproperties.include?(:password) and Puppet.features.root?
197
200
  attribute_hash
198
201
  end
199
202
 
@@ -268,46 +271,144 @@ class DirectoryService < Puppet::Provider::NameService
268
271
  end
269
272
 
270
273
  def self.set_password(resource_name, guid, password_hash)
271
- password_hash_file = "#{@@password_hash_dir}/#{guid}"
272
- begin
273
- File.open(password_hash_file, 'w') { |f| f.write(password_hash)}
274
- rescue Errno::EACCES => detail
275
- fail("Could not write to password hash file: #{detail}")
274
+ # Use Puppet::Util::Package.versioncmp() to catch the scenario where a
275
+ # version '10.10' would be < '10.7' with simple string comparison. This
276
+ # if-statement only executes if the current version is less-than 10.7
277
+ if (Puppet::Util::Package.versioncmp(get_macosx_version_major, '10.7') == -1)
278
+ password_hash_file = "#{@@password_hash_dir}/#{guid}"
279
+ begin
280
+ File.open(password_hash_file, 'w') { |f| f.write(password_hash)}
281
+ rescue Errno::EACCES => detail
282
+ fail("Could not write to password hash file: #{detail}")
283
+ end
284
+
285
+ # NBK: For shadow hashes, the user AuthenticationAuthority must contain a value of
286
+ # ";ShadowHash;". The LKDC in 10.5 makes this more interesting though as it
287
+ # will dynamically generate ;Kerberosv5;;username@LKDC:SHA1 attributes if
288
+ # missing. Thus we make sure we only set ;ShadowHash; if it is missing, and
289
+ # we can do this with the merge command. This allows people to continue to
290
+ # use other custom AuthenticationAuthority attributes without stomping on them.
291
+ #
292
+ # There is a potential problem here in that we're only doing this when setting
293
+ # the password, and the attribute could get modified at other times while the
294
+ # hash doesn't change and so this doesn't get called at all... but
295
+ # without switching all the other attributes to merge instead of create I can't
296
+ # see a simple enough solution for this that doesn't modify the user record
297
+ # every single time. This should be a rather rare edge case. (famous last words)
298
+
299
+ dscl_vector = self.get_exec_preamble("-merge", resource_name)
300
+ dscl_vector << "AuthenticationAuthority" << ";ShadowHash;"
301
+ begin
302
+ dscl_output = execute(dscl_vector)
303
+ rescue Puppet::ExecutionFailure => detail
304
+ fail("Could not set AuthenticationAuthority.")
305
+ end
306
+ else
307
+ # 10.7 uses salted SHA512 password hashes which are 128 characters plus
308
+ # an 8 character salt. Previous versions used a SHA1 hash padded with
309
+ # zeroes. If someone attempts to use a password hash that worked with
310
+ # a previous version of OX X, we will fail early and warn them.
311
+ if password_hash.length != 136
312
+ fail("OS X 10.7 requires a Salted SHA512 hash password of 136 characters. \
313
+ Please check your password and try again.")
314
+ end
315
+
316
+ if File.exists?("#{@@users_plist_dir}/#{resource_name}.plist")
317
+ # If a plist already exists in /var/db/dslocal/nodes/Default/users, then
318
+ # we will need to extract the binary plist from the 'ShadowHashData'
319
+ # key, log the new password into the resultant plist's 'SALTED-SHA512'
320
+ # key, and then save the entire structure back.
321
+ users_plist = Plist::parse_xml(plutil( '-convert', 'xml1', '-o', '/dev/stdout', \
322
+ "#{@@users_plist_dir}/#{resource_name}.plist"))
323
+
324
+ # users_plist['ShadowHashData'][0].string is actually a binary plist
325
+ # that's nested INSIDE the user's plist (which itself is a binary
326
+ # plist).
327
+ password_hash_plist = users_plist['ShadowHashData'][0].string
328
+ converted_hash_plist = convert_binary_to_xml(password_hash_plist)
329
+
330
+ # converted_hash_plist['SALTED-SHA512'].string expects a Base64 encoded
331
+ # string. The password_hash provided as a resource attribute is a
332
+ # hex value. We need to convert the provided hex value to a Base64
333
+ # encoded string to nest it in the converted hash plist.
334
+ converted_hash_plist['SALTED-SHA512'].string = \
335
+ password_hash.unpack('a2'*(password_hash.size/2)).collect { |i| i.hex.chr }.join
336
+
337
+ # Finally, we can convert the nested plist back to binary, embed it
338
+ # into the user's plist, and convert the resultant plist back to
339
+ # a binary plist.
340
+ changed_plist = convert_xml_to_binary(converted_hash_plist)
341
+ users_plist['ShadowHashData'][0].string = changed_plist
342
+ Plist::Emit.save_plist(users_plist, "#{@@users_plist_dir}/#{resource_name}.plist")
343
+ plutil('-convert', 'binary1', "#{@@users_plist_dir}/#{resource_name}.plist")
344
+ end
345
+ end
346
+ end
347
+
348
+ def self.get_password(guid, username)
349
+ # Use Puppet::Util::Package.versioncmp() to catch the scenario where a
350
+ # version '10.10' would be < '10.7' with simple string comparison. This
351
+ # if-statement only executes if the current version is less-than 10.7
352
+ if (Puppet::Util::Package.versioncmp(get_macosx_version_major, '10.7') == -1)
353
+ password_hash = nil
354
+ password_hash_file = "#{@@password_hash_dir}/#{guid}"
355
+ if File.exists?(password_hash_file) and File.file?(password_hash_file)
356
+ fail("Could not read password hash file at #{password_hash_file}") if not File.readable?(password_hash_file)
357
+ f = File.new(password_hash_file)
358
+ password_hash = f.read
359
+ f.close
360
+ end
361
+ password_hash
362
+ else
363
+ if File.exists?("#{@@users_plist_dir}/#{username}.plist")
364
+ # If a plist exists in /var/db/dslocal/nodes/Default/users, we will
365
+ # extract the binary plist from the 'ShadowHashData' key, decode the
366
+ # salted-SHA512 password hash, and then return it.
367
+ users_plist = Plist::parse_xml(plutil('-convert', 'xml1', '-o', '/dev/stdout', "#{@@users_plist_dir}/#{username}.plist"))
368
+ if users_plist['ShadowHashData']
369
+ # users_plist['ShadowHashData'][0].string is actually a binary plist
370
+ # that's nested INSIDE the user's plist (which itself is a binary
371
+ # plist).
372
+ password_hash_plist = users_plist['ShadowHashData'][0].string
373
+ converted_hash_plist = convert_binary_to_xml(password_hash_plist)
374
+
375
+ # converted_hash_plist['SALTED-SHA512'].string is a Base64 encoded
376
+ # string. The password_hash provided as a resource attribute is a
377
+ # hex value. We need to convert the Base64 encoded string to a
378
+ # hex value and provide it back to Puppet.
379
+ password_hash = converted_hash_plist['SALTED-SHA512'].string.unpack("H*")[0]
380
+ password_hash
381
+ end
382
+ end
276
383
  end
384
+ end
277
385
 
278
- # NBK: For shadow hashes, the user AuthenticationAuthority must contain a value of
279
- # ";ShadowHash;". The LKDC in 10.5 makes this more interesting though as it
280
- # will dynamically generate ;Kerberosv5;;username@LKDC:SHA1 attributes if
281
- # missing. Thus we make sure we only set ;ShadowHash; if it is missing, and
282
- # we can do this with the merge command. This allows people to continue to
283
- # use other custom AuthenticationAuthority attributes without stomping on them.
284
- #
285
- # There is a potential problem here in that we're only doing this when setting
286
- # the password, and the attribute could get modified at other times while the
287
- # hash doesn't change and so this doesn't get called at all... but
288
- # without switching all the other attributes to merge instead of create I can't
289
- # see a simple enough solution for this that doesn't modify the user record
290
- # every single time. This should be a rather rare edge case. (famous last words)
291
-
292
- dscl_vector = self.get_exec_preamble("-merge", resource_name)
293
- dscl_vector << "AuthenticationAuthority" << ";ShadowHash;"
294
- begin
295
- dscl_output = execute(dscl_vector)
296
- rescue Puppet::ExecutionFailure => detail
297
- fail("Could not set AuthenticationAuthority.")
386
+ # This method will accept a hash that has been returned from Plist::parse_xml
387
+ # and convert it to a binary plist (string value).
388
+ def self.convert_xml_to_binary(plist_data)
389
+ Puppet.debug('Converting XML plist to binary')
390
+ Puppet.debug('Executing: \'plutil -convert binary1 -o - -\'')
391
+ IO.popen('plutil -convert binary1 -o - -', mode='r+') do |io|
392
+ io.write plist_data.to_plist
393
+ io.close_write
394
+ @converted_plist = io.read
298
395
  end
396
+ @converted_plist
299
397
  end
300
398
 
301
- def self.get_password(guid)
302
- password_hash = nil
303
- password_hash_file = "#{@@password_hash_dir}/#{guid}"
304
- if File.exists?(password_hash_file) and File.file?(password_hash_file)
305
- fail("Could not read password hash file at #{password_hash_file}") if not File.readable?(password_hash_file)
306
- f = File.new(password_hash_file)
307
- password_hash = f.read
308
- f.close
399
+ # This method will accept a binary plist (as a string) and convert it to a
400
+ # hash via Plist::parse_xml.
401
+ def self.convert_binary_to_xml(plist_data)
402
+ Puppet.debug('Converting binary plist to XML')
403
+ Puppet.debug('Executing: \'plutil -convert xml1 -o - -\'')
404
+ IO.popen('plutil -convert xml1 -o - -', mode='r+') do |io|
405
+ io.write plist_data
406
+ io.close_write
407
+ @converted_plist = io.read
309
408
  end
310
- password_hash
409
+ Puppet.debug('Converting XML values to a hash.')
410
+ @plist_hash = Plist::parse_xml(@converted_plist)
411
+ @plist_hash
311
412
  end
312
413
 
313
414
  # Unlike most other *nixes, OS X doesn't provide built in functionality
@@ -468,7 +569,14 @@ class DirectoryService < Puppet::Provider::NameService
468
569
  begin
469
570
  execute(cmd)
470
571
  rescue Puppet::ExecutionFailure => detail
471
- fail("Could not remove #{member} from group: #{@resource.name}, #{detail}")
572
+ # TODO: We're falling back to removing the member using dscl due to rdar://8481241
573
+ # This bug causes dseditgroup to fail to remove a member if that member doesn't exist
574
+ cmd = [:dscl, ".", "-delete", "/Groups/#{@resource.name}", "GroupMembership", member]
575
+ begin
576
+ execute(cmd)
577
+ rescue Puppet::ExecutionFailure => detail
578
+ fail("Could not remove #{member} from group: #{@resource.name}, #{detail}")
579
+ end
472
580
  end
473
581
  end
474
582
  end
@@ -535,3 +643,4 @@ class DirectoryService < Puppet::Provider::NameService
535
643
  end
536
644
  end
537
645
  end
646
+
@@ -102,7 +102,7 @@ Puppet::Type.type(:package).provide :pip,
102
102
  self.class.commands :pip => pathname
103
103
  pip *args
104
104
  else
105
- raise e
105
+ raise e, 'Could not locate the pip command.'
106
106
  end
107
107
  end
108
108
  end
@@ -56,7 +56,6 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do
56
56
  wanted = @resource[:name]
57
57
  operation = :install
58
58
 
59
- # XXX: We don't actually deal with epochs here.
60
59
  case should
61
60
  when true, false, Symbol
62
61
  # pass
@@ -87,7 +86,7 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do
87
86
  unless upd.nil?
88
87
  # FIXME: there could be more than one update for a package
89
88
  # because of multiarch
90
- return "#{upd[:version]}-#{upd[:release]}"
89
+ return "#{upd[:epoch]}:#{upd[:version]}-#{upd[:release]}"
91
90
  else
92
91
  # Yum didn't find updates, pretend the current
93
92
  # version is the latest
@@ -42,11 +42,25 @@ Puppet::Type.type(:service).provide :debian, :parent => :init do
42
42
  # See x-man-page://invoke-rc.d
43
43
  if [104, 106].include?($CHILD_STATUS.exitstatus)
44
44
  return :true
45
+ elsif [105].include?($CHILD_STATUS.exitstatus)
46
+ # 105 is unknown, which generally means the the iniscript does not support query
47
+ # The debian policy states that the initscript should support methods of query
48
+ # For those that do not, peform the checks manually
49
+ # http://www.debian.org/doc/debian-policy/ch-opersys.html
50
+ if get_start_link_count >= 4
51
+ return :true
52
+ else
53
+ return :false
54
+ end
45
55
  else
46
56
  return :false
47
57
  end
48
58
  end
49
59
 
60
+ def get_start_link_count
61
+ Dir.glob("/etc/rc*.d/S*#{@resource[:name]}").length
62
+ end
63
+
50
64
  def enable
51
65
  update_rc "-f", @resource[:name], "remove"
52
66
  update_rc @resource[:name], "defaults"
@@ -196,7 +196,7 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do
196
196
  did_enable_job = false
197
197
  cmds = []
198
198
  cmds << :launchctl << :load
199
- if self.enabled? == :false # launchctl won't load disabled jobs
199
+ if self.enabled? == :false || self.status == :stopped # launchctl won't load disabled jobs
200
200
  cmds << "-w"
201
201
  did_enable_job = true
202
202
  end
@@ -58,7 +58,7 @@ Puppet::Type.type(:service).provide :smf, :parent => :base do
58
58
  when :maintenance
59
59
  [command(:adm), :clear, @resource[:name]]
60
60
  else
61
- [command(:adm), :enable, @resource[:name]]
61
+ [command(:adm), :enable, "-s", @resource[:name]]
62
62
  end
63
63
  end
64
64
 
@@ -98,7 +98,7 @@ Puppet::Type.type(:service).provide :smf, :parent => :base do
98
98
  end
99
99
 
100
100
  def stopcmd
101
- [command(:adm), :disable, @resource[:name]]
101
+ [command(:adm), :disable, "-s", @resource[:name]]
102
102
  end
103
103
  end
104
104
 
@@ -1,16 +1,18 @@
1
1
  require 'puppet/provider/nameservice/pw'
2
+ require 'open3'
2
3
 
3
4
  Puppet::Type.type(:user).provide :pw, :parent => Puppet::Provider::NameService::PW do
4
5
  desc "User management via `pw` on FreeBSD."
5
6
 
6
7
  commands :pw => "pw"
7
- has_features :manages_homedir, :allows_duplicates
8
+ has_features :manages_homedir, :allows_duplicates, :manages_passwords, :manages_expiry
8
9
 
9
10
  defaultfor :operatingsystem => :freebsd
10
11
 
11
12
  options :home, :flag => "-d", :method => :dir
12
13
  options :comment, :method => :gecos
13
14
  options :groups, :flag => "-G"
15
+ options :expiry, :method => :expire
14
16
 
15
17
  verify :gid, "GID must be an integer" do |value|
16
18
  value.is_a? Integer
@@ -23,10 +25,14 @@ Puppet::Type.type(:user).provide :pw, :parent => Puppet::Provider::NameService::
23
25
  def addcmd
24
26
  cmd = [command(:pw), "useradd", @resource[:name]]
25
27
  @resource.class.validproperties.each do |property|
26
- next if property == :ensure
28
+ next if property == :ensure or property == :password
27
29
  # the value needs to be quoted, mostly because -c might
28
30
  # have spaces in it
29
31
  if value = @resource.should(property) and value != ""
32
+ if property == :expiry
33
+ # FreeBSD uses DD-MM-YYYY rather than YYYY-MM-DD
34
+ value = value.split("-").reverse.join("-")
35
+ end
30
36
  cmd << flag(property) << value
31
37
  end
32
38
  end
@@ -37,5 +43,53 @@ Puppet::Type.type(:user).provide :pw, :parent => Puppet::Provider::NameService::
37
43
 
38
44
  cmd
39
45
  end
46
+
47
+ def modifycmd(param, value)
48
+ if param == :expiry
49
+ # FreeBSD uses DD-MM-YYYY rather than YYYY-MM-DD
50
+ value = value.split("-").reverse.join("-")
51
+ end
52
+ cmd = super(param, value)
53
+ cmd << "-m" if @resource.managehome?
54
+ cmd
55
+ end
56
+
57
+ def create
58
+ super
59
+
60
+ # Set the password after create if given
61
+ self.password = @resource[:password] if @resource[:password]
62
+ end
63
+
64
+ # use pw to update password hash
65
+ def password=(cryptopw)
66
+ Puppet.debug "change password for user '#{@resource[:name]}' method called with hash '#{cryptopw}'"
67
+ stdin, stdout, stderr = Open3.popen3("pw user mod #{@resource[:name]} -H 0")
68
+ stdin.puts(cryptopw)
69
+ stdin.close
70
+ Puppet.debug "finished password for user '#{@resource[:name]}' method called with hash '#{cryptopw}'"
71
+ end
72
+
73
+ # get password from /etc/master.passwd
74
+ def password
75
+ Puppet.debug "checking password for user '#{@resource[:name]}' method called"
76
+ current_passline = `getent passwd #{@resource[:name]}`
77
+ current_password = current_passline.chomp.split(':')[1] if current_passline
78
+ Puppet.debug "finished password for user '#{@resource[:name]}' method called : '#{current_password}'"
79
+ current_password
80
+ end
81
+
82
+ # Get expiry from system and convert to Puppet-style date
83
+ def expiry
84
+ expiry = self.get(:expiry)
85
+ expiry = :absent if expiry == 0
86
+
87
+ if expiry != :absent
88
+ t = Time.at(expiry)
89
+ expiry = "%4d-%02d-%02d" % [t.year, t.month, t.mday]
90
+ end
91
+
92
+ expiry
93
+ end
40
94
  end
41
95