chef 11.18.12 → 12.0.0.alpha.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (307) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +10 -0
  3. data/README.md +1 -1
  4. data/distro/common/html/_sources/index.txt +5 -2
  5. data/distro/common/html/_sources/knife_serve.txt +19 -0
  6. data/distro/common/html/_sources/knife_ssl_check.txt +2 -2
  7. data/distro/common/html/_sources/knife_ssl_fetch.txt +2 -2
  8. data/distro/common/html/_static/basic.css +1 -1
  9. data/distro/common/html/_static/doctools.js +1 -1
  10. data/distro/common/html/_static/searchtools.js +1 -1
  11. data/distro/common/html/_static/websupport.js +1 -1
  12. data/distro/common/html/ctl_chef_client.html +19 -9
  13. data/distro/common/html/ctl_chef_server.html +7 -1
  14. data/distro/common/html/ctl_chef_shell.html +3 -4
  15. data/distro/common/html/ctl_chef_solo.html +12 -7
  16. data/distro/common/html/index.html +19 -12
  17. data/distro/common/html/knife.html +1 -2
  18. data/distro/common/html/knife_bootstrap.html +16 -5
  19. data/distro/common/html/knife_client.html +8 -5
  20. data/distro/common/html/knife_common_options.html +11 -12
  21. data/distro/common/html/knife_configure.html +2 -3
  22. data/distro/common/html/knife_cookbook.html +16 -17
  23. data/distro/common/html/knife_cookbook_site.html +19 -18
  24. data/distro/common/html/knife_data_bag.html +6 -7
  25. data/distro/common/html/knife_delete.html +2 -3
  26. data/distro/common/html/knife_deps.html +2 -3
  27. data/distro/common/html/knife_diff.html +3 -4
  28. data/distro/common/html/knife_download.html +6 -7
  29. data/distro/common/html/knife_edit.html +0 -1
  30. data/distro/common/html/knife_environment.html +3 -4
  31. data/distro/common/html/knife_exec.html +0 -1
  32. data/distro/common/html/knife_index_rebuild.html +0 -1
  33. data/distro/common/html/knife_list.html +5 -6
  34. data/distro/common/html/knife_node.html +13 -6
  35. data/distro/common/html/knife_raw.html +0 -1
  36. data/distro/common/html/knife_recipe_list.html +0 -1
  37. data/distro/common/html/knife_role.html +1 -2
  38. data/distro/common/html/knife_search.html +2 -3
  39. data/distro/common/html/knife_serve.html +79 -0
  40. data/distro/common/html/knife_show.html +1 -2
  41. data/distro/common/html/knife_ssh.html +2 -3
  42. data/distro/common/html/knife_ssl_check.html +12 -9
  43. data/distro/common/html/knife_ssl_fetch.html +9 -10
  44. data/distro/common/html/knife_status.html +2 -3
  45. data/distro/common/html/knife_tag.html +0 -1
  46. data/distro/common/html/knife_upload.html +3 -4
  47. data/distro/common/html/knife_user.html +2 -3
  48. data/distro/common/html/knife_using.html +0 -1
  49. data/distro/common/html/knife_xargs.html +3 -4
  50. data/distro/common/html/search.html +0 -1
  51. data/distro/common/html/searchindex.js +1 -1
  52. data/lib/chef/api_client.rb +1 -1
  53. data/lib/chef/application.rb +24 -67
  54. data/lib/chef/application/client.rb +1 -1
  55. data/lib/chef/application/knife.rb +1 -1
  56. data/lib/chef/chef_fs/chef_fs_data_store.rb +2 -3
  57. data/lib/chef/chef_fs/command_line.rb +2 -3
  58. data/lib/chef/chef_fs/data_handler/group_data_handler.rb +1 -5
  59. data/lib/chef/chef_fs/file_system/acl_entry.rb +1 -2
  60. data/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb +1 -2
  61. data/lib/chef/chef_fs/file_system/cookbooks_dir.rb +2 -2
  62. data/lib/chef/chef_fs/file_system/rest_list_dir.rb +2 -3
  63. data/lib/chef/chef_fs/file_system/rest_list_entry.rb +4 -5
  64. data/lib/chef/config.rb +26 -2
  65. data/lib/chef/config_fetcher.rb +1 -1
  66. data/lib/chef/cookbook/cookbook_version_loader.rb +4 -4
  67. data/lib/chef/cookbook/metadata.rb +1 -1
  68. data/lib/chef/cookbook/synchronizer.rb +50 -8
  69. data/lib/chef/cookbook_uploader.rb +9 -23
  70. data/lib/chef/cookbook_version.rb +3 -2
  71. data/lib/chef/data_bag.rb +1 -1
  72. data/lib/chef/data_bag_item.rb +1 -1
  73. data/lib/chef/dsl/recipe.rb +1 -14
  74. data/lib/chef/encrypted_data_bag_item/decryptor.rb +3 -3
  75. data/lib/chef/environment.rb +1 -1
  76. data/lib/chef/exceptions.rb +2 -24
  77. data/lib/chef/file_content_management/tempfile.rb +8 -1
  78. data/lib/chef/formatters/base.rb +0 -7
  79. data/lib/chef/http.rb +12 -19
  80. data/lib/chef/http/json_input.rb +12 -1
  81. data/lib/chef/json_compat.rb +45 -64
  82. data/lib/chef/knife.rb +2 -5
  83. data/lib/chef/knife/bootstrap.rb +2 -2
  84. data/lib/chef/knife/bootstrap/archlinux-gems.erb +2 -2
  85. data/lib/chef/knife/bootstrap/centos5-gems.erb +2 -2
  86. data/lib/chef/knife/bootstrap/chef-aix.erb +2 -2
  87. data/lib/chef/knife/bootstrap/chef-full.erb +2 -2
  88. data/lib/chef/knife/bootstrap/fedora13-gems.erb +2 -2
  89. data/lib/chef/knife/bootstrap/ubuntu10.04-apt.erb +2 -2
  90. data/lib/chef/knife/bootstrap/ubuntu10.04-gems.erb +2 -2
  91. data/lib/chef/knife/bootstrap/ubuntu12.04-gems.erb +2 -2
  92. data/lib/chef/knife/cookbook_site_download.rb +1 -1
  93. data/lib/chef/knife/cookbook_site_install.rb +10 -34
  94. data/lib/chef/knife/cookbook_site_list.rb +1 -1
  95. data/lib/chef/knife/cookbook_site_search.rb +1 -1
  96. data/lib/chef/knife/cookbook_site_share.rb +3 -3
  97. data/lib/chef/knife/cookbook_site_show.rb +3 -3
  98. data/lib/chef/knife/cookbook_site_unshare.rb +1 -1
  99. data/lib/chef/knife/cookbook_upload.rb +1 -1
  100. data/lib/chef/knife/core/subcommand_loader.rb +0 -24
  101. data/lib/chef/knife/core/ui.rb +8 -7
  102. data/lib/chef/knife/deps.rb +2 -3
  103. data/lib/chef/knife/serve.rb +2 -1
  104. data/lib/chef/local_mode.rb +105 -0
  105. data/lib/chef/mixin/file_class.rb +1 -4
  106. data/lib/chef/mixin/shell_out.rb +15 -0
  107. data/lib/chef/mixin/windows_architecture_helper.rb +0 -16
  108. data/lib/chef/node.rb +1 -1
  109. data/lib/chef/platform/query_helpers.rb +1 -5
  110. data/lib/chef/policy_builder/expand_node_object.rb +3 -0
  111. data/lib/chef/provider.rb +2 -0
  112. data/lib/chef/provider/cookbook_file.rb +0 -1
  113. data/lib/chef/provider/deploy.rb +0 -1
  114. data/lib/chef/provider/deploy/revision.rb +1 -1
  115. data/lib/chef/provider/env.rb +10 -25
  116. data/lib/chef/provider/env/windows.rb +23 -10
  117. data/lib/chef/provider/execute.rb +0 -3
  118. data/lib/chef/provider/file.rb +0 -3
  119. data/lib/chef/provider/git.rb +0 -6
  120. data/lib/chef/provider/group/dscl.rb +9 -27
  121. data/lib/chef/provider/group/gpasswd.rb +0 -3
  122. data/lib/chef/provider/group/groupmod.rb +0 -4
  123. data/lib/chef/provider/group/suse.rb +0 -3
  124. data/lib/chef/provider/group/usermod.rb +0 -3
  125. data/lib/chef/provider/link.rb +22 -5
  126. data/lib/chef/provider/log.rb +15 -4
  127. data/lib/chef/provider/mdadm.rb +0 -3
  128. data/lib/chef/provider/mount/mount.rb +0 -2
  129. data/lib/chef/provider/mount/solaris.rb +0 -2
  130. data/lib/chef/provider/package.rb +0 -1
  131. data/lib/chef/provider/package/apt.rb +0 -3
  132. data/lib/chef/provider/package/dpkg.rb +0 -1
  133. data/lib/chef/provider/package/easy_install.rb +0 -4
  134. data/lib/chef/provider/package/freebsd/base.rb +0 -3
  135. data/lib/chef/provider/package/freebsd/pkgng.rb +0 -2
  136. data/lib/chef/provider/package/freebsd/port.rb +0 -2
  137. data/lib/chef/provider/package/ips.rb +0 -3
  138. data/lib/chef/provider/package/paludis.rb +0 -5
  139. data/lib/chef/provider/package/portage.rb +0 -2
  140. data/lib/chef/provider/package/rpm.rb +2 -4
  141. data/lib/chef/provider/package/rubygems.rb +0 -4
  142. data/lib/chef/provider/package/smartos.rb +0 -3
  143. data/lib/chef/provider/package/windows/msi.rb +0 -2
  144. data/lib/chef/provider/package/yum.rb +0 -4
  145. data/lib/chef/provider/package/zypper.rb +0 -3
  146. data/lib/chef/provider/registry_key.rb +0 -2
  147. data/lib/chef/provider/remote_directory.rb +0 -1
  148. data/lib/chef/provider/remote_file.rb +0 -1
  149. data/lib/chef/provider/remote_file/cache_control_data.rb +1 -3
  150. data/lib/chef/provider/remote_file/content.rb +0 -1
  151. data/lib/chef/provider/remote_file/fetcher.rb +0 -2
  152. data/lib/chef/provider/remote_file/ftp.rb +0 -1
  153. data/lib/chef/provider/resource_update.rb +0 -3
  154. data/lib/chef/provider/service/freebsd.rb +0 -3
  155. data/lib/chef/provider/service/init.rb +0 -3
  156. data/lib/chef/provider/service/macosx.rb +0 -1
  157. data/lib/chef/provider/service/redhat.rb +0 -2
  158. data/lib/chef/provider/service/simple.rb +0 -3
  159. data/lib/chef/provider/service/solaris.rb +0 -3
  160. data/lib/chef/provider/service/systemd.rb +15 -14
  161. data/lib/chef/provider/service/windows.rb +0 -3
  162. data/lib/chef/provider/subversion.rb +0 -2
  163. data/lib/chef/provider/template.rb +0 -2
  164. data/lib/chef/provider/template/content.rb +0 -1
  165. data/lib/chef/provider/user/dscl.rb +156 -549
  166. data/lib/chef/provider/user/solaris.rb +0 -1
  167. data/lib/chef/provider/user/useradd.rb +0 -3
  168. data/lib/chef/provider/whyrun_safe_ruby_block.rb +1 -1
  169. data/lib/chef/providers.rb +0 -1
  170. data/lib/chef/resource.rb +1 -4
  171. data/lib/chef/resource/freebsd_package.rb +2 -10
  172. data/lib/chef/resource/lwrp_base.rb +1 -12
  173. data/lib/chef/resource/user.rb +0 -18
  174. data/lib/chef/resource_collection.rb +1 -1
  175. data/lib/chef/resource_reporter.rb +10 -10
  176. data/lib/chef/resources.rb +0 -1
  177. data/lib/chef/role.rb +3 -3
  178. data/lib/chef/run_list.rb +1 -1
  179. data/lib/chef/tasks/chef_repo.rake +131 -264
  180. data/lib/chef/user.rb +1 -1
  181. data/lib/chef/util/path_helper.rb +2 -2
  182. data/lib/chef/version.rb +9 -1
  183. data/lib/chef/win32/api/system.rb +0 -9
  184. data/spec/data/bootstrap/test-hints.erb +1 -1
  185. data/spec/data/bootstrap/test.erb +1 -1
  186. data/spec/functional/dsl/reboot_pending_spec.rb +53 -58
  187. data/spec/functional/knife/cookbook_delete_spec.rb +3 -3
  188. data/spec/functional/knife/exec_spec.rb +1 -1
  189. data/spec/functional/mixin/shell_out_spec.rb +48 -0
  190. data/spec/functional/resource/base.rb +0 -10
  191. data/spec/functional/resource/group_spec.rb +1 -5
  192. data/spec/functional/resource/link_spec.rb +8 -0
  193. data/spec/functional/resource/{user/useradd_spec.rb → user_spec.rb} +1 -1
  194. data/spec/integration/knife/chef_fs_data_store_spec.rb +3 -3
  195. data/spec/integration/knife/chef_repo_path_spec.rb +1 -6
  196. data/spec/integration/knife/chef_repository_file_system_spec.rb +1 -1
  197. data/spec/integration/knife/chefignore_spec.rb +1 -1
  198. data/spec/integration/knife/common_options_spec.rb +50 -3
  199. data/spec/integration/knife/cookbook_api_ipv6_spec.rb +1 -1
  200. data/spec/integration/knife/delete_spec.rb +1 -1
  201. data/spec/integration/knife/deps_spec.rb +1 -1
  202. data/spec/integration/knife/diff_spec.rb +3 -3
  203. data/spec/integration/knife/download_spec.rb +3 -3
  204. data/spec/integration/knife/list_spec.rb +1 -1
  205. data/spec/integration/knife/raw_spec.rb +1 -11
  206. data/spec/integration/knife/redirection_spec.rb +1 -1
  207. data/spec/integration/knife/serve_spec.rb +2 -2
  208. data/spec/integration/knife/show_spec.rb +1 -1
  209. data/spec/integration/knife/upload_spec.rb +9 -9
  210. data/spec/spec_helper.rb +0 -9
  211. data/spec/support/pedant/pedant_config.rb +2 -1
  212. data/spec/support/pedant/run_pedant.rb +2 -1
  213. data/spec/support/platform_helpers.rb +5 -24
  214. data/spec/support/shared/integration/integration_helper.rb +2 -1
  215. data/spec/support/shared/matchers.rb +17 -0
  216. data/spec/tiny_server.rb +1 -2
  217. data/spec/unit/api_client_spec.rb +3 -3
  218. data/spec/unit/application_spec.rb +9 -32
  219. data/spec/unit/config_fetcher_spec.rb +1 -1
  220. data/spec/unit/cookbook/metadata_spec.rb +3 -7
  221. data/spec/unit/cookbook/synchronizer_spec.rb +441 -226
  222. data/spec/unit/cookbook_loader_spec.rb +1 -1
  223. data/spec/unit/cookbook_uploader_spec.rb +160 -0
  224. data/spec/unit/cookbook_version_spec.rb +0 -4
  225. data/spec/unit/data_bag_item_spec.rb +1 -5
  226. data/spec/unit/data_bag_spec.rb +1 -5
  227. data/spec/unit/deprecation_spec.rb +1 -1
  228. data/spec/unit/dsl/recipe_spec.rb +12 -0
  229. data/spec/unit/encrypted_data_bag_item_spec.rb +7 -14
  230. data/spec/unit/environment_spec.rb +3 -7
  231. data/spec/unit/exceptions_spec.rb +0 -6
  232. data/spec/unit/http/json_input_spec.rb +128 -0
  233. data/spec/unit/json_compat_spec.rb +17 -58
  234. data/spec/unit/knife/client_create_spec.rb +3 -3
  235. data/spec/unit/knife/configure_client_spec.rb +6 -5
  236. data/spec/unit/knife/cookbook_delete_spec.rb +1 -1
  237. data/spec/unit/knife/cookbook_download_spec.rb +5 -5
  238. data/spec/unit/knife/cookbook_metadata_from_file_spec.rb +1 -0
  239. data/spec/unit/knife/cookbook_metadata_spec.rb +1 -1
  240. data/spec/unit/knife/cookbook_site_download_spec.rb +10 -11
  241. data/spec/unit/knife/cookbook_site_install_spec.rb +116 -161
  242. data/spec/unit/knife/cookbook_site_share_spec.rb +8 -8
  243. data/spec/unit/knife/cookbook_upload_spec.rb +3 -2
  244. data/spec/unit/knife/core/bootstrap_context_spec.rb +3 -3
  245. data/spec/unit/knife/core/subcommand_loader_spec.rb +1 -66
  246. data/spec/unit/knife/data_bag_from_file_spec.rb +2 -1
  247. data/spec/unit/knife/tag_create_spec.rb +3 -3
  248. data/spec/unit/knife/tag_delete_spec.rb +3 -3
  249. data/spec/unit/knife/user_create_spec.rb +1 -1
  250. data/spec/unit/knife_spec.rb +14 -14
  251. data/spec/unit/lwrp_spec.rb +1 -21
  252. data/spec/unit/mixin/shell_out_spec.rb +92 -0
  253. data/spec/unit/node_spec.rb +0 -4
  254. data/spec/unit/platform/query_helpers_spec.rb +0 -23
  255. data/spec/unit/provider/env/windows_spec.rb +34 -70
  256. data/spec/unit/provider/env_spec.rb +11 -76
  257. data/spec/unit/provider/group/dscl_spec.rb +1 -38
  258. data/spec/unit/provider/log_spec.rb +18 -0
  259. data/spec/unit/provider/package/rpm_spec.rb +0 -12
  260. data/spec/unit/provider/remote_file/cache_control_data_spec.rb +1 -1
  261. data/spec/unit/provider/service/systemd_service_spec.rb +44 -27
  262. data/spec/unit/provider/user/dscl_spec.rb +264 -660
  263. data/spec/unit/provider/user/useradd_spec.rb +0 -1
  264. data/spec/unit/provider/whyrun_safe_ruby_block_spec.rb +2 -2
  265. data/spec/unit/provider_spec.rb +12 -0
  266. data/spec/unit/recipe_spec.rb +0 -41
  267. data/spec/unit/resource_collection_spec.rb +1 -5
  268. data/spec/unit/resource_reporter_spec.rb +3 -51
  269. data/spec/unit/resource_spec.rb +3 -14
  270. data/spec/unit/rest_spec.rb +1 -4
  271. data/spec/unit/role_spec.rb +0 -10
  272. data/spec/unit/run_list_spec.rb +1 -5
  273. data/spec/unit/user_spec.rb +1 -5
  274. metadata +20 -100
  275. data/lib/chef/mixin/windows_env_helper.rb +0 -56
  276. data/lib/chef/provider/dsc_script.rb +0 -175
  277. data/lib/chef/resource/dsc_script.rb +0 -126
  278. data/lib/chef/streaming_cookbook_uploader.rb +0 -205
  279. data/lib/chef/util/dsc/configuration_generator.rb +0 -115
  280. data/lib/chef/util/dsc/lcm_output_parser.rb +0 -133
  281. data/lib/chef/util/dsc/local_configuration_manager.rb +0 -141
  282. data/lib/chef/util/dsc/resource_info.rb +0 -26
  283. data/lib/chef/util/powershell/cmdlet.rb +0 -136
  284. data/lib/chef/util/powershell/cmdlet_result.rb +0 -46
  285. data/spec/data/mac_users/10.7-8.plist.xml +0 -559
  286. data/spec/data/mac_users/10.7-8.shadow.xml +0 -11
  287. data/spec/data/mac_users/10.7.plist.xml +0 -559
  288. data/spec/data/mac_users/10.7.shadow.xml +0 -11
  289. data/spec/data/mac_users/10.8.plist.xml +0 -559
  290. data/spec/data/mac_users/10.8.shadow.xml +0 -21
  291. data/spec/data/mac_users/10.9.plist.xml +0 -560
  292. data/spec/data/mac_users/10.9.shadow.xml +0 -21
  293. data/spec/functional/provider/whyrun_safe_ruby_block_spec.rb +0 -51
  294. data/spec/functional/resource/dsc_script_spec.rb +0 -382
  295. data/spec/functional/resource/env_spec.rb +0 -182
  296. data/spec/functional/resource/user/dscl_spec.rb +0 -199
  297. data/spec/functional/util/powershell/cmdlet_spec.rb +0 -113
  298. data/spec/support/lib/chef/resource/zen_follower.rb +0 -46
  299. data/spec/support/shared/shared_examples.rb +0 -10
  300. data/spec/unit/chef_fs/data_handler/group_handler_spec.rb +0 -63
  301. data/spec/unit/formatters/base_spec.rb +0 -48
  302. data/spec/unit/provider/dsc_script_spec.rb +0 -174
  303. data/spec/unit/resource/dsc_script_spec.rb +0 -98
  304. data/spec/unit/util/dsc/configuration_generator_spec.rb +0 -171
  305. data/spec/unit/util/dsc/lcm_output_parser_spec.rb +0 -169
  306. data/spec/unit/util/dsc/local_configuration_manager_spec.rb +0 -139
  307. data/spec/unit/util/powershell/cmdlet_spec.rb +0 -106
@@ -40,7 +40,6 @@ class Chef
40
40
  name !~ /(?:^|#{Regexp.escape(::File::SEPARATOR)})\.\.?$/
41
41
  end)
42
42
 
43
-
44
43
  files_to_transfer.each do |cookbook_file_relative_path|
45
44
  create_cookbook_file(cookbook_file_relative_path)
46
45
  # the file is removed from the purge list
@@ -50,4 +50,3 @@ class Chef
50
50
  end
51
51
  end
52
52
  end
53
-
@@ -140,7 +140,7 @@ class Chef
140
140
 
141
141
  def load_data
142
142
  Chef::JSONCompat.from_json(load_json_data)
143
- rescue Chef::Exceptions::FileNotFound, FFI_Yajl::ParseError, Chef::Exceptions::JSON::ParseError
143
+ rescue Chef::Exceptions::FileNotFound, FFI_Yajl::ParseError, JSON::ParserError
144
144
  false
145
145
  end
146
146
 
@@ -161,5 +161,3 @@ class Chef
161
161
  end
162
162
  end
163
163
  end
164
-
165
-
@@ -17,7 +17,6 @@
17
17
  # limitations under the License.
18
18
  #
19
19
 
20
- require 'rest_client'
21
20
  require 'uri'
22
21
  require 'tempfile'
23
22
  require 'chef/file_content_management/content_base'
@@ -17,7 +17,6 @@
17
17
  # limitations under the License.
18
18
  #
19
19
 
20
-
21
20
  class Chef
22
21
  class Provider
23
22
  class RemoteFile
@@ -40,4 +39,3 @@ class Chef
40
39
  end
41
40
  end
42
41
  end
43
-
@@ -81,7 +81,6 @@ class Chef
81
81
  @filename
82
82
  end
83
83
 
84
-
85
84
  def fetch
86
85
  with_connection do
87
86
  get
@@ -50,6 +50,3 @@ class Chef
50
50
  end
51
51
  end
52
52
  end
53
-
54
-
55
-
@@ -16,7 +16,6 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require 'chef/mixin/shell_out'
20
19
  require 'chef/resource/service'
21
20
  require 'chef/provider/service/init'
22
21
  require 'chef/mixin/command'
@@ -26,8 +25,6 @@ class Chef
26
25
  class Service
27
26
  class Freebsd < Chef::Provider::Service::Init
28
27
 
29
- include Chef::Mixin::ShellOut
30
-
31
28
  def load_current_resource
32
29
  @current_resource = Chef::Resource::Service.new(@new_resource.name)
33
30
  @current_resource.service_name(@new_resource.service_name)
@@ -16,7 +16,6 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require 'chef/mixin/shell_out'
20
19
  require 'chef/provider/service/simple'
21
20
  require 'chef/mixin/command'
22
21
 
@@ -25,8 +24,6 @@ class Chef
25
24
  class Service
26
25
  class Init < Chef::Provider::Service::Simple
27
26
 
28
- include Chef::Mixin::ShellOut
29
-
30
27
  def initialize(new_resource, run_context)
31
28
  super
32
29
  @init_command = "/etc/init.d/#{@new_resource.service_name}"
@@ -24,7 +24,6 @@ class Chef
24
24
  class Provider
25
25
  class Service
26
26
  class Macosx < Chef::Provider::Service::Simple
27
- include Chef::Mixin::ShellOut
28
27
 
29
28
  def self.gather_plist_dirs
30
29
  locations = %w{/Library/LaunchAgents
@@ -17,13 +17,11 @@
17
17
  #
18
18
 
19
19
  require 'chef/provider/service/init'
20
- require 'chef/mixin/shell_out'
21
20
 
22
21
  class Chef
23
22
  class Provider
24
23
  class Service
25
24
  class Redhat < Chef::Provider::Service::Init
26
- include Chef::Mixin::ShellOut
27
25
 
28
26
  CHKCONFIG_ON = /\d:on/
29
27
  CHKCONFIG_MISSING = /No such/
@@ -16,7 +16,6 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require 'chef/mixin/shell_out'
20
19
  require 'chef/provider/service'
21
20
  require 'chef/resource/service'
22
21
  require 'chef/mixin/command'
@@ -26,8 +25,6 @@ class Chef
26
25
  class Service
27
26
  class Simple < Chef::Provider::Service
28
27
 
29
- include Chef::Mixin::ShellOut
30
-
31
28
  def load_current_resource
32
29
  @current_resource = Chef::Resource::Service.new(@new_resource.name)
33
30
  @current_resource.service_name(@new_resource.service_name)
@@ -16,7 +16,6 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require 'chef/mixin/shell_out'
20
19
  require 'chef/provider/service'
21
20
  require 'chef/resource/service'
22
21
  require 'chef/mixin/command'
@@ -25,7 +24,6 @@ class Chef
25
24
  class Provider
26
25
  class Service
27
26
  class Solaris < Chef::Provider::Service
28
- include Chef::Mixin::ShellOut
29
27
  attr_reader :maintenance
30
28
 
31
29
  def initialize(new_resource, run_context=nil)
@@ -35,7 +33,6 @@ class Chef
35
33
  @maintenace = false
36
34
  end
37
35
 
38
-
39
36
  def load_current_resource
40
37
  @current_resource = Chef::Resource::Service.new(@new_resource.name)
41
38
  @current_resource.service_name(@new_resource.service_name)
@@ -18,7 +18,6 @@
18
18
 
19
19
  require 'chef/resource/service'
20
20
  require 'chef/provider/service/simple'
21
- require 'chef/mixin/command'
22
21
 
23
22
  class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
24
23
  def load_current_resource
@@ -29,11 +28,9 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
29
28
  if @new_resource.status_command
30
29
  Chef::Log.debug("#{@new_resource} you have specified a status command, running..")
31
30
 
32
- begin
33
- if run_command_with_systems_locale(:command => @new_resource.status_command) == 0
34
- @current_resource.running(true)
35
- end
36
- rescue Chef::Exceptions::Exec
31
+ unless shell_out_with_systems_locale(@new_resource.status_command).error?
32
+ @current_resource.running(true)
33
+ else
37
34
  @status_check_success = false
38
35
  @current_resource.running(false)
39
36
  @current_resource.enabled(false)
@@ -64,7 +61,7 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
64
61
  if @new_resource.start_command
65
62
  super
66
63
  else
67
- run_command_with_systems_locale(:command => "/bin/systemctl start #{@new_resource.service_name}")
64
+ shell_out_with_systems_locale("/bin/systemctl start #{@new_resource.service_name}")
68
65
  end
69
66
  end
70
67
  end
@@ -76,7 +73,7 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
76
73
  if @new_resource.stop_command
77
74
  super
78
75
  else
79
- run_command_with_systems_locale(:command => "/bin/systemctl stop #{@new_resource.service_name}")
76
+ shell_out_with_systems_locale("/bin/systemctl stop #{@new_resource.service_name}")
80
77
  end
81
78
  end
82
79
  end
@@ -85,7 +82,7 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
85
82
  if @new_resource.restart_command
86
83
  super
87
84
  else
88
- run_command_with_systems_locale(:command => "/bin/systemctl restart #{@new_resource.service_name}")
85
+ shell_out_with_systems_locale("/bin/systemctl restart #{@new_resource.service_name}")
89
86
  end
90
87
  end
91
88
 
@@ -93,23 +90,27 @@ class Chef::Provider::Service::Systemd < Chef::Provider::Service::Simple
93
90
  if @new_resource.reload_command
94
91
  super
95
92
  else
96
- run_command_with_systems_locale(:command => "/bin/systemctl reload #{@new_resource.service_name}")
93
+ if @current_resource.running
94
+ shell_out_with_systems_locale("/bin/systemctl reload #{@new_resource.service_name}")
95
+ else
96
+ start_service
97
+ end
97
98
  end
98
99
  end
99
100
 
100
101
  def enable_service
101
- run_command_with_systems_locale(:command => "/bin/systemctl enable #{@new_resource.service_name}")
102
+ shell_out_with_systems_locale("/bin/systemctl enable #{@new_resource.service_name}")
102
103
  end
103
104
 
104
105
  def disable_service
105
- run_command_with_systems_locale(:command => "/bin/systemctl disable #{@new_resource.service_name}")
106
+ shell_out_with_systems_locale("/bin/systemctl disable #{@new_resource.service_name}")
106
107
  end
107
108
 
108
109
  def is_active?
109
- run_command_with_systems_locale({:command => "/bin/systemctl is-active #{@new_resource.service_name}", :ignore_failure => true}) == 0
110
+ shell_out_with_systems_locale("/bin/systemctl is-active #{@new_resource.service_name} --quiet").exitstatus == 0
110
111
  end
111
112
 
112
113
  def is_enabled?
113
- run_command_with_systems_locale({:command => "/bin/systemctl is-enabled #{@new_resource.service_name}", :ignore_failure => true}) == 0
114
+ shell_out_with_systems_locale("/bin/systemctl is-enabled #{@new_resource.service_name} --quiet").exitstatus == 0
114
115
  end
115
116
  end
@@ -18,7 +18,6 @@
18
18
  # limitations under the License.
19
19
  #
20
20
 
21
- require 'chef/mixin/shell_out'
22
21
  require 'chef/provider/service/simple'
23
22
  if RUBY_PLATFORM =~ /mswin|mingw32|windows/
24
23
  require 'win32/service'
@@ -26,8 +25,6 @@ end
26
25
 
27
26
  class Chef::Provider::Service::Windows < Chef::Provider::Service
28
27
 
29
- include Chef::Mixin::ShellOut
30
-
31
28
  #Win32::Service.get_start_type
32
29
  AUTO_START = 'auto start'
33
30
  DISABLED = 'disabled'
@@ -16,7 +16,6 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
-
20
19
  #TODO subversion and git should both extend from a base SCM provider.
21
20
 
22
21
  require 'chef/log'
@@ -199,7 +198,6 @@ class Chef
199
198
  ['svn', *args].compact.join(" ")
200
199
  end
201
200
 
202
-
203
201
  def target_dir_non_existent_or_empty?
204
202
  !::File.exist?(@new_resource.destination) || Dir.entries(@new_resource.destination).sort == ['.','..']
205
203
  end
@@ -22,7 +22,6 @@ require 'chef/provider/file'
22
22
  require 'chef/deprecation/provider/template'
23
23
  require 'chef/deprecation/warnings'
24
24
 
25
-
26
25
  class Chef
27
26
  class Provider
28
27
  class Template < Chef::Provider::File
@@ -63,4 +62,3 @@ class Chef
63
62
  end
64
63
  end
65
64
  end
66
-
@@ -58,4 +58,3 @@ class Chef
58
58
  end
59
59
  end
60
60
  end
61
-
@@ -16,214 +16,40 @@
16
16
  # limitations under the License.
17
17
  #
18
18
 
19
- require 'mixlib/shellout'
20
19
  require 'chef/provider/user'
21
20
  require 'openssl'
22
- require 'plist'
23
21
 
24
22
  class Chef
25
23
  class Provider
26
24
  class User
27
- #
28
- # The most tricky bit of this provider is the way it deals with user passwords.
29
- # Mac OS X has different password shadow calculations based on the version.
30
- # < 10.7 => password shadow calculation format SALTED-SHA1
31
- # => stored in: /var/db/shadow/hash/#{guid}
32
- # => shadow binary length 68 bytes
33
- # => First 4 bytes salt / Next 64 bytes shadow value
34
- # = 10.7 => password shadow calculation format SALTED-SHA512
35
- # => stored in: /var/db/dslocal/nodes/Default/users/#{name}.plist
36
- # => shadow binary length 68 bytes
37
- # => First 4 bytes salt / Next 64 bytes shadow value
38
- # > 10.7 => password shadow calculation format SALTED-SHA512-PBKDF2
39
- # => stored in: /var/db/dslocal/nodes/Default/users/#{name}.plist
40
- # => shadow binary length 128 bytes
41
- # => Salt / Iterations are stored seperately in the same file
42
- #
43
- # This provider only supports Mac OSX versions 10.7 and above
44
25
  class Dscl < Chef::Provider::User
45
- include Chef::Mixin::ShellOut
46
26
 
47
- def define_resource_requirements
48
- super
49
-
50
- requirements.assert(:all_actions) do |a|
51
- a.assertion { mac_osx_version_less_than_10_7? == false }
52
- a.failure_message(Chef::Exceptions::User, "Chef::Provider::User::Dscl only supports Mac OS X versions 10.7 and above.")
53
- end
54
-
55
- requirements.assert(:all_actions) do |a|
56
- a.assertion { ::File.exists?("/usr/bin/dscl") }
57
- a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/dscl' on the system for #{@new_resource}!")
58
- end
59
-
60
- requirements.assert(:all_actions) do |a|
61
- a.assertion { ::File.exists?("/usr/bin/plutil") }
62
- a.failure_message(Chef::Exceptions::User, "Cannot find binary '/usr/bin/plutil' on the system for #{@new_resource}!")
63
- end
64
-
65
- requirements.assert(:create, :modify, :manage) do |a|
66
- a.assertion do
67
- if @new_resource.password && mac_osx_version_greater_than_10_7?
68
- # SALTED-SHA512 password shadow hashes are not supported on 10.8 and above.
69
- !salted_sha512?(@new_resource.password)
70
- else
71
- true
72
- end
73
- end
74
- a.failure_message(Chef::Exceptions::User, "SALTED-SHA512 passwords are not supported on Mac 10.8 and above. \
75
- If you want to set the user password using shadow info make sure you specify a SALTED-SHA512-PBKDF2 shadow hash \
76
- in 'password', with the associated 'salt' and 'iterations'.")
77
- end
78
-
79
- requirements.assert(:create, :modify, :manage) do |a|
80
- a.assertion do
81
- if @new_resource.password && mac_osx_version_greater_than_10_7? && salted_sha512_pbkdf2?(@new_resource.password)
82
- # salt and iterations should be specified when
83
- # SALTED-SHA512-PBKDF2 password shadow hash is given
84
- !@new_resource.salt.nil? && !@new_resource.iterations.nil?
85
- else
86
- true
87
- end
88
- end
89
- a.failure_message(Chef::Exceptions::User, "SALTED-SHA512-PBKDF2 shadow hash is given without associated \
90
- 'salt' and 'iterations'. Please specify 'salt' and 'iterations' in order to set the user password using shadow hash.")
91
- end
92
-
93
- requirements.assert(:create, :modify, :manage) do |a|
94
- a.assertion do
95
- if @new_resource.password && !mac_osx_version_greater_than_10_7?
96
- # On 10.7 SALTED-SHA512-PBKDF2 is not supported
97
- !salted_sha512_pbkdf2?(@new_resource.password)
98
- else
99
- true
100
- end
101
- end
102
- a.failure_message(Chef::Exceptions::User, "SALTED-SHA512-PBKDF2 shadow hashes are not supported on \
103
- Mac OS X version 10.7. Please specify a SALTED-SHA512 shadow hash in 'password' attribute to set the \
104
- user password using shadow hash.")
105
- end
27
+ NFS_HOME_DIRECTORY = %r{^NFSHomeDirectory: (.*)$}
28
+ AUTHENTICATION_AUTHORITY = %r{^AuthenticationAuthority: (.*)$}
106
29
 
30
+ def dscl(*args)
31
+ shell_out("dscl . -#{args.join(' ')}")
107
32
  end
108
33
 
109
- def load_current_resource
110
- @current_resource = Chef::Resource::User.new(@new_resource.username)
111
- @current_resource.username(@new_resource.username)
112
-
113
- @user_info = read_user_info
114
- if @user_info
115
- @current_resource.uid(dscl_get(@user_info, :uid))
116
- @current_resource.gid(dscl_get(@user_info, :gid))
117
- @current_resource.home(dscl_get(@user_info, :home))
118
- @current_resource.shell(dscl_get(@user_info, :shell))
119
- @current_resource.comment(dscl_get(@user_info, :comment))
120
- @authentication_authority = dscl_get(@user_info, :auth_authority)
121
-
122
- if @new_resource.password && dscl_get(@user_info, :password) == "********"
123
- # A password is set. Let's get the password information from shadow file
124
- shadow_hash_binary = dscl_get(@user_info, :shadow_hash)
125
-
126
- # Calling shell_out directly since we want to give an input stream
127
- shadow_hash_xml = convert_binary_plist_to_xml(shadow_hash_binary.string)
128
- shadow_hash = Plist::parse_xml(shadow_hash_xml)
129
-
130
- if shadow_hash["SALTED-SHA512"]
131
- # Convert the shadow value from Base64 encoding to hex before consuming them
132
- @password_shadow_conversion_algorithm = "SALTED-SHA512"
133
- @current_resource.password(shadow_hash["SALTED-SHA512"].string.unpack('H*').first)
134
- elsif shadow_hash["SALTED-SHA512-PBKDF2"]
135
- @password_shadow_conversion_algorithm = "SALTED-SHA512-PBKDF2"
136
- # Convert the entropy from Base64 encoding to hex before consuming them
137
- @current_resource.password(shadow_hash["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack('H*').first)
138
- @current_resource.iterations(shadow_hash["SALTED-SHA512-PBKDF2"]["iterations"])
139
- # Convert the salt from Base64 encoding to hex before consuming them
140
- @current_resource.salt(shadow_hash["SALTED-SHA512-PBKDF2"]["salt"].string.unpack('H*').first)
141
- else
142
- raise(Chef::Exceptions::User,"Unknown shadow_hash format: #{shadow_hash.keys.join(' ')}")
143
- end
144
- end
145
-
146
- convert_group_name if @new_resource.gid
147
- else
148
- @user_exists = false
149
- Chef::Log.debug("#{@new_resource} user does not exist")
150
- end
151
-
152
- @current_resource
153
- end
154
-
155
- #
156
- # Provider Actions
157
- #
158
-
159
- def create_user
160
- dscl_create_user
161
- # set_password modifies the plist file of the user directly. So update
162
- # the password first before making any modifications to the user.
163
- set_password
164
- dscl_create_comment
165
- dscl_set_uid
166
- dscl_set_gid
167
- dscl_set_home
168
- dscl_set_shell
169
- end
170
-
171
- def manage_user
172
- # set_password modifies the plist file of the user directly. So update
173
- # the password first before making any modifications to the user.
174
- set_password if diverged_password?
175
- dscl_create_user if diverged?(:username)
176
- dscl_create_comment if diverged?(:comment)
177
- dscl_set_uid if diverged?(:uid)
178
- dscl_set_gid if diverged?(:gid)
179
- dscl_set_home if diverged?(:home)
180
- dscl_set_shell if diverged?(:shell)
181
- end
182
-
183
- #
184
- # Action Helpers
185
- #
186
-
187
- #
188
- # Create a user using dscl
189
- #
190
- def dscl_create_user
191
- run_dscl("create /Users/#{@new_resource.username}")
192
- end
193
-
194
- #
195
- # Saves the specified Chef user `comment` into RealName attribute
196
- # of Mac user.
197
- #
198
- def dscl_create_comment
199
- run_dscl("create /Users/#{@new_resource.username} RealName '#{@new_resource.comment}'")
34
+ def safe_dscl(*args)
35
+ result = dscl(*args)
36
+ return "" if ( args.first =~ /^delete/ ) && ( result.exitstatus != 0 )
37
+ raise(Chef::Exceptions::DsclCommandFailed,"dscl error: #{result.inspect}") unless result.exitstatus == 0
38
+ raise(Chef::Exceptions::DsclCommandFailed,"dscl error: #{result.inspect}") if result.stdout =~ /No such key: /
39
+ return result.stdout
200
40
  end
201
41
 
202
- #
203
- # Sets the user id for the user using dscl.
204
- # If a `uid` is not specified, it finds the next available one starting
205
- # from 200 if `system` is set, 500 otherwise.
206
- #
207
- def dscl_set_uid
208
- @new_resource.uid(get_free_uid) if (@new_resource.uid.nil? || @new_resource.uid == '')
209
-
210
- if uid_used?(@new_resource.uid)
211
- raise(Chef::Exceptions::RequestedUIDUnavailable, "uid #{@new_resource.uid} is already in use")
212
- end
213
-
214
- run_dscl("create /Users/#{@new_resource.username} UniqueID #{@new_resource.uid}")
215
- end
42
+ # This is handled in providers/group.rb by Etc.getgrnam()
43
+ # def user_exists?(user)
44
+ # users = safe_dscl("list /Users")
45
+ # !! ( users =~ Regexp.new("\n#{user}\n") )
46
+ # end
216
47
 
217
- #
218
- # Find the next available uid on the system. starting with 200 if `system` is set,
219
- # 500 otherwise.
220
- #
48
+ # get a free UID greater than 200
221
49
  def get_free_uid(search_limit=1000)
222
- uid = nil
223
- base_uid = @new_resource.system ? 200 : 500
224
- next_uid_guess = base_uid
225
- users_uids = run_dscl("list /Users uid")
226
- while(next_uid_guess < search_limit + base_uid)
50
+ uid = nil; next_uid_guess = 200
51
+ users_uids = safe_dscl("list /Users uid")
52
+ while(next_uid_guess < search_limit + 200)
227
53
  if users_uids =~ Regexp.new("#{Regexp.escape(next_uid_guess.to_s)}\n")
228
54
  next_uid_guess += 1
229
55
  else
@@ -234,41 +60,22 @@ user password using shadow hash.")
234
60
  return uid || raise("uid not found. Exhausted. Searched #{search_limit} times")
235
61
  end
236
62
 
237
- #
238
- # Returns true if uid is in use by a different account, false otherwise.
239
- #
240
63
  def uid_used?(uid)
241
64
  return false unless uid
242
- users_uids = run_dscl("list /Users uid")
65
+ users_uids = safe_dscl("list /Users uid")
243
66
  !! ( users_uids =~ Regexp.new("#{Regexp.escape(uid.to_s)}\n") )
244
67
  end
245
68
 
246
- #
247
- # Sets the group id for the user using dscl. Fails if a group doesn't
248
- # exist on the system with given group id.
249
- #
250
- def dscl_set_gid
251
- unless @new_resource.gid && @new_resource.gid.to_s.match(/^\d+$/)
252
- begin
253
- possible_gid = run_dscl("read /Groups/#{@new_resource.gid} PrimaryGroupID").split(" ").last
254
- rescue Chef::Exceptions::DsclCommandFailed => e
255
- raise Chef::Exceptions::GroupIDNotFound.new("Group not found for #{@new_resource.gid} when creating user #{@new_resource.username}")
256
- end
257
- @new_resource.gid(possible_gid) if possible_gid && possible_gid.match(/^\d+$/)
69
+ def set_uid
70
+ @new_resource.uid(get_free_uid) if (@new_resource.uid.nil? || @new_resource.uid == '')
71
+ if uid_used?(@new_resource.uid)
72
+ raise(Chef::Exceptions::RequestedUIDUnavailable, "uid #{@new_resource.uid} is already in use")
258
73
  end
259
- run_dscl("create /Users/#{@new_resource.username} PrimaryGroupID '#{@new_resource.gid}'")
74
+ safe_dscl("create /Users/#{@new_resource.username} UniqueID #{@new_resource.uid}")
260
75
  end
261
76
 
262
- #
263
- # Sets the home directory for the user. If `:manage_home` is set home
264
- # directory is managed (moved / created) for the user.
265
- #
266
- def dscl_set_home
267
- if @new_resource.home.nil? || @new_resource.home.empty?
268
- run_dscl("delete /Users/#{@new_resource.username} NFSHomeDirectory")
269
- return
270
- end
271
-
77
+ def modify_home
78
+ return safe_dscl("delete /Users/#{@new_resource.username} NFSHomeDirectory") if (@new_resource.home.nil? || @new_resource.home.empty?)
272
79
  if @new_resource.supports[:manage_home]
273
80
  validate_home_dir_specification!
274
81
 
@@ -280,399 +87,199 @@ user password using shadow hash.")
280
87
  move_home
281
88
  end
282
89
  end
283
- run_dscl("create /Users/#{@new_resource.username} NFSHomeDirectory '#{@new_resource.home}'")
90
+ safe_dscl("create /Users/#{@new_resource.username} NFSHomeDirectory '#{@new_resource.home}'")
284
91
  end
285
92
 
286
- def validate_home_dir_specification!
287
- unless @new_resource.home =~ /^\//
288
- raise(Chef::Exceptions::InvalidHomeDirectory,"invalid path spec for User: '#{@new_resource.username}', home directory: '#{@new_resource.home}'")
289
- end
93
+ def osx_shadow_hash?(string)
94
+ return !! ( string =~ /^[[:xdigit:]]{1240}$/ )
290
95
  end
291
96
 
292
- def current_home_exists?
293
- ::File.exist?("#{@current_resource.home}")
97
+ def osx_salted_sha1?(string)
98
+ return !! ( string =~ /^[[:xdigit:]]{48}$/ )
294
99
  end
295
100
 
296
- def new_home_exists?
297
- ::File.exist?("#{@new_resource.home}")
298
- end
299
-
300
- def ditto_home
301
- skel = "/System/Library/User Template/English.lproj"
302
- raise(Chef::Exceptions::User,"can't find skel at: #{skel}") unless ::File.exists?(skel)
303
- shell_out! "ditto '#{skel}' '#{@new_resource.home}'"
304
- ::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
305
- end
306
-
307
- def move_home
308
- Chef::Log.debug("#{@new_resource} moving #{self} home from #{@current_resource.home} to #{@new_resource.home}")
309
-
310
- src = @current_resource.home
311
- FileUtils.mkdir_p(@new_resource.home)
312
- files = ::Dir.glob("#{src}/*", ::File::FNM_DOTMATCH) - ["#{src}/.","#{src}/.."]
313
- ::FileUtils.mv(files,@new_resource.home, :force => true)
314
- ::FileUtils.rmdir(src)
315
- ::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
101
+ def guid
102
+ safe_dscl("read /Users/#{@new_resource.username} GeneratedUID").gsub(/GeneratedUID: /,"").strip
316
103
  end
317
104
 
318
- #
319
- # Sets the shell for the user using dscl.
320
- #
321
- def dscl_set_shell
322
- if @new_resource.shell || ::File.exists?("#{@new_resource.shell}")
323
- run_dscl("create /Users/#{@new_resource.username} UserShell '#{@new_resource.shell}'")
105
+ def shadow_hash_set?
106
+ user_data = safe_dscl("read /Users/#{@new_resource.username}")
107
+ if user_data =~ /AuthenticationAuthority: / && user_data =~ /ShadowHash/
108
+ true
324
109
  else
325
- run_dscl("create /Users/#{@new_resource.username} UserShell '/usr/bin/false'")
110
+ false
326
111
  end
327
112
  end
328
113
 
329
- #
330
- # Sets the password for the user based on given password parameters.
331
- # Chef supports specifying plain-text passwords and password shadow
332
- # hash data.
333
- #
334
- def set_password
335
- # Return if there is no password to set
336
- return if @new_resource.password.nil?
337
-
338
- shadow_info = prepare_password_shadow_info
339
-
340
- # Shadow info is saved as binary plist. Convert the info to binary plist.
341
- shadow_info_binary = StringIO.new
342
- command = Mixlib::ShellOut.new("plutil -convert binary1 -o - -",
343
- :input => shadow_info.to_plist, :live_stream => shadow_info_binary)
344
- command.run_command
345
-
346
- if @user_info.nil?
347
- # User is just created. read_user_info() will read the fresh information
348
- # for the user with a cache flush. However with experimentation we've seen
349
- # that dscl cache is not immediately updated after the creation of the user
350
- # This is odd and needs to be investigated further.
351
- sleep 3
352
- @user_info = read_user_info
353
- end
354
-
355
- # Replace the shadow info in user's plist
356
- dscl_set(@user_info, :shadow_hash, shadow_info_binary)
357
- save_user_info(@user_info)
358
- end
114
+ def modify_password
115
+ if @new_resource.password
116
+ shadow_hash = nil
359
117
 
360
- #
361
- # Prepares the password shadow info based on the platform version.
362
- #
363
- def prepare_password_shadow_info
364
- shadow_info = { }
365
- entropy = nil
366
- salt = nil
367
- iterations = nil
368
-
369
- if mac_osx_version_10_7?
370
- hash_value = if salted_sha512?(@new_resource.password)
371
- @new_resource.password
118
+ Chef::Log.debug("#{new_resource} updating password")
119
+ if osx_shadow_hash?(@new_resource.password)
120
+ shadow_hash = @new_resource.password.upcase
372
121
  else
373
- # Create a random 4 byte salt
374
- salt = OpenSSL::Random.random_bytes(4)
375
- encoded_password = OpenSSL::Digest::SHA512.hexdigest(salt + @new_resource.password)
376
- hash_value = salt.unpack('H*').first + encoded_password
122
+ if osx_salted_sha1?(@new_resource.password)
123
+ salted_sha1 = @new_resource.password.upcase
124
+ else
125
+ hex_salt = ""
126
+ OpenSSL::Random.random_bytes(10).each_byte { |b| hex_salt << b.to_i.to_s(16) }
127
+ hex_salt = hex_salt.slice(0...8)
128
+ salt = [hex_salt].pack("H*")
129
+ sha1 = ::OpenSSL::Digest::SHA1.hexdigest(salt+@new_resource.password)
130
+ salted_sha1 = (hex_salt+sha1).upcase
131
+ end
132
+ shadow_hash = String.new("00000000"*155)
133
+ shadow_hash[168] = salted_sha1
377
134
  end
378
135
 
379
- shadow_info["SALTED-SHA512"] = StringIO.new
380
- shadow_info["SALTED-SHA512"].string = convert_to_binary(hash_value)
381
- shadow_info
382
- else
383
- if salted_sha512_pbkdf2?(@new_resource.password)
384
- entropy = convert_to_binary(@new_resource.password)
385
- salt = convert_to_binary(@new_resource.salt)
386
- iterations = @new_resource.iterations
387
- else
388
- salt = OpenSSL::Random.random_bytes(32)
389
- iterations = @new_resource.iterations # Use the default if not specified by the user
390
-
391
- entropy = OpenSSL::PKCS5::pbkdf2_hmac(
392
- @new_resource.password,
393
- salt,
394
- iterations,
395
- 128,
396
- OpenSSL::Digest::SHA512.new
397
- )
136
+ ::File.open("/var/db/shadow/hash/#{guid}",'w',0600) do |output|
137
+ output.puts shadow_hash
398
138
  end
399
139
 
400
- pbkdf_info = { }
401
- pbkdf_info["entropy"] = StringIO.new
402
- pbkdf_info["entropy"].string = entropy
403
- pbkdf_info["salt"] = StringIO.new
404
- pbkdf_info["salt"].string = salt
405
- pbkdf_info["iterations"] = iterations
406
-
407
- shadow_info["SALTED-SHA512-PBKDF2"] = pbkdf_info
408
- end
409
-
410
- shadow_info
411
- end
412
-
413
- #
414
- # Removes the user from the system after removing user from his groups
415
- # and deleting home directory if needed.
416
- #
417
- def remove_user
418
- if @new_resource.supports[:manage_home]
419
- # Remove home directory
420
- FileUtils.rm_rf(@current_resource.home)
421
- end
422
-
423
- # Remove the user from its groups
424
- run_dscl("list /Groups").each_line do |group|
425
- if member_of_group?(group.chomp)
426
- run_dscl("delete /Groups/#{group.chomp} GroupMembership '#{@new_resource.username}'")
140
+ unless shadow_hash_set?
141
+ safe_dscl("append /Users/#{@new_resource.username} AuthenticationAuthority ';ShadowHash;'")
427
142
  end
428
143
  end
429
-
430
- # Remove user account
431
- run_dscl("delete /Users/#{@new_resource.username}")
432
144
  end
433
145
 
434
- #
435
- # Locks the user.
436
- #
437
- def lock_user
438
- run_dscl("append /Users/#{@new_resource.username} AuthenticationAuthority ';DisabledUser;'")
439
- end
440
-
441
- #
442
- # Unlocks the user
443
- #
444
- def unlock_user
445
- auth_string = @authentication_authority.gsub(/AuthenticationAuthority: /,"").gsub(/;DisabledUser;/,"").strip
446
- run_dscl("create /Users/#{@new_resource.username} AuthenticationAuthority '#{auth_string}'")
146
+ def load_current_resource
147
+ super
148
+ raise Chef::Exceptions::User, "Could not find binary /usr/bin/dscl for #{@new_resource}" unless ::File.exists?("/usr/bin/dscl")
447
149
  end
448
150
 
449
- #
450
- # Returns true if the user is locked, false otherwise.
451
- #
452
- def locked?
453
- if @authentication_authority
454
- !!(@authentication_authority =~ /DisabledUser/ )
455
- else
456
- false
457
- end
151
+ def create_user
152
+ dscl_create_user
153
+ dscl_create_comment
154
+ set_uid
155
+ dscl_set_gid
156
+ modify_home
157
+ dscl_set_shell
158
+ modify_password
458
159
  end
459
160
 
460
- #
461
- # This is the interface base User provider requires to provide idempotency.
462
- #
463
- def check_lock
464
- return @locked = locked?
161
+ def manage_user
162
+ dscl_create_user if diverged?(:username)
163
+ dscl_create_comment if diverged?(:comment)
164
+ set_uid if diverged?(:uid)
165
+ dscl_set_gid if diverged?(:gid)
166
+ modify_home if diverged?(:home)
167
+ dscl_set_shell if diverged?(:shell)
168
+ modify_password if diverged?(:password)
465
169
  end
466
170
 
467
- #
468
- # Helper functions
469
- #
470
-
471
- #
472
- # Returns true if the system state and desired state is different for
473
- # given attribute.
474
- #
475
- def diverged?(parameter)
476
- parameter_updated?(parameter) && (not @new_resource.send(parameter).nil?)
171
+ def dscl_create_user
172
+ safe_dscl("create /Users/#{@new_resource.username}")
477
173
  end
478
174
 
479
- def parameter_updated?(parameter)
480
- not (@new_resource.send(parameter) == @current_resource.send(parameter))
175
+ def dscl_create_comment
176
+ safe_dscl("create /Users/#{@new_resource.username} RealName '#{@new_resource.comment}'")
481
177
  end
482
178
 
483
- #
484
- # We need a special check function for password since we support both
485
- # plain text and shadow hash data.
486
- #
487
- # Checks if password needs update based on platform version and the
488
- # type of the password specified.
489
- #
490
- def diverged_password?
491
- return false if @new_resource.password.nil?
492
-
493
- # Dscl provider supports both plain text passwords and shadow hashes.
494
- if mac_osx_version_10_7?
495
- if salted_sha512?(@new_resource.password)
496
- diverged?(:password)
497
- else
498
- !salted_sha512_password_match?
499
- end
500
- else
501
- # When a system is upgraded to a version 10.7+ shadow hashes of the users
502
- # will be updated when the user logs in. So it's possible that we will have
503
- # SALTED-SHA512 password in the current_resource. In that case we will force
504
- # password to be updated.
505
- return true if salted_sha512?(@current_resource.password)
506
-
507
- if salted_sha512_pbkdf2?(@new_resource.password)
508
- diverged?(:password) || diverged?(:salt) || diverged?(:iterations)
509
- else
510
- !salted_sha512_pbkdf2_password_match?
179
+ def dscl_set_gid
180
+ unless @new_resource.gid && @new_resource.gid.to_s.match(/^\d+$/)
181
+ begin
182
+ possible_gid = safe_dscl("read /Groups/#{@new_resource.gid} PrimaryGroupID").split(" ").last
183
+ rescue Chef::Exceptions::DsclCommandFailed => e
184
+ raise Chef::Exceptions::GroupIDNotFound.new("Group not found for #{@new_resource.gid} when creating user #{@new_resource.username}")
511
185
  end
186
+ @new_resource.gid(possible_gid) if possible_gid && possible_gid.match(/^\d+$/)
512
187
  end
188
+ safe_dscl("create /Users/#{@new_resource.username} PrimaryGroupID '#{@new_resource.gid}'")
513
189
  end
514
190
 
515
- #
516
- # Returns true if user is member of the specified group, false otherwise.
517
- #
518
- def member_of_group?(group_name)
519
- membership_info = ""
520
- begin
521
- membership_info = run_dscl("read /Groups/#{group_name}")
522
- rescue Chef::Exceptions::DsclCommandFailed
523
- # Raised if the group doesn't contain any members
191
+ def dscl_set_shell
192
+ if @new_resource.password || ::File.exists?("#{@new_resource.shell}")
193
+ safe_dscl("create /Users/#{@new_resource.username} UserShell '#{@new_resource.shell}'")
194
+ else
195
+ safe_dscl("create /Users/#{@new_resource.username} UserShell '/usr/bin/false'")
524
196
  end
525
- # Output is something like:
526
- # GroupMembership: root admin etc
527
- members = membership_info.split(" ")
528
- members.shift # Get rid of GroupMembership: string
529
- members.include?(@new_resource.username)
530
197
  end
531
198
 
532
- #
533
- # DSCL Helper functions
534
- #
535
-
536
- # A simple map of Chef's terms to DSCL's terms.
537
- DSCL_PROPERTY_MAP = {
538
- :uid => "generateduid",
539
- :gid => "gid",
540
- :home => "home",
541
- :shell => "shell",
542
- :comment => "realname",
543
- :password => "passwd",
544
- :auth_authority => "authentication_authority",
545
- :shadow_hash => "ShadowHashData"
546
- }.freeze
547
-
548
- # Directory where the user plist files are stored for versions 10.7 and above
549
- USER_PLIST_DIRECTORY = "/var/db/dslocal/nodes/Default/users".freeze
550
-
551
- #
552
- # Reads the user plist and returns a hash keyed with DSCL properties specified
553
- # in DSCL_PROPERTY_MAP. Return nil if the user is not found.
554
- #
555
- def read_user_info
556
- user_info = nil
557
-
558
- # We flush the cache here in order to make sure that we read fresh information
559
- # for the user.
560
- shell_out("dscacheutil '-flushcache'")
561
-
562
- begin
563
- user_plist_file = "#{USER_PLIST_DIRECTORY}/#{@new_resource.username}.plist"
564
- user_plist_info = run_plutil("convert xml1 -o - #{user_plist_file}")
565
- user_info = Plist::parse_xml(user_plist_info)
566
- rescue Chef::Exceptions::PlistUtilCommandFailed
199
+ def remove_user
200
+ if @new_resource.supports[:manage_home]
201
+ user_info = safe_dscl("read /Users/#{@new_resource.username}")
202
+ if nfs_home_match = user_info.match(NFS_HOME_DIRECTORY)
203
+ #nfs_home = safe_dscl("read /Users/#{@new_resource.username} NFSHomeDirectory")
204
+ #nfs_home.gsub!(/NFSHomeDirectory: /,"").gsub!(/\n$/,"")
205
+ nfs_home = nfs_home_match[1]
206
+ FileUtils.rm_rf(nfs_home)
207
+ end
567
208
  end
568
-
569
- user_info
570
- end
571
-
572
- #
573
- # Saves the given hash keyed with DSCL properties specified
574
- # in DSCL_PROPERTY_MAP to the disk.
575
- #
576
- def save_user_info(user_info)
577
- user_plist_file = "#{USER_PLIST_DIRECTORY}/#{@new_resource.username}.plist"
578
- Plist::Emit.save_plist(user_info, user_plist_file)
579
- run_plutil("convert binary1 #{user_plist_file}")
580
- end
581
-
582
- #
583
- # Sets a value in user information hash using Chef attributes as keys.
584
- #
585
- def dscl_set(user_hash, key, value)
586
- raise "Unknown dscl key #{key}" unless DSCL_PROPERTY_MAP.keys.include?(key)
587
- user_hash[DSCL_PROPERTY_MAP[key]] = [ value ]
588
- user_hash
589
- end
590
-
591
- #
592
- # Gets a value from user information hash using Chef attributes as keys.
593
- #
594
- def dscl_get(user_hash, key)
595
- raise "Unknown dscl key #{key}" unless DSCL_PROPERTY_MAP.keys.include?(key)
596
- # DSCL values are set as arrays
597
- value = user_hash[DSCL_PROPERTY_MAP[key]]
598
- value.nil? ? value : value.first
209
+ # remove the user from its groups
210
+ groups = []
211
+ Etc.group do |group|
212
+ groups << group.name if group.mem.include?(@new_resource.username)
213
+ end
214
+ groups.each do |group_name|
215
+ safe_dscl("delete /Groups/#{group_name} GroupMembership '#{@new_resource.username}'")
216
+ end
217
+ # remove user account
218
+ safe_dscl("delete /Users/#{@new_resource.username}")
599
219
  end
600
220
 
601
- #
602
- # System Helpets
603
- #
604
-
605
- def mac_osx_version
606
- # This provider will only be invoked on node[:platform] == "mac_os_x"
607
- # We do not check or assert that here.
608
- node[:platform_version]
221
+ def locked?
222
+ user_info = safe_dscl("read /Users/#{@new_resource.username}")
223
+ if auth_authority_md = AUTHENTICATION_AUTHORITY.match(user_info)
224
+ !!(auth_authority_md[1] =~ /DisabledUser/ )
225
+ else
226
+ false
227
+ end
609
228
  end
610
229
 
611
- def mac_osx_version_10_7?
612
- mac_osx_version.start_with?("10.7.")
230
+ def check_lock
231
+ return @locked = locked?
613
232
  end
614
233
 
615
- def mac_osx_version_less_than_10_7?
616
- versions = mac_osx_version.split(".")
617
- # Make integer comparison in order not to report 10.10 less than 10.7
618
- (versions[0].to_i <= 10 && versions[1].to_i < 7)
234
+ def lock_user
235
+ safe_dscl("append /Users/#{@new_resource.username} AuthenticationAuthority ';DisabledUser;'")
619
236
  end
620
237
 
621
- def mac_osx_version_greater_than_10_7?
622
- versions = mac_osx_version.split(".")
623
- # Make integer comparison in order not to report 10.10 less than 10.7
624
- (versions[0].to_i >= 10 && versions[1].to_i > 7)
238
+ def unlock_user
239
+ auth_info = safe_dscl("read /Users/#{@new_resource.username} AuthenticationAuthority")
240
+ auth_string = auth_info.gsub(/AuthenticationAuthority: /,"").gsub(/;DisabledUser;/,"").strip#.gsub!(/[; ]*$/,"")
241
+ safe_dscl("create /Users/#{@new_resource.username} AuthenticationAuthority '#{auth_string}'")
625
242
  end
626
243
 
627
- def run_dscl(*args)
628
- result = shell_out("dscl . -#{args.join(' ')}")
629
- return "" if ( args.first =~ /^delete/ ) && ( result.exitstatus != 0 )
630
- raise(Chef::Exceptions::DsclCommandFailed,"dscl error: #{result.inspect}") unless result.exitstatus == 0
631
- raise(Chef::Exceptions::DsclCommandFailed,"dscl error: #{result.inspect}") if result.stdout =~ /No such key: /
632
- result.stdout
244
+ def validate_home_dir_specification!
245
+ unless @new_resource.home =~ /^\//
246
+ raise(Chef::Exceptions::InvalidHomeDirectory,"invalid path spec for User: '#{@new_resource.username}', home directory: '#{@new_resource.home}'")
247
+ end
633
248
  end
634
249
 
635
- def run_plutil(*args)
636
- result = shell_out("plutil -#{args.join(' ')}")
637
- raise(Chef::Exceptions::PlistUtilCommandFailed,"plutil error: #{result.inspect}") unless result.exitstatus == 0
638
- result.stdout
250
+ def current_home_exists?
251
+ ::File.exist?("#{@current_resource.home}")
639
252
  end
640
253
 
641
- def convert_binary_plist_to_xml(binary_plist_string)
642
- Mixlib::ShellOut.new("plutil -convert xml1 -o - -", :input => binary_plist_string).run_command.stdout
254
+ def new_home_exists?
255
+ ::File.exist?("#{@new_resource.home}")
643
256
  end
644
257
 
645
- def convert_to_binary(string)
646
- string.unpack('a2'*(string.size/2)).collect { |i| i.hex.chr }.join
258
+ def ditto_home
259
+ skel = "/System/Library/User Template/English.lproj"
260
+ raise(Chef::Exceptions::User,"can't find skel at: #{skel}") unless ::File.exists?(skel)
261
+ shell_out! "ditto '#{skel}' '#{@new_resource.home}'"
262
+ ::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
647
263
  end
648
264
 
649
- def salted_sha512?(string)
650
- !!(string =~ /^[[:xdigit:]]{136}$/)
651
- end
265
+ def move_home
266
+ Chef::Log.debug("#{@new_resource} moving #{self} home from #{@current_resource.home} to #{@new_resource.home}")
652
267
 
653
- def salted_sha512_password_match?
654
- # Salt is included in the first 4 bytes of shadow data
655
- salt = @current_resource.password.slice(0,8)
656
- shadow = OpenSSL::Digest::SHA512.hexdigest(convert_to_binary(salt) + @new_resource.password)
657
- @current_resource.password == salt + shadow
268
+ src = @current_resource.home
269
+ FileUtils.mkdir_p(@new_resource.home)
270
+ files = ::Dir.glob("#{src}/*", ::File::FNM_DOTMATCH) - ["#{src}/.","#{src}/.."]
271
+ ::FileUtils.mv(files,@new_resource.home, :force => true)
272
+ ::FileUtils.rmdir(src)
273
+ ::FileUtils.chown_R(@new_resource.username,@new_resource.gid.to_s,@new_resource.home)
658
274
  end
659
275
 
660
- def salted_sha512_pbkdf2?(string)
661
- !!(string =~ /^[[:xdigit:]]{256}$/)
276
+ def diverged?(parameter)
277
+ parameter_updated?(parameter) && (not @new_resource.send(parameter).nil?)
662
278
  end
663
279
 
664
- def salted_sha512_pbkdf2_password_match?
665
- salt = convert_to_binary(@current_resource.salt)
666
-
667
- OpenSSL::PKCS5::pbkdf2_hmac(
668
- @new_resource.password,
669
- salt,
670
- @current_resource.iterations,
671
- 128,
672
- OpenSSL::Digest::SHA512.new
673
- ).unpack('H*').first == @current_resource.password
280
+ def parameter_updated?(parameter)
281
+ not (@new_resource.send(parameter) == @current_resource.send(parameter))
674
282
  end
675
-
676
283
  end
677
284
  end
678
285
  end