chef 10.34.6 → 11.0.0.beta.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (506) hide show
  1. data/CONTRIBUTING.md +155 -0
  2. data/README.md +89 -0
  3. data/Rakefile +4 -12
  4. data/bin/chef-apply +25 -0
  5. data/bin/chef-shell +34 -0
  6. data/bin/shef +6 -5
  7. data/distro/common/html/chef-client.8.html +4 -4
  8. data/distro/common/html/chef-expander.8.html +4 -4
  9. data/distro/common/html/chef-expanderctl.8.html +4 -4
  10. data/distro/common/html/chef-server-webui.8.html +4 -4
  11. data/distro/common/html/chef-server.8.html +4 -4
  12. data/distro/common/html/{shef.1.html → chef-shell.1.html} +49 -46
  13. data/distro/common/html/chef-solo.8.html +18 -12
  14. data/distro/common/html/chef-solr.8.html +4 -4
  15. data/distro/common/html/knife-bootstrap.1.html +4 -4
  16. data/distro/common/html/knife-client.1.html +4 -4
  17. data/distro/common/html/knife-configure.1.html +4 -4
  18. data/distro/common/html/knife-cookbook-site.1.html +4 -4
  19. data/distro/common/html/knife-cookbook.1.html +10 -7
  20. data/distro/common/html/knife-data-bag.1.html +10 -7
  21. data/distro/common/html/knife-environment.1.html +8 -6
  22. data/distro/common/html/knife-exec.1.html +9 -9
  23. data/distro/common/html/knife-index.1.html +4 -4
  24. data/distro/common/html/knife-node.1.html +4 -4
  25. data/distro/common/html/knife-role.1.html +4 -4
  26. data/distro/common/html/knife-search.1.html +4 -4
  27. data/distro/common/html/knife-ssh.1.html +4 -4
  28. data/distro/common/html/knife-status.1.html +4 -4
  29. data/distro/common/html/knife-tag.1.html +4 -4
  30. data/distro/common/html/knife.1.html +8 -13
  31. data/distro/common/man/man1/{shef.1 → chef-shell.1} +21 -57
  32. data/distro/common/man/man1/knife-bootstrap.1 +1 -1
  33. data/distro/common/man/man1/knife-client.1 +1 -1
  34. data/distro/common/man/man1/knife-configure.1 +1 -1
  35. data/distro/common/man/man1/knife-cookbook-site.1 +1 -1
  36. data/distro/common/man/man1/knife-cookbook.1 +15 -2
  37. data/distro/common/man/man1/knife-data-bag.1 +15 -2
  38. data/distro/common/man/man1/knife-environment.1 +12 -2
  39. data/distro/common/man/man1/knife-exec.1 +4 -7
  40. data/distro/common/man/man1/knife-index.1 +1 -1
  41. data/distro/common/man/man1/knife-node.1 +1 -1
  42. data/distro/common/man/man1/knife-role.1 +1 -1
  43. data/distro/common/man/man1/knife-search.1 +1 -1
  44. data/distro/common/man/man1/knife-ssh.1 +1 -1
  45. data/distro/common/man/man1/knife-status.1 +1 -1
  46. data/distro/common/man/man1/knife-tag.1 +1 -1
  47. data/distro/common/man/man1/knife.1 +3 -6
  48. data/distro/common/man/man8/chef-client.8 +1 -1
  49. data/distro/common/man/man8/chef-expander.8 +1 -1
  50. data/distro/common/man/man8/chef-expanderctl.8 +1 -1
  51. data/distro/common/man/man8/chef-server-webui.8 +1 -1
  52. data/distro/common/man/man8/chef-server.8 +1 -1
  53. data/distro/common/man/man8/chef-solo.8 +36 -4
  54. data/distro/common/man/man8/chef-solr.8 +1 -1
  55. data/distro/common/markdown/man1/{shef.mkd → chef-shell.mkd} +49 -43
  56. data/distro/common/markdown/man1/knife-exec.mkd +11 -6
  57. data/distro/common/markdown/man1/knife.mkd +4 -9
  58. data/distro/debian/etc/default/chef-client +0 -1
  59. data/distro/debian/etc/init.d/chef-client +2 -2
  60. data/lib/chef.rb +2 -5
  61. data/lib/chef/api_client.rb +20 -130
  62. data/lib/chef/api_client/registration.rb +126 -0
  63. data/lib/chef/application.rb +71 -14
  64. data/lib/chef/application/apply.rb +160 -0
  65. data/lib/chef/application/client.rb +25 -18
  66. data/lib/chef/application/knife.rb +0 -2
  67. data/lib/chef/application/solo.rb +23 -8
  68. data/lib/chef/application/windows_service.rb +5 -2
  69. data/lib/chef/applications.rb +1 -0
  70. data/lib/chef/chef_fs.rb +11 -0
  71. data/lib/chef/chef_fs/command_line.rb +232 -0
  72. data/lib/chef/chef_fs/file_pattern.rb +312 -0
  73. data/lib/chef/chef_fs/file_system.rb +358 -0
  74. data/lib/chef/chef_fs/file_system/base_fs_dir.rb +47 -0
  75. data/lib/chef/chef_fs/file_system/base_fs_object.rb +121 -0
  76. data/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb +109 -0
  77. data/{spec/unit/monkey_patches/uri_spec.rb → lib/chef/chef_fs/file_system/chef_repository_file_system_root_dir.rb} +12 -15
  78. data/lib/chef/chef_fs/file_system/chef_server_root_dir.rb +84 -0
  79. data/lib/chef/chef_fs/file_system/cookbook_dir.rb +188 -0
  80. data/lib/chef/chef_fs/file_system/cookbook_file.rb +78 -0
  81. data/lib/chef/chef_fs/file_system/cookbook_subdir.rb +54 -0
  82. data/lib/chef/chef_fs/file_system/cookbooks_dir.rb +68 -0
  83. data/lib/chef/chef_fs/file_system/data_bag_dir.rb +78 -0
  84. data/lib/chef/chef_fs/file_system/data_bag_item.rb +59 -0
  85. data/lib/chef/chef_fs/file_system/data_bags_dir.rb +66 -0
  86. data/lib/chef/chef_fs/file_system/file_system_entry.rb +90 -0
  87. data/lib/chef/{index_queue.rb → chef_fs/file_system/file_system_error.rb} +14 -12
  88. data/lib/chef/{resource/whyrun_safe_ruby_block.rb → chef_fs/file_system/file_system_root_dir.rb} +10 -10
  89. data/lib/chef/chef_fs/file_system/must_delete_recursively_error.rb +31 -0
  90. data/lib/chef/chef_fs/file_system/nodes_dir.rb +47 -0
  91. data/lib/chef/{provider/whyrun_safe_ruby_block.rb → chef_fs/file_system/nonexistent_fs_object.rb} +19 -9
  92. data/lib/chef/chef_fs/file_system/not_found_error.rb +31 -0
  93. data/lib/chef/chef_fs/file_system/rest_list_dir.rb +84 -0
  94. data/lib/chef/chef_fs/file_system/rest_list_entry.rb +123 -0
  95. data/lib/chef/chef_fs/knife.rb +77 -0
  96. data/lib/chef/chef_fs/path_utils.rb +64 -0
  97. data/lib/chef/client.rb +44 -21
  98. data/lib/chef/config.rb +52 -43
  99. data/lib/chef/cookbook/synchronizer.rb +6 -8
  100. data/lib/chef/cookbook/syntax_check.rb +61 -14
  101. data/lib/chef/cookbook_loader.rb +39 -26
  102. data/lib/chef/cookbook_uploader.rb +17 -19
  103. data/lib/chef/cookbook_version.rb +3 -302
  104. data/lib/chef/daemon.rb +3 -18
  105. data/lib/chef/data_bag.rb +4 -97
  106. data/lib/chef/data_bag_item.rb +2 -65
  107. data/lib/chef/digester.rb +73 -0
  108. data/lib/chef/dsl.rb +6 -0
  109. data/lib/chef/dsl/data_query.rb +66 -0
  110. data/lib/chef/dsl/include_attribute.rb +60 -0
  111. data/lib/chef/dsl/include_recipe.rb +42 -0
  112. data/lib/chef/dsl/platform_introspection.rb +213 -0
  113. data/lib/chef/dsl/recipe.rb +84 -0
  114. data/lib/chef/dsl/registry_helper.rb +59 -0
  115. data/lib/chef/encrypted_data_bag_item.rb +74 -19
  116. data/lib/chef/environment.rb +9 -180
  117. data/lib/chef/exceptions.rb +87 -14
  118. data/lib/chef/formatters/base.rb +4 -1
  119. data/lib/chef/formatters/error_inspectors/registration_error_inspector.rb +0 -4
  120. data/lib/chef/json_compat.rb +1 -97
  121. data/lib/chef/knife.rb +90 -41
  122. data/lib/chef/knife/bootstrap/archlinux-gems.erb +2 -2
  123. data/lib/chef/knife/bootstrap/centos5-gems.erb +2 -2
  124. data/lib/chef/knife/bootstrap/chef-full.erb +3 -3
  125. data/lib/chef/knife/bootstrap/fedora13-gems.erb +2 -2
  126. data/lib/chef/knife/bootstrap/ubuntu10.04-apt.erb +2 -2
  127. data/lib/chef/knife/bootstrap/ubuntu10.04-gems.erb +2 -2
  128. data/lib/chef/knife/bootstrap/ubuntu12.04-gems.erb +2 -2
  129. data/lib/chef/knife/configure.rb +1 -2
  130. data/lib/chef/knife/cookbook_metadata.rb +1 -0
  131. data/lib/chef/knife/cookbook_test.rb +3 -2
  132. data/lib/chef/knife/cookbook_upload.rb +12 -7
  133. data/lib/chef/knife/core/bootstrap_context.rb +1 -1
  134. data/lib/chef/knife/core/generic_presenter.rb +26 -13
  135. data/lib/chef/knife/core/node_editor.rb +36 -16
  136. data/lib/chef/knife/core/node_presenter.rb +1 -1
  137. data/lib/chef/knife/core/text_formatter.rb +23 -37
  138. data/lib/chef/knife/core/ui.rb +15 -9
  139. data/lib/chef/knife/delete.rb +39 -0
  140. data/lib/chef/knife/diff.rb +46 -0
  141. data/lib/chef/knife/download.rb +50 -0
  142. data/lib/chef/knife/environment_show.rb +7 -0
  143. data/lib/chef/knife/exec.rb +5 -5
  144. data/lib/chef/knife/help_topics.rb +1 -1
  145. data/lib/chef/knife/index_rebuild.rb +91 -7
  146. data/lib/chef/knife/list.rb +109 -0
  147. data/lib/chef/knife/raw.rb +108 -0
  148. data/lib/chef/knife/search.rb +40 -22
  149. data/lib/chef/knife/show.rb +32 -0
  150. data/lib/chef/knife/ssh.rb +6 -2
  151. data/lib/chef/knife/upload.rb +50 -0
  152. data/lib/chef/mixin/checksum.rb +3 -3
  153. data/lib/chef/mixin/deep_merge.rb +55 -197
  154. data/lib/chef/mixin/language.rb +9 -222
  155. data/lib/chef/mixin/language_include_attribute.rb +6 -38
  156. data/lib/chef/mixin/language_include_recipe.rb +3 -35
  157. data/lib/chef/mixin/params_validate.rb +6 -19
  158. data/lib/chef/mixin/recipe_definition_dsl_core.rb +8 -61
  159. data/lib/chef/mixin/securable.rb +32 -7
  160. data/lib/chef/mixin/template.rb +40 -0
  161. data/lib/chef/mixins.rb +0 -4
  162. data/lib/chef/monkey_patches/net_http.rb +0 -34
  163. data/lib/chef/node.rb +133 -309
  164. data/lib/chef/node/attribute.rb +333 -473
  165. data/lib/chef/node/attribute_collections.rb +199 -0
  166. data/lib/chef/node/immutable_collections.rb +186 -0
  167. data/lib/chef/platform.rb +7 -22
  168. data/lib/chef/provider.rb +2 -49
  169. data/lib/chef/provider/breakpoint.rb +6 -6
  170. data/lib/chef/provider/cookbook_file.rb +5 -33
  171. data/lib/chef/provider/deploy.rb +2 -1
  172. data/lib/chef/provider/directory.rb +14 -17
  173. data/lib/chef/provider/file.rb +19 -52
  174. data/lib/chef/provider/group.rb +31 -51
  175. data/lib/chef/provider/group/dscl.rb +13 -53
  176. data/lib/chef/provider/group/gpasswd.rb +19 -14
  177. data/lib/chef/provider/group/groupadd.rb +1 -41
  178. data/lib/chef/provider/group/groupmod.rb +36 -46
  179. data/lib/chef/provider/group/pw.rb +16 -59
  180. data/lib/chef/provider/group/suse.rb +13 -16
  181. data/lib/chef/provider/group/usermod.rb +18 -40
  182. data/lib/chef/provider/group/windows.rb +6 -13
  183. data/lib/chef/provider/http_request.rb +25 -42
  184. data/lib/chef/provider/link.rb +2 -0
  185. data/lib/chef/provider/lwrp_base.rb +150 -0
  186. data/lib/chef/provider/package/portage.rb +4 -9
  187. data/lib/chef/provider/package/rpm.rb +2 -2
  188. data/lib/chef/provider/package/rubygems.rb +9 -41
  189. data/lib/chef/provider/package/yum.rb +12 -19
  190. data/lib/chef/provider/registry_key.rb +156 -0
  191. data/lib/chef/provider/remote_directory.rb +2 -0
  192. data/lib/chef/provider/remote_file.rb +21 -12
  193. data/lib/chef/provider/ruby_block.rb +5 -2
  194. data/lib/chef/provider/service.rb +15 -0
  195. data/lib/chef/provider/service/init.rb +9 -7
  196. data/lib/chef/provider/service/macosx.rb +15 -73
  197. data/lib/chef/provider/service/simple.rb +1 -1
  198. data/lib/chef/provider/service/solaris.rb +3 -3
  199. data/lib/chef/provider/template.rb +22 -25
  200. data/lib/chef/provider/template_finder.rb +61 -0
  201. data/lib/chef/provider/user.rb +0 -1
  202. data/lib/chef/provider/user/dscl.rb +175 -568
  203. data/lib/chef/provider/user/useradd.rb +30 -47
  204. data/lib/chef/providers.rb +3 -2
  205. data/lib/chef/recipe.rb +14 -8
  206. data/lib/chef/resource.rb +13 -154
  207. data/lib/chef/resource/group.rb +1 -11
  208. data/lib/chef/resource/http_request.rb +2 -1
  209. data/lib/chef/resource/lwrp_base.rb +127 -0
  210. data/lib/chef/resource/mount.rb +10 -11
  211. data/lib/chef/resource/registry_key.rb +86 -0
  212. data/lib/chef/resource/remote_directory.rb +6 -5
  213. data/lib/chef/resource/remote_file.rb +22 -31
  214. data/lib/chef/resource/ruby_block.rb +2 -2
  215. data/lib/chef/resource/service.rb +14 -0
  216. data/lib/chef/resource/user.rb +0 -18
  217. data/lib/chef/resource_collection.rb +25 -21
  218. data/lib/chef/resources.rb +2 -1
  219. data/lib/chef/rest.rb +50 -131
  220. data/lib/chef/rest/auth_credentials.rb +4 -20
  221. data/lib/chef/rest/rest_request.rb +2 -7
  222. data/lib/chef/role.rb +1 -97
  223. data/lib/chef/run_context.rb +108 -130
  224. data/lib/chef/run_context/cookbook_compiler.rb +280 -0
  225. data/lib/chef/run_list.rb +0 -2
  226. data/lib/chef/run_list/run_list_expansion.rb +0 -15
  227. data/lib/chef/run_lock.rb +90 -0
  228. data/lib/chef/runner.rb +28 -5
  229. data/lib/chef/sandbox.rb +15 -148
  230. data/lib/chef/scan_access_control.rb +2 -4
  231. data/lib/chef/shef/ext.rb +3 -575
  232. data/lib/chef/{shef.rb → shell.rb} +35 -40
  233. data/lib/chef/shell/ext.rb +593 -0
  234. data/lib/chef/{shef → shell}/model_wrapper.rb +3 -3
  235. data/lib/chef/{shef/shef_rest.rb → shell/shell_rest.rb} +4 -4
  236. data/lib/chef/{shef/shef_session.rb → shell/shell_session.rb} +17 -15
  237. data/lib/chef/shell_out.rb +7 -0
  238. data/lib/chef/util/windows/net_group.rb +1 -5
  239. data/lib/chef/version.rb +3 -3
  240. data/lib/chef/win32/api/process.rb +0 -1
  241. data/lib/chef/win32/handle.rb +1 -8
  242. data/lib/chef/win32/registry.rb +371 -0
  243. data/spec/data/big_json.json +1 -2
  244. data/spec/data/big_json_plus_one.json +1 -2
  245. data/spec/data/cookbooks/openldap/attributes/default.rb +10 -9
  246. data/spec/data/cookbooks/openldap/attributes/smokey.rb +1 -1
  247. data/spec/data/lwrp/providers/inline_compiler.rb +26 -0
  248. data/spec/data/nodes/default.rb +3 -3
  249. data/spec/data/nodes/test.example.com.rb +3 -3
  250. data/spec/data/nodes/test.rb +3 -3
  251. data/spec/data/partial_one.erb +1 -0
  252. data/spec/data/run_context/cookbooks/circular-dep1/attributes/default.rb +4 -0
  253. data/spec/data/run_context/cookbooks/circular-dep1/definitions/circular_dep1_res.rb +1 -0
  254. data/spec/data/run_context/cookbooks/circular-dep1/libraries/lib.rb +2 -0
  255. data/spec/data/run_context/cookbooks/circular-dep1/metadata.rb +2 -0
  256. data/spec/data/run_context/cookbooks/circular-dep1/providers/provider.rb +1 -0
  257. data/spec/data/{knife-home/.chef/plugins/knife/example_home_subcommand.rb → run_context/cookbooks/circular-dep1/recipes/default.rb} +0 -0
  258. data/spec/data/run_context/cookbooks/circular-dep1/resources/resource.rb +1 -0
  259. data/spec/data/run_context/cookbooks/circular-dep2/attributes/default.rb +3 -0
  260. data/spec/data/run_context/cookbooks/circular-dep2/definitions/circular_dep2_res.rb +1 -0
  261. data/spec/data/run_context/cookbooks/circular-dep2/libraries/lib.rb +2 -0
  262. data/spec/data/run_context/cookbooks/circular-dep2/metadata.rb +2 -0
  263. data/spec/data/run_context/cookbooks/circular-dep2/providers/provider.rb +1 -0
  264. data/spec/data/{lwrp_const_scoping/resources/conflict.rb → run_context/cookbooks/circular-dep2/recipes/default.rb} +0 -0
  265. data/spec/data/run_context/cookbooks/circular-dep2/resources/resource.rb +1 -0
  266. data/spec/data/run_context/cookbooks/dependency1/attributes/aa_first.rb +2 -0
  267. data/spec/data/run_context/cookbooks/dependency1/attributes/default.rb +2 -0
  268. data/spec/data/run_context/cookbooks/dependency1/attributes/zz_last.rb +3 -0
  269. data/spec/data/run_context/cookbooks/dependency1/definitions/dependency1_res.rb +1 -0
  270. data/spec/data/run_context/cookbooks/dependency1/libraries/lib.rb +2 -0
  271. data/spec/data/run_context/cookbooks/dependency1/providers/provider.rb +1 -0
  272. data/spec/data/run_context/cookbooks/dependency1/recipes/default.rb +0 -0
  273. data/spec/data/run_context/cookbooks/dependency1/resources/resource.rb +1 -0
  274. data/spec/data/run_context/cookbooks/dependency2/attributes/default.rb +3 -0
  275. data/spec/data/run_context/cookbooks/dependency2/definitions/dependency2_res.rb +1 -0
  276. data/spec/data/run_context/cookbooks/dependency2/libraries/lib.rb +2 -0
  277. data/spec/data/run_context/cookbooks/dependency2/providers/provider.rb +1 -0
  278. data/spec/data/run_context/cookbooks/dependency2/recipes/default.rb +0 -0
  279. data/spec/data/run_context/cookbooks/dependency2/resources/resource.rb +1 -0
  280. data/spec/data/run_context/cookbooks/no-default-attr/attributes/server.rb +3 -0
  281. data/spec/data/run_context/cookbooks/no-default-attr/definitions/no_default-attr_res.rb +1 -0
  282. data/spec/data/run_context/cookbooks/no-default-attr/providers/provider.rb +1 -0
  283. data/spec/data/run_context/cookbooks/no-default-attr/recipes/default.rb +0 -0
  284. data/spec/data/run_context/cookbooks/no-default-attr/resources/resource.rb +1 -0
  285. data/spec/data/run_context/cookbooks/test-with-circular-deps/attributes/default.rb +3 -0
  286. data/spec/data/run_context/cookbooks/test-with-circular-deps/definitions/test_with-circular-deps_res.rb +1 -0
  287. data/spec/data/run_context/cookbooks/test-with-circular-deps/libraries/lib.rb +2 -0
  288. data/spec/data/run_context/cookbooks/test-with-circular-deps/metadata.rb +2 -0
  289. data/spec/data/run_context/cookbooks/test-with-circular-deps/providers/provider.rb +1 -0
  290. data/spec/data/run_context/cookbooks/test-with-circular-deps/recipes/default.rb +0 -0
  291. data/spec/data/run_context/cookbooks/test-with-circular-deps/resources/resource.rb +1 -0
  292. data/spec/data/run_context/cookbooks/test-with-deps/attributes/default.rb +3 -0
  293. data/spec/data/run_context/cookbooks/test-with-deps/definitions/test_with-deps_res.rb +1 -0
  294. data/spec/data/run_context/cookbooks/test-with-deps/libraries/lib.rb +1 -0
  295. data/spec/data/run_context/cookbooks/test-with-deps/metadata.rb +3 -0
  296. data/spec/data/run_context/cookbooks/test-with-deps/providers/provider.rb +1 -0
  297. data/spec/data/run_context/cookbooks/test-with-deps/recipes/default.rb +0 -0
  298. data/spec/data/run_context/cookbooks/test-with-deps/recipes/server.rb +0 -0
  299. data/spec/data/run_context/cookbooks/test-with-deps/resources/resource.rb +1 -0
  300. data/spec/data/run_context/cookbooks/test/attributes/default.rb +0 -0
  301. data/spec/data/run_context/cookbooks/test/attributes/george.rb +1 -1
  302. data/spec/data/run_context/cookbooks/test/definitions/test_res.rb +1 -0
  303. data/spec/data/run_context/cookbooks/test/providers/provider.rb +1 -0
  304. data/spec/data/run_context/cookbooks/test/resources/resource.rb +1 -0
  305. data/spec/data/shef-config.rb +7 -0
  306. data/spec/functional/dsl/registry_helper_spec.rb +63 -0
  307. data/spec/functional/knife/cookbook_delete_spec.rb +1 -1
  308. data/spec/functional/knife/exec_spec.rb +2 -2
  309. data/spec/functional/knife/ssh_spec.rb +5 -1
  310. data/spec/functional/resource/cookbook_file_spec.rb +7 -19
  311. data/spec/functional/resource/directory_spec.rb +4 -0
  312. data/spec/functional/resource/file_spec.rb +56 -22
  313. data/spec/functional/resource/link_spec.rb +2 -0
  314. data/spec/functional/resource/registry_spec.rb +576 -0
  315. data/spec/functional/resource/remote_directory_spec.rb +142 -36
  316. data/spec/functional/resource/remote_file_spec.rb +18 -0
  317. data/spec/functional/resource/template_spec.rb +23 -2
  318. data/spec/functional/run_lock_spec.rb +106 -0
  319. data/spec/functional/shell_spec.rb +100 -0
  320. data/spec/functional/win32/registry_helper_spec.rb +632 -0
  321. data/spec/spec_helper.rb +5 -29
  322. data/spec/stress/win32/security_spec.rb +1 -1
  323. data/spec/support/chef_helpers.rb +0 -2
  324. data/spec/support/platform_helpers.rb +8 -15
  325. data/spec/support/shared/functional/directory_resource.rb +84 -22
  326. data/spec/support/shared/functional/file_resource.rb +169 -71
  327. data/spec/support/shared/functional/securable_resource.rb +143 -119
  328. data/spec/support/shared/functional/securable_resource_with_reporting.rb +375 -0
  329. data/spec/support/shared/unit/file_system_support.rb +110 -0
  330. data/spec/support/shared/unit/platform_introspector.rb +162 -0
  331. data/spec/unit/api_client/registration_spec.rb +175 -0
  332. data/spec/unit/api_client_spec.rb +78 -156
  333. data/spec/unit/application/apply.rb +84 -0
  334. data/spec/unit/application/client_spec.rb +1 -37
  335. data/spec/unit/application/knife_spec.rb +5 -0
  336. data/spec/unit/application_spec.rb +57 -2
  337. data/spec/unit/checksum/storage/filesystem_spec.rb +1 -1
  338. data/spec/unit/chef_fs/diff_spec.rb +328 -0
  339. data/spec/unit/chef_fs/file_pattern_spec.rb +526 -0
  340. data/spec/unit/chef_fs/file_system/chef_server_root_dir_spec.rb +237 -0
  341. data/spec/unit/chef_fs/file_system/cookbooks_dir_spec.rb +568 -0
  342. data/spec/unit/chef_fs/file_system/data_bags_dir_spec.rb +220 -0
  343. data/spec/unit/chef_fs/file_system_spec.rb +136 -0
  344. data/spec/unit/client_spec.rb +124 -33
  345. data/spec/unit/config_spec.rb +46 -13
  346. data/spec/unit/cookbook/synchronizer_spec.rb +1 -49
  347. data/spec/unit/cookbook/syntax_check_spec.rb +48 -109
  348. data/spec/unit/cookbook_loader_spec.rb +153 -91
  349. data/spec/unit/cookbook_manifest_spec.rb +81 -81
  350. data/spec/unit/cookbook_spec.rb +3 -20
  351. data/spec/unit/cookbook_version_spec.rb +23 -122
  352. data/spec/unit/daemon_spec.rb +3 -24
  353. data/spec/unit/data_bag_spec.rb +6 -4
  354. data/spec/unit/digester_spec.rb +50 -0
  355. data/spec/unit/dsl/data_query_spec.rb +66 -0
  356. data/spec/unit/dsl/platform_introspection_spec.rb +130 -0
  357. data/spec/unit/dsl/regsitry_helper_spec.rb +55 -0
  358. data/spec/unit/encrypted_data_bag_item_spec.rb +50 -105
  359. data/spec/unit/environment_spec.rb +0 -130
  360. data/spec/unit/exceptions_spec.rb +2 -3
  361. data/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb +3 -3
  362. data/spec/unit/json_compat_spec.rb +15 -7
  363. data/spec/unit/knife/bootstrap_spec.rb +2 -0
  364. data/spec/unit/knife/configure_spec.rb +20 -14
  365. data/spec/unit/knife/cookbook_metadata_spec.rb +11 -4
  366. data/spec/unit/knife/cookbook_test_spec.rb +1 -0
  367. data/spec/unit/knife/cookbook_upload_spec.rb +43 -8
  368. data/spec/unit/knife/core/bootstrap_context_spec.rb +1 -1
  369. data/spec/unit/knife/core/ui_spec.rb +156 -125
  370. data/spec/unit/knife/data_bag_create_spec.rb +9 -0
  371. data/spec/unit/knife/data_bag_edit_spec.rb +1 -4
  372. data/spec/unit/knife/data_bag_from_file_spec.rb +4 -6
  373. data/spec/unit/knife/data_bag_show_spec.rb +11 -4
  374. data/spec/unit/knife/index_rebuild_spec.rb +96 -33
  375. data/spec/unit/knife/knife_help.rb +7 -7
  376. data/spec/unit/knife/node_edit_spec.rb +6 -33
  377. data/spec/unit/knife/node_run_list_remove_spec.rb +2 -1
  378. data/spec/unit/knife/ssh_spec.rb +12 -15
  379. data/spec/unit/knife/status_spec.rb +2 -2
  380. data/spec/unit/knife_spec.rb +53 -0
  381. data/spec/unit/lwrp_spec.rb +59 -42
  382. data/spec/unit/mixin/checksum_spec.rb +2 -2
  383. data/spec/unit/mixin/deep_merge_spec.rb +101 -799
  384. data/spec/unit/mixin/enforce_ownership_and_permissions_spec.rb +6 -1
  385. data/spec/unit/mixin/params_validate_spec.rb +4 -37
  386. data/spec/unit/mixin/securable_spec.rb +5 -3
  387. data/spec/unit/mixin/template_spec.rb +119 -0
  388. data/spec/unit/node/attribute_spec.rb +195 -173
  389. data/spec/unit/node/immutable_collections_spec.rb +139 -0
  390. data/spec/unit/node_spec.rb +366 -370
  391. data/spec/unit/platform_spec.rb +9 -10
  392. data/spec/unit/provider/breakpoint_spec.rb +8 -8
  393. data/spec/unit/provider/cookbook_file_spec.rb +4 -8
  394. data/spec/unit/provider/directory_spec.rb +96 -64
  395. data/spec/unit/provider/env_spec.rb +2 -2
  396. data/spec/unit/provider/file_spec.rb +48 -39
  397. data/spec/unit/provider/group/dscl_spec.rb +0 -36
  398. data/spec/unit/provider/group/gpasswd_spec.rb +9 -16
  399. data/spec/unit/provider/group/groupadd_spec.rb +4 -3
  400. data/spec/unit/provider/group/groupmod_spec.rb +1 -0
  401. data/spec/unit/provider/group/pw_spec.rb +15 -12
  402. data/spec/unit/provider/group/usermod_spec.rb +6 -21
  403. data/spec/unit/provider/group/windows_spec.rb +8 -0
  404. data/spec/unit/provider/group_spec.rb +6 -28
  405. data/spec/unit/provider/http_request_spec.rb +28 -69
  406. data/spec/unit/provider/ifconfig_spec.rb +2 -2
  407. data/spec/unit/provider/ohai_spec.rb +4 -4
  408. data/spec/unit/provider/package/apt_spec.rb +0 -1
  409. data/spec/unit/provider/package/ips_spec.rb +0 -1
  410. data/spec/unit/provider/package/portage_spec.rb +0 -44
  411. data/spec/unit/provider/package/rpm_spec.rb +0 -12
  412. data/spec/unit/provider/package/rubygems_spec.rb +1 -44
  413. data/spec/unit/provider/package/yum_spec.rb +39 -36
  414. data/spec/unit/provider/package_spec.rb +7 -5
  415. data/spec/unit/provider/registry_key_spec.rb +269 -0
  416. data/spec/unit/provider/remote_directory_spec.rb +7 -3
  417. data/spec/unit/provider/remote_file_spec.rb +36 -0
  418. data/spec/unit/provider/route_spec.rb +4 -3
  419. data/spec/unit/provider/ruby_block_spec.rb +8 -0
  420. data/spec/unit/provider/service/arch_service_spec.rb +5 -5
  421. data/spec/unit/provider/service/debian_service_spec.rb +1 -1
  422. data/spec/unit/provider/service/freebsd_service_spec.rb +5 -5
  423. data/spec/unit/provider/service/init_service_spec.rb +27 -4
  424. data/spec/unit/provider/service/insserv_service_spec.rb +1 -1
  425. data/spec/unit/provider/service/invokercd_service_spec.rb +4 -4
  426. data/spec/unit/provider/service/macosx_spec.rb +11 -66
  427. data/spec/unit/provider/service/redhat_spec.rb +1 -1
  428. data/spec/unit/provider/service/simple_service_spec.rb +3 -3
  429. data/spec/unit/provider/service/upstart_service_spec.rb +9 -9
  430. data/spec/unit/provider/subversion_spec.rb +1 -1
  431. data/spec/unit/provider/template_spec.rb +35 -11
  432. data/spec/unit/provider/user/dscl_spec.rb +285 -681
  433. data/spec/unit/provider/user/useradd_spec.rb +1 -22
  434. data/spec/unit/provider/user_spec.rb +1 -1
  435. data/spec/unit/recipe_spec.rb +10 -8
  436. data/spec/unit/registry_helper_spec.rb +374 -0
  437. data/spec/unit/resource/mount_spec.rb +0 -11
  438. data/spec/unit/resource/registry_key_spec.rb +171 -0
  439. data/spec/unit/resource/remote_file_spec.rb +21 -23
  440. data/spec/unit/resource/ruby_block_spec.rb +7 -3
  441. data/spec/unit/resource/service_spec.rb +11 -0
  442. data/spec/unit/resource_spec.rb +4 -19
  443. data/spec/unit/rest/auth_credentials_spec.rb +2 -19
  444. data/spec/unit/rest_spec.rb +130 -284
  445. data/spec/unit/run_context/cookbook_compiler_spec.rb +181 -0
  446. data/spec/unit/run_context_spec.rb +18 -4
  447. data/spec/unit/run_list_spec.rb +0 -209
  448. data/spec/unit/run_lock_spec.rb +37 -0
  449. data/spec/unit/runner_spec.rb +101 -2
  450. data/spec/unit/scan_access_control_spec.rb +4 -4
  451. data/spec/unit/{shef → shell}/model_wrapper_spec.rb +5 -5
  452. data/spec/unit/{shef/shef_ext_spec.rb → shell/shell_ext_spec.rb} +21 -21
  453. data/spec/unit/{shef/shef_session_spec.rb → shell/shell_session_spec.rb} +14 -69
  454. data/spec/unit/shell_out_spec.rb +18 -0
  455. data/spec/unit/{shef_spec.rb → shell_spec.rb} +20 -20
  456. metadata +275 -234
  457. checksums.yaml +0 -15
  458. data/README.rdoc +0 -177
  459. data/distro/common/html/knife-recipe.1.html +0 -92
  460. data/lib/chef/certificate.rb +0 -161
  461. data/lib/chef/checksum.rb +0 -167
  462. data/lib/chef/checksum_cache.rb +0 -190
  463. data/lib/chef/cookbook_version_selector.rb +0 -168
  464. data/lib/chef/couchdb.rb +0 -246
  465. data/lib/chef/index_queue/amqp_client.rb +0 -116
  466. data/lib/chef/index_queue/consumer.rb +0 -76
  467. data/lib/chef/index_queue/indexable.rb +0 -109
  468. data/lib/chef/knife/bootstrap/ubuntu12.10-gems.erb +0 -60
  469. data/lib/chef/monkey_patches/moneta.rb +0 -50
  470. data/lib/chef/monkey_patches/uri.rb +0 -70
  471. data/lib/chef/openid_registration.rb +0 -187
  472. data/lib/chef/provider/user/solaris.rb +0 -90
  473. data/lib/chef/solr_query.rb +0 -187
  474. data/lib/chef/solr_query/lucene.treetop +0 -150
  475. data/lib/chef/solr_query/lucene_nodes.rb +0 -285
  476. data/lib/chef/solr_query/query_transform.rb +0 -65
  477. data/lib/chef/solr_query/solr_http_request.rb +0 -132
  478. data/lib/chef/webui_user.rb +0 -231
  479. data/spec/data/cookbooks/openldap/files/default/.dotfile +0 -1
  480. data/spec/data/cookbooks/openldap/files/default/.ssh/id_rsa +0 -1
  481. data/spec/data/cookbooks/openldap/files/default/remotedir/.a_dotdir/.a_dotfile_in_a_dotdir +0 -1
  482. data/spec/data/cookbooks/openldap/files/default/remotedir/remotesubdir/.a_dotfile +0 -1
  483. data/spec/data/mac_users/10.7-8.plist.xml +0 -559
  484. data/spec/data/mac_users/10.7-8.shadow.xml +0 -11
  485. data/spec/data/mac_users/10.7.plist.xml +0 -559
  486. data/spec/data/mac_users/10.7.shadow.xml +0 -11
  487. data/spec/data/mac_users/10.8.plist.xml +0 -559
  488. data/spec/data/mac_users/10.8.shadow.xml +0 -21
  489. data/spec/data/mac_users/10.9.plist.xml +0 -560
  490. data/spec/data/mac_users/10.9.shadow.xml +0 -21
  491. data/spec/functional/resource/base.rb +0 -40
  492. data/spec/functional/resource/group_spec.rb +0 -343
  493. data/spec/functional/resource/user/dscl_spec.rb +0 -199
  494. data/spec/unit/certificate_spec.rb +0 -76
  495. data/spec/unit/checksum_cache_spec.rb +0 -209
  496. data/spec/unit/checksum_spec.rb +0 -94
  497. data/spec/unit/couchdb_spec.rb +0 -274
  498. data/spec/unit/index_queue_spec.rb +0 -391
  499. data/spec/unit/mixin/language_spec.rb +0 -305
  500. data/spec/unit/openid_registration_spec.rb +0 -153
  501. data/spec/unit/provider/user/solaris_spec.rb +0 -414
  502. data/spec/unit/provider/whyrun_safe_ruby_block_spec.rb +0 -47
  503. data/spec/unit/solr_query/query_transform_spec.rb +0 -454
  504. data/spec/unit/solr_query/solr_http_request_spec.rb +0 -244
  505. data/spec/unit/solr_query_spec.rb +0 -203
  506. data/spec/unit/webui_user_spec.rb +0 -238
@@ -21,9 +21,9 @@ require 'spec_helper'
21
21
  describe Chef::Provider::Service::Upstart do
22
22
  before(:each) do
23
23
  @node =Chef::Node.new
24
- @node[:name] = 'upstarter'
25
- @node[:platform] = 'ubuntu'
26
- @node[:platform_version] = '9.10'
24
+ @node.name('upstarter')
25
+ @node.automatic_attrs[:platform] = 'ubuntu'
26
+ @node.automatic_attrs[:platform_version] = '9.10'
27
27
 
28
28
  @events = Chef::EventDispatch::Dispatcher.new
29
29
  @run_context = Chef::RunContext.new(@node, {}, @events)
@@ -38,7 +38,7 @@ describe Chef::Provider::Service::Upstart do
38
38
  end
39
39
 
40
40
  it "should return /etc/event.d as the upstart job directory when running on Ubuntu 9.04" do
41
- @node[:platform_version] = '9.04'
41
+ @node.automatic_attrs[:platform_version] = '9.04'
42
42
  #Chef::Platform.stub!(:find_platform_and_version).and_return([ "ubuntu", "9.04" ])
43
43
  @provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context)
44
44
  @provider.instance_variable_get(:@upstart_job_dir).should == "/etc/event.d"
@@ -46,14 +46,14 @@ describe Chef::Provider::Service::Upstart do
46
46
  end
47
47
 
48
48
  it "should return /etc/init as the upstart job directory when running on Ubuntu 9.10" do
49
- @node[:platform_version] = '9.10'
49
+ @node.automatic_attrs[:platform_version] = '9.10'
50
50
  @provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context)
51
51
  @provider.instance_variable_get(:@upstart_job_dir).should == "/etc/init"
52
52
  @provider.instance_variable_get(:@upstart_conf_suffix).should == ".conf"
53
53
  end
54
54
 
55
55
  it "should return /etc/init as the upstart job directory by default" do
56
- @node[:platform_version] = '9000'
56
+ @node.automatic_attrs[:platform_version] = '9000'
57
57
  @provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context)
58
58
  @provider.instance_variable_get(:@upstart_job_dir).should == "/etc/init"
59
59
  @provider.instance_variable_get(:@upstart_conf_suffix).should == ".conf"
@@ -62,7 +62,7 @@ describe Chef::Provider::Service::Upstart do
62
62
 
63
63
  describe "load_current_resource" do
64
64
  before(:each) do
65
- @node[:command] = {:ps => "ps -ax"}
65
+ @node.automatic_attrs[:command] = {:ps => "ps -ax"}
66
66
 
67
67
  @current_resource = Chef::Resource::Service.new("rsyslog")
68
68
  Chef::Resource::Service.stub!(:new).and_return(@current_resource)
@@ -97,7 +97,7 @@ describe Chef::Provider::Service::Upstart do
97
97
  before do
98
98
  end
99
99
 
100
- it "should set running to true if the status command returns 0" do
100
+ it "should set running to true if the the status command returns 0" do
101
101
  @stdout = StringIO.new("rsyslog start/running")
102
102
  @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
103
103
  @provider.load_current_resource
@@ -113,7 +113,7 @@ describe Chef::Provider::Service::Upstart do
113
113
  end
114
114
 
115
115
  describe "when the status command uses the old format" do
116
- it "should set running to true if the status command returns 0" do
116
+ it "should set running to true if the the status command returns 0" do
117
117
  @stdout = StringIO.new("rsyslog (start) running, process 32225")
118
118
  @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
119
119
  @provider.load_current_resource
@@ -63,7 +63,7 @@ describe Chef::Provider::Subversion do
63
63
  "Last Changed Author: codeninja\n" +
64
64
  "Last Changed Rev: 11410\n" + # Last Changed Rev is preferred to Revision
65
65
  "Last Changed Date: 2009-03-25 06:09:56 -0600 (Wed, 25 Mar 2009)\n\n"
66
- ::File.should_receive(:exist?).with("/my/deploy/dir/.svn").and_return(true)
66
+ ::File.should_receive(:exist?).at_least(1).times.with("/my/deploy/dir/.svn").and_return(true)
67
67
  ::File.should_receive(:directory?).with("/my/deploy/dir").and_return(true)
68
68
  ::Dir.should_receive(:chdir).with("/my/deploy/dir").and_yield
69
69
  @stdout.stub!(:string).and_return(example_svn_info)
@@ -26,7 +26,9 @@ describe Chef::Provider::Template do
26
26
  Chef::Cookbook::FileVendor.on_create { |manifest| Chef::Cookbook::FileSystemFileVendor.new(manifest, @cookbook_repo) }
27
27
 
28
28
  @node = Chef::Node.new
29
- @cookbook_collection = Chef::CookbookCollection.new(Chef::CookbookLoader.new(@cookbook_repo))
29
+ cl = Chef::CookbookLoader.new(@cookbook_repo)
30
+ cl.load_cookbooks
31
+ @cookbook_collection = Chef::CookbookCollection.new(cl)
30
32
  @events = Chef::EventDispatch::Dispatcher.new
31
33
  @run_context = Chef::RunContext.new(@node, @cookbook_collection, @events)
32
34
 
@@ -45,16 +47,16 @@ describe Chef::Provider::Template do
45
47
  else
46
48
  Struct::Passwd.new("root", "x", 0, 0, "root", "/root", "/bin/bash")
47
49
  end
48
- group_struct = OpenStruct.new(:name => "root", :passwd => "x", :gid => 0)
50
+ group_struct = mock("Group Ent", :name => "root", :passwd => "x", :gid => 0)
49
51
  Etc.stub!(:getpwuid).and_return(passwd_struct)
50
52
  Etc.stub!(:getgrgid).and_return(group_struct)
51
53
  end
52
54
 
53
55
  describe "when creating the template" do
54
56
 
55
- before do
56
-
57
+ before do
57
58
  end
59
+
58
60
  after do
59
61
  FileUtils.rm(@rendered_file_location) if ::File.exist?(@rendered_file_location)
60
62
  end
@@ -87,7 +89,7 @@ describe Chef::Provider::Template do
87
89
  it "creates the template with the rendered content" do
88
90
  @access_controls.stub!(:requires_changes?).and_return(true)
89
91
  @access_controls.should_receive(:set_all!)
90
- @node[:slappiness] = "a warm gun"
92
+ @node.normal[:slappiness] = "a warm gun"
91
93
  @provider.should_receive(:backup)
92
94
  @provider.run_action(:create)
93
95
  IO.read(@rendered_file_location).should == "slappiness is a warm gun"
@@ -107,12 +109,35 @@ describe Chef::Provider::Template do
107
109
  it "creates the template with the rendered content for the create if missing action" do
108
110
  @access_controls.stub!(:requires_changes?).and_return(true)
109
111
  @access_controls.should_receive(:set_all!)
110
- @node[:slappiness] = "happiness"
112
+ @node.normal[:slappiness] = "happiness"
111
113
  @provider.should_receive(:backup)
112
114
  @provider.run_action(:create_if_missing)
113
115
  IO.read(@rendered_file_location).should == "slappiness is happiness"
114
116
  @resource.should be_updated_by_last_action
115
117
  end
118
+
119
+ context "and no access control settings are set on the resource" do
120
+ context "on a Unix system" do
121
+ before do
122
+ Chef::Platform.stub!(:windows?).and_return(false)
123
+ end
124
+
125
+ it "sets access control metadata on the new resource" do
126
+ @access_controls.stub!(:requires_changes?).and_return(false)
127
+ @access_controls.should_receive(:set_all!)
128
+ @node.normal[:slappiness] = "happiness"
129
+ @provider.should_receive(:backup)
130
+ @provider.run_action(:create)
131
+ IO.read(@rendered_file_location).should == "slappiness is happiness"
132
+ @resource.should be_updated_by_last_action
133
+
134
+ # Veracity of actual data checked in functional tests
135
+ @resource.owner.should be_a_kind_of(String)
136
+ @resource.group.should be_a_kind_of(String)
137
+ @resource.mode.should be_a_kind_of(String)
138
+ end
139
+ end
140
+ end
116
141
  end
117
142
 
118
143
  describe "when the target file has the wrong content" do
@@ -121,7 +146,7 @@ describe Chef::Provider::Template do
121
146
  end
122
147
 
123
148
  it "overwrites the file with the updated content when the create action is run" do
124
- @node[:slappiness] = "a warm gun"
149
+ @node.normal[:slappiness] = "a warm gun"
125
150
  @access_controls.stub!(:requires_changes?).and_return(false)
126
151
  @access_controls.should_receive(:set_all!)
127
152
  @provider.should_receive(:backup)
@@ -144,7 +169,7 @@ describe Chef::Provider::Template do
144
169
  it "doesn't overwrite the file when the create if missing action is run" do
145
170
  @access_controls.stub!(:requires_changes?).and_return(false)
146
171
  @access_controls.should_not_receive(:set_all!)
147
- @node[:slappiness] = "a warm gun"
172
+ @node.normal[:slappiness] = "a warm gun"
148
173
  @provider.should_not_receive(:backup)
149
174
  @provider.run_action(:create_if_missing)
150
175
  IO.read(@rendered_file_location).should == "blargh"
@@ -154,7 +179,6 @@ describe Chef::Provider::Template do
154
179
 
155
180
  describe "when the target has the correct content" do
156
181
  before do
157
- Chef::ChecksumCache.instance.reset!
158
182
  File.open(@rendered_file_location, "w") { |f| f.print "slappiness is a warm gun" }
159
183
  @current_resource.checksum('4ff94a87794ed9aefe88e734df5a66fc8727a179e9496cbd88e3b5ec762a5ee9')
160
184
  @access_controls = mock("access controls")
@@ -162,7 +186,7 @@ describe Chef::Provider::Template do
162
186
  end
163
187
 
164
188
  it "does not backup the original or overwrite it" do
165
- @node[:slappiness] = "a warm gun"
189
+ @node.normal[:slappiness] = "a warm gun"
166
190
  @access_controls.stub!(:requires_changes?).and_return(false)
167
191
  @provider.should_not_receive(:backup)
168
192
  FileUtils.should_not_receive(:mv)
@@ -171,7 +195,7 @@ describe Chef::Provider::Template do
171
195
  end
172
196
 
173
197
  it "does not backup the original or overwrite it on create if missing" do
174
- @node[:slappiness] = "a warm gun"
198
+ @node.normal[:slappiness] = "a warm gun"
175
199
  @access_controls.stub!(:requires_changes?).and_return(false)
176
200
  @provider.should_not_receive(:backup)
177
201
  FileUtils.should_not_receive(:mv)
@@ -6,9 +6,9 @@
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
8
8
  # You may obtain a copy of the License at
9
- #
9
+ #
10
10
  # http://www.apache.org/licenses/LICENSE-2.0
11
- #
11
+ #
12
12
  # Unless required by applicable law or agreed to in writing, software
13
13
  # distributed under the License is distributed on an "AS IS" BASIS,
14
14
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -20,857 +20,461 @@ ShellCmdResult = Struct.new(:stdout, :stderr, :exitstatus)
20
20
 
21
21
  require 'spec_helper'
22
22
  require 'ostruct'
23
- require 'mixlib/shellout'
24
23
 
25
24
  describe Chef::Provider::User::Dscl do
26
- let(:node) {
27
- node = Chef::Node.new
28
- node.stub(:[]).with(:platform_version).and_return(mac_version)
29
- node.stub(:[]).with(:platform).and_return("mac_os_x")
30
- node
31
- }
32
-
33
- let(:events) {
34
- Chef::EventDispatch::Dispatcher.new
35
- }
36
-
37
- let(:run_context) {
38
- Chef::RunContext.new(node, {}, events)
39
- }
40
-
41
- let(:new_resource) {
42
- r = Chef::Resource::User.new("toor")
43
- r.password(password)
44
- r.salt(salt)
45
- r.iterations(iterations)
46
- r
47
- }
48
-
49
- let(:provider) {
50
- Chef::Provider::User::Dscl.new(new_resource, run_context)
51
- }
52
-
53
- let(:mac_version) {
54
- "10.9.1"
55
- }
56
-
57
- let(:password) { nil }
58
- let(:salt) { nil }
59
- let(:iterations) { nil }
60
-
61
- let(:salted_sha512_password) {
62
- "0f543f021c63255e64e121a3585601b8ecfedf6d2\
63
- 705ddac69e682a33db5dbcdb9b56a2520bc8fff63a\
64
- 2ba6b7984c0737ff0b7949455071581f7affcd536d\
65
- 402b6cdb097"
66
- }
67
-
68
- let(:salted_sha512_pbkdf2_password) {
69
- "c734b6e4787c3727bb35e29fdd92b97c\
70
- 1de12df509577a045728255ec7c6c5f5\
71
- c18efa05ed02b682ffa7ebc05119900e\
72
- b1d4880833aa7a190afc13e2bf0936b8\
73
- 20123e8c98f0f9bcac2a629d9163caac\
74
- 9464a8c234f3919082400b4f939bb77b\
75
- c5adbbac718b7eb99463a7b679571e0f\
76
- 1c9fef2ef08d0b9e9c2bcf644eed2ffc"
77
- }
78
-
79
- let(:salted_sha512_pbkdf2_salt) {
80
- "2d942d8364a9ccf2b8e5cb7ed1ff58f78\
81
- e29dbfee7f9db58859144d061fd0058"
82
- }
83
-
84
- let(:salted_sha512_pbkdf2_iterations) {
85
- 25000
86
- }
87
-
88
- let(:vagrant_sha_512) {
89
- "6f75d7190441facc34291ebbea1fc756b242d4f\
90
- e9bcff141bccb84f1979e27e539539aa31f9f7dcc92c0cea959\
91
- ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30"
92
- }
93
-
94
- let(:vagrant_sha_512_pbkdf2) {
95
- "12601a90db17cbf\
96
- 8ba4808e6382fb0d3b9d8a6c1a190477bf680ab21afb\
97
- 6065467136e55cc208a6f74156e3daf20fb13369ef4b\
98
- 7bafa047d80359fb46a48a4adccd548ebb33851b093\
99
- 47cca84341a7f93a27147343f89fb843fb46c0017d2\
100
- 64afa4976baacf941b915bd1ec1ca24c30b3e759e02\
101
- 403e02f59fe7ff5938a7636c"
102
- }
103
-
104
- let(:vagrant_sha_512_pbkdf2_salt) {
105
- "ee954be472fdc60ddf89484781433993625f006af6ec810c08f49a7e413946a1"
106
- }
107
-
108
- let(:vagrant_sha_512_pbkdf2_iterations) {
109
- 34482
110
- }
111
-
25
+ before do
26
+ @node = Chef::Node.new
27
+ @events = Chef::EventDispatch::Dispatcher.new
28
+ @run_context = Chef::RunContext.new(@node, {}, @events)
29
+ @new_resource = Chef::Resource::User.new("toor")
30
+ @provider = Chef::Provider::User::Dscl.new(@new_resource, @run_context)
31
+ end
32
+
112
33
  describe "when shelling out to dscl" do
113
34
  it "should run dscl with the supplied cmd /Path args" do
114
35
  shell_return = ShellCmdResult.new('stdout', 'err', 0)
115
- provider.should_receive(:shell_out).with("dscl . -cmd /Path args").and_return(shell_return)
116
- provider.run_dscl("cmd /Path args").should == 'stdout'
36
+ @provider.should_receive(:shell_out).with("dscl . -cmd /Path args").and_return(shell_return)
37
+ @provider.safe_dscl("cmd /Path args").should == 'stdout'
117
38
  end
118
39
 
119
40
  it "returns an empty string from delete commands" do
120
41
  shell_return = ShellCmdResult.new('out', 'err', 23)
121
- provider.should_receive(:shell_out).with("dscl . -delete /Path args").and_return(shell_return)
122
- provider.run_dscl("delete /Path args").should == ""
42
+ @provider.should_receive(:shell_out).with("dscl . -delete /Path args").and_return(shell_return)
43
+ @provider.safe_dscl("delete /Path args").should == ""
123
44
  end
124
45
 
125
46
  it "should raise an exception for any other command" do
126
47
  shell_return = ShellCmdResult.new('out', 'err', 23)
127
- provider.should_receive(:shell_out).with('dscl . -cmd /Path arguments').and_return(shell_return)
128
- lambda { provider.run_dscl("cmd /Path arguments") }.should raise_error(Chef::Exceptions::DsclCommandFailed)
48
+ @provider.should_receive(:shell_out).with('dscl . -cmd /Path arguments').and_return(shell_return)
49
+ lambda { @provider.safe_dscl("cmd /Path arguments") }.should raise_error(Chef::Exceptions::DsclCommandFailed)
129
50
  end
130
51
 
131
52
  it "raises an exception when dscl reports 'no such key'" do
132
53
  shell_return = ShellCmdResult.new("No such key: ", 'err', 23)
133
- provider.should_receive(:shell_out).with('dscl . -cmd /Path args').and_return(shell_return)
134
- lambda { provider.run_dscl("cmd /Path args") }.should raise_error(Chef::Exceptions::DsclCommandFailed)
54
+ @provider.should_receive(:shell_out).with('dscl . -cmd /Path args').and_return(shell_return)
55
+ lambda { @provider.safe_dscl("cmd /Path args") }.should raise_error(Chef::Exceptions::DsclCommandFailed)
135
56
  end
136
57
 
137
58
  it "raises an exception when dscl reports 'eDSRecordNotFound'" do
138
59
  shell_return = ShellCmdResult.new("<dscl_cmd> DS Error: -14136 (eDSRecordNotFound)", 'err', -14136)
139
- provider.should_receive(:shell_out).with('dscl . -cmd /Path args').and_return(shell_return)
140
- lambda { provider.run_dscl("cmd /Path args") }.should raise_error(Chef::Exceptions::DsclCommandFailed)
60
+ @provider.should_receive(:shell_out).with('dscl . -cmd /Path args').and_return(shell_return)
61
+ lambda { @provider.safe_dscl("cmd /Path args") }.should raise_error(Chef::Exceptions::DsclCommandFailed)
141
62
  end
142
63
  end
143
64
 
144
65
  describe "get_free_uid" do
145
66
  before do
146
- provider.should_receive(:run_dscl).with("list /Users uid").and_return("\nwheel 200\nstaff 201\nbrahms 500\nchopin 501\n")
67
+ @provider.stub!(:safe_dscl).and_return("\nwheel 200\nstaff 201\n")
147
68
  end
148
-
149
- describe "when resource is configured as system" do
150
- before do
151
- new_resource.system(true)
152
- end
153
-
154
- it "should return the first unused uid number on or above 500" do
155
- provider.get_free_uid.should eq(202)
156
- end
69
+
70
+ it "should run safe_dscl with list /Users uid" do
71
+ @provider.should_receive(:safe_dscl).with("list /Users uid")
72
+ @provider.get_free_uid
157
73
  end
158
74
 
159
75
  it "should return the first unused uid number on or above 200" do
160
- provider.get_free_uid.should eq(502)
76
+ @provider.get_free_uid.should == 202
161
77
  end
162
-
78
+
163
79
  it "should raise an exception when the search limit is exhausted" do
164
80
  search_limit = 1
165
- lambda { provider.get_free_uid(search_limit) }.should raise_error(RuntimeError)
81
+ lambda { @provider.get_free_uid(search_limit) }.should raise_error(RuntimeError)
166
82
  end
167
83
  end
168
84
 
169
85
  describe "uid_used?" do
170
- it "should return false if not given any valid uid number" do
171
- provider.uid_used?(nil).should be_false
86
+ before do
87
+ @provider.stub!(:safe_dscl).and_return("\naj 500\n")
172
88
  end
173
89
 
174
- describe "when called with a user id" do
175
- before do
176
- provider.should_receive(:run_dscl).with("list /Users uid").and_return("\naj 500\n")
177
- end
90
+ it "should run safe_dscl with list /Users uid" do
91
+ @provider.should_receive(:safe_dscl).with("list /Users uid")
92
+ @provider.uid_used?(500)
93
+ end
94
+
95
+ it "should return true for a used uid number" do
96
+ @provider.uid_used?(500).should be_true
97
+ end
178
98
 
179
- it "should return true for a used uid number" do
180
- provider.uid_used?(500).should be_true
181
- end
99
+ it "should return false for an unused uid number" do
100
+ @provider.uid_used?(501).should be_false
101
+ end
182
102
 
183
- it "should return false for an unused uid number" do
184
- provider.uid_used?(501).should be_false
185
- end
103
+ it "should return false if not given any valid uid number" do
104
+ @provider.uid_used?(nil).should be_false
186
105
  end
187
106
  end
188
107
 
189
108
  describe "when determining the uid to set" do
190
109
  it "raises RequestedUIDUnavailable if the requested uid is already in use" do
191
- provider.stub(:uid_used?).and_return(true)
192
- provider.should_receive(:get_free_uid).and_return(501)
193
- lambda { provider.dscl_set_uid }.should raise_error(Chef::Exceptions::RequestedUIDUnavailable)
110
+ @provider.stub!(:uid_used?).and_return(true)
111
+ @provider.should_receive(:get_free_uid).and_return(501)
112
+ lambda { @provider.set_uid }.should raise_error(Chef::Exceptions::RequestedUIDUnavailable)
194
113
  end
195
-
114
+
196
115
  it "finds a valid, unused uid when none is specified" do
197
- provider.should_receive(:run_dscl).with("list /Users uid").and_return('')
198
- provider.should_receive(:run_dscl).with("create /Users/toor UniqueID 501")
199
- provider.should_receive(:get_free_uid).and_return(501)
200
- provider.dscl_set_uid
201
- new_resource.uid.should eq(501)
116
+ @provider.should_receive(:safe_dscl).with("list /Users uid").and_return('')
117
+ @provider.should_receive(:safe_dscl).with("create /Users/toor UniqueID 501")
118
+ @provider.should_receive(:get_free_uid).and_return(501)
119
+ @provider.set_uid
120
+ @new_resource.uid.should == 501
202
121
  end
203
-
122
+
204
123
  it "sets the uid specified in the resource" do
205
- new_resource.uid(1000)
206
- provider.should_receive(:run_dscl).with("create /Users/toor UniqueID 1000").and_return(true)
207
- provider.should_receive(:run_dscl).with("list /Users uid").and_return('')
208
- provider.dscl_set_uid
124
+ @new_resource.uid(1000)
125
+ @provider.should_receive(:safe_dscl).with("create /Users/toor UniqueID 1000").and_return(true)
126
+ @provider.should_receive(:safe_dscl).with("list /Users uid").and_return('')
127
+ @provider.set_uid
209
128
  end
210
129
  end
211
130
 
212
131
  describe "when modifying the home directory" do
213
- let(:current_resource) {
214
- new_resource.dup
215
- }
216
-
217
132
  before do
218
- new_resource.supports({ :manage_home => true })
219
- new_resource.home('/Users/toor')
220
-
221
- provider.current_resource = current_resource
133
+ @new_resource.supports({ :manage_home => true })
134
+ @new_resource.home('/Users/toor')
135
+
136
+ @current_resource = @new_resource.dup
137
+ @provider.current_resource = @current_resource
222
138
  end
223
139
 
224
140
  it "deletes the home directory when resource#home is nil" do
225
- new_resource.instance_variable_set(:@home, nil)
226
- provider.should_receive(:run_dscl).with("delete /Users/toor NFSHomeDirectory").and_return(true)
227
- provider.dscl_set_home
141
+ @new_resource.instance_variable_set(:@home, nil)
142
+ @provider.should_receive(:safe_dscl).with("delete /Users/toor NFSHomeDirectory").and_return(true)
143
+ @provider.modify_home
228
144
  end
229
-
145
+
230
146
 
231
147
  it "raises InvalidHomeDirectory when the resource's home directory doesn't look right" do
232
- new_resource.home('epic-fail')
233
- lambda { provider.dscl_set_home }.should raise_error(Chef::Exceptions::InvalidHomeDirectory)
148
+ @new_resource.home('epic-fail')
149
+ lambda { @provider.modify_home }.should raise_error(Chef::Exceptions::InvalidHomeDirectory)
234
150
  end
235
151
 
236
152
  it "moves the users home to the new location if it exists and the target location is different" do
237
- new_resource.supports(:manage_home => true)
238
-
153
+ @new_resource.supports(:manage_home => true)
154
+
239
155
  current_home = CHEF_SPEC_DATA + '/old_home_dir'
240
156
  current_home_files = [current_home + '/my-dot-emacs', current_home + '/my-dot-vim']
241
- current_resource.home(current_home)
242
- new_resource.gid(23)
243
- ::File.stub(:exists?).with('/old/home/toor').and_return(true)
244
- ::File.stub(:exists?).with('/Users/toor').and_return(true)
245
-
157
+ @current_resource.home(current_home)
158
+ @new_resource.gid(23)
159
+ ::File.stub!(:exists?).with('/old/home/toor').and_return(true)
160
+ ::File.stub!(:exists?).with('/Users/toor').and_return(true)
161
+
246
162
  FileUtils.should_receive(:mkdir_p).with('/Users/toor').and_return(true)
247
163
  FileUtils.should_receive(:rmdir).with(current_home)
248
164
  ::Dir.should_receive(:glob).with("#{CHEF_SPEC_DATA}/old_home_dir/*",::File::FNM_DOTMATCH).and_return(current_home_files)
249
165
  FileUtils.should_receive(:mv).with(current_home_files, "/Users/toor", :force => true)
250
166
  FileUtils.should_receive(:chown_R).with('toor','23','/Users/toor')
251
-
252
- provider.should_receive(:run_dscl).with("create /Users/toor NFSHomeDirectory '/Users/toor'")
253
- provider.dscl_set_home
167
+
168
+ @provider.should_receive(:safe_dscl).with("create /Users/toor NFSHomeDirectory '/Users/toor'")
169
+ @provider.modify_home
254
170
  end
255
171
 
256
172
  it "should raise an exception when the systems user template dir (skel) cannot be found" do
257
- ::File.stub(:exists?).and_return(false,false,false)
258
- lambda { provider.dscl_set_home }.should raise_error(Chef::Exceptions::User)
173
+ ::File.stub!(:exists?).and_return(false,false,false)
174
+ lambda { @provider.modify_home }.should raise_error(Chef::Exceptions::User)
259
175
  end
260
176
 
261
177
  it "should run ditto to copy any missing files from skel to the new home dir" do
262
178
  ::File.should_receive(:exists?).with("/System/Library/User\ Template/English.lproj").and_return(true)
263
179
  FileUtils.should_receive(:chown_R).with('toor', '', '/Users/toor')
264
- provider.should_receive(:shell_out!).with("ditto '/System/Library/User Template/English.lproj' '/Users/toor'")
265
- provider.ditto_home
180
+ @provider.should_receive(:shell_out!).with("ditto '/System/Library/User Template/English.lproj' '/Users/toor'")
181
+ @provider.ditto_home
266
182
  end
267
183
 
268
184
  it "creates the user's NFSHomeDirectory and home directory" do
269
- provider.should_receive(:run_dscl).with("create /Users/toor NFSHomeDirectory '/Users/toor'").and_return(true)
270
- provider.should_receive(:ditto_home)
271
- provider.dscl_set_home
185
+ @provider.should_receive(:safe_dscl).with("create /Users/toor NFSHomeDirectory '/Users/toor'").and_return(true)
186
+ @provider.should_receive(:ditto_home)
187
+ @provider.modify_home
272
188
  end
273
189
  end
274
190
 
275
- describe "resource_requirements" do
276
- let(:dscl_exists) { true }
277
- let(:plutil_exists) { true }
278
-
279
- before do
280
- ::File.stub(:exists?).with("/usr/bin/dscl").and_return(dscl_exists)
281
- ::File.stub(:exists?).with("/usr/bin/plutil").and_return(plutil_exists)
282
- end
283
-
284
- def run_requirements
285
- provider.define_resource_requirements
286
- provider.action = :create
287
- provider.process_resource_requirements
288
- end
289
-
290
- describe "when dscl doesn't exist" do
291
- let(:dscl_exists) { false }
292
-
293
- it "should raise an error" do
294
- lambda { run_requirements }.should raise_error
295
- end
191
+ describe "osx_shadow_hash?" do
192
+ it "should return true when the string is a shadow hash" do
193
+ @provider.osx_shadow_hash?("0"*8*155).should eql(true)
296
194
  end
297
195
 
298
- describe "when plutil doesn't exist" do
299
- let(:plutil_exists) { false }
300
-
301
- it "should raise an error" do
302
- lambda { run_requirements }.should raise_error
303
- end
304
- end
305
-
306
- describe "when on Mac 10.6" do
307
- let(:mac_version) {
308
- "10.6.5"
309
- }
310
-
311
- it "should raise an error" do
312
- lambda { run_requirements }.should raise_error
313
- end
196
+ it "should return false otherwise" do
197
+ @provider.osx_shadow_hash?("any other string").should eql(false)
314
198
  end
199
+ end
315
200
 
316
- describe "when on Mac 10.7" do
317
- let(:mac_version) {
318
- "10.7.5"
319
- }
320
-
321
- describe "when password is SALTED-SHA512" do
322
- let(:password) { salted_sha512_password }
323
-
324
- it "should not raise an error" do
325
- lambda { run_requirements }.should_not raise_error
326
- end
327
- end
328
-
329
- describe "when password is SALTED-SHA512-PBKDF2" do
330
- let(:password) { salted_sha512_pbkdf2_password }
331
-
332
- it "should raise an error" do
333
- lambda { run_requirements }.should raise_error
334
- end
335
- end
201
+ describe "when detecting the format of a password" do
202
+ it "detects a OS X salted sha1" do
203
+ @provider.osx_salted_sha1?("0"*48).should eql(true)
204
+ @provider.osx_salted_sha1?("any other string").should eql(false)
336
205
  end
206
+ end
337
207
 
338
- [ "10.9", "10.10"].each do |version|
339
- describe "when on Mac #{version}" do
340
- let(:mac_version) {
341
- "#{version}.2"
342
- }
343
-
344
- describe "when password is SALTED-SHA512" do
345
- let(:password) { salted_sha512_password }
346
-
347
- it "should raise an error" do
348
- lambda { run_requirements }.should raise_error
349
- end
350
- end
351
-
352
- describe "when password is SALTED-SHA512-PBKDF2" do
353
- let(:password) { salted_sha512_pbkdf2_password }
354
-
355
- describe "when salt and iteration is not set" do
356
- it "should raise an error" do
357
- lambda { run_requirements }.should raise_error
358
- end
359
- end
360
-
361
- describe "when salt and iteration is set" do
362
- let(:salt) { salted_sha512_pbkdf2_salt }
363
- let(:iterations) { salted_sha512_pbkdf2_iterations }
364
-
365
- it "should not raise an error" do
366
- lambda { run_requirements }.should_not raise_error
367
- end
368
- end
369
- end
370
- end
208
+ describe "guid" do
209
+ it "should run safe_dscl with read /Users/user GeneratedUID to get the users GUID" do
210
+ expected_uuid = "b398449e-cee0-45e0-80f8-b0b5b1bfdeaa"
211
+ @provider.should_receive(:safe_dscl).with("read /Users/toor GeneratedUID").and_return(expected_uuid + "\n")
212
+ @provider.guid.should == expected_uuid
371
213
  end
372
214
  end
373
215
 
374
- describe "load_current_resource" do
375
- # set this to any of the user plist files under spec/data
376
- let(:user_plist_file) { nil }
377
-
378
- before do
379
- provider.should_receive(:shell_out).with("dscacheutil '-flushcache'")
380
- provider.should_receive(:shell_out).with("plutil -convert xml1 -o - /var/db/dslocal/nodes/Default/users/toor.plist") do
381
- if user_plist_file.nil?
382
- ShellCmdResult.new('Can not find the file', 'Sorry!!', 1)
383
- else
384
- ShellCmdResult.new(File.read(File.join(CHEF_SPEC_DATA, "mac_users/#{user_plist_file}.plist.xml")), "", 0)
385
- end
386
- end
216
+ describe "shadow_hash_set?" do
387
217
 
388
- if !user_plist_file.nil?
389
- provider.should_receive(:convert_binary_plist_to_xml).and_return(File.read(File.join(CHEF_SPEC_DATA, "mac_users/#{user_plist_file}.shadow.xml")))
390
- end
218
+ it "should run safe_dscl with read /Users/user to see if the AuthenticationAuthority key exists" do
219
+ @provider.should_receive(:safe_dscl).with("read /Users/toor")
220
+ @provider.shadow_hash_set?
391
221
  end
392
222
 
393
- describe "when user is not there" do
394
- it "shouldn't raise an error" do
395
- lambda { provider.load_current_resource }.should_not raise_error
223
+ describe "when the user account has an AuthenticationAuthority key" do
224
+ it "uses the shadow hash when there is a ShadowHash field in the AuthenticationAuthority key" do
225
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("\nAuthenticationAuthority: ;ShadowHash;\n")
226
+ @provider.shadow_hash_set?.should be_true
396
227
  end
397
228
 
398
- it "should set @user_exists" do
399
- provider.load_current_resource
400
- provider.instance_variable_get(:@user_exists).should be_false
229
+ it "does not use the shadow hash when there is no ShadowHash field in the AuthenticationAuthority key" do
230
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("\nAuthenticationAuthority: \n")
231
+ @provider.shadow_hash_set?.should be_false
401
232
  end
402
233
 
403
- it "should set username" do
404
- provider.load_current_resource
405
- provider.current_resource.username.should eq("toor")
406
- end
407
234
  end
408
235
 
409
- describe "when user is there" do
410
- let(:password) { "something" } # Load password during load_current_resource
411
-
412
- describe "on 10.7" do
413
- let(:mac_version) {
414
- "10.7.5"
415
- }
416
-
417
- let(:user_plist_file) { "10.7" }
418
-
419
- it "collects the user data correctly" do
420
- provider.load_current_resource
421
- provider.current_resource.comment.should eq("vagrant")
422
- provider.current_resource.uid.should eq("11112222-3333-4444-AAAA-BBBBCCCCDDDD")
423
- provider.current_resource.gid.should eq("80")
424
- provider.current_resource.home.should eq("/Users/vagrant")
425
- provider.current_resource.shell.should eq("/bin/bash")
426
- provider.current_resource.password.should eq(vagrant_sha_512)
427
- end
428
-
429
- describe "when a plain password is set that is same" do
430
- let(:password) { "vagrant" }
431
-
432
- it "diverged_password? should report false" do
433
- provider.load_current_resource
434
- provider.diverged_password?.should be_false
435
- end
436
- end
437
-
438
- describe "when a plain password is set that is different" do
439
- let(:password) { "not_vagrant" }
440
-
441
- it "diverged_password? should report true" do
442
- provider.load_current_resource
443
- provider.diverged_password?.should be_true
444
- end
445
- end
446
-
447
- describe "when iterations change" do
448
- let(:password) { vagrant_sha_512 }
449
- let(:iterations) { 12345 }
450
-
451
- it "diverged_password? should report false" do
452
- provider.load_current_resource
453
- provider.diverged_password?.should be_false
454
- end
455
- end
456
-
457
- describe "when shadow hash changes" do
458
- let(:password) { salted_sha512_password }
459
-
460
- it "diverged_password? should report true" do
461
- provider.load_current_resource
462
- provider.diverged_password?.should be_true
463
- end
464
- end
465
-
466
- describe "when salt change" do
467
- let(:password) { vagrant_sha_512 }
468
- let(:salt) { "SOMETHINGRANDOM" }
469
-
470
- it "diverged_password? should report false" do
471
- provider.load_current_resource
472
- provider.diverged_password?.should be_false
473
- end
474
- end
475
- end
476
-
477
- describe "on 10.8" do
478
- let(:mac_version) {
479
- "10.8.3"
480
- }
481
-
482
- let(:user_plist_file) { "10.8" }
483
-
484
- it "collects the user data correctly" do
485
- provider.load_current_resource
486
- provider.current_resource.comment.should eq("vagrant")
487
- provider.current_resource.uid.should eq("11112222-3333-4444-AAAA-BBBBCCCCDDDD")
488
- provider.current_resource.gid.should eq("80")
489
- provider.current_resource.home.should eq("/Users/vagrant")
490
- provider.current_resource.shell.should eq("/bin/bash")
491
- provider.current_resource.password.should eq("ea4c2d265d801ba0ec0dfccd\
492
- 253dfc1de91cbe0806b4acc1ed7fe22aebcf6beb5344d0f442e590\
493
- ffa04d679075da3afb119e41b72b5eaf08ee4aa54693722646d5\
494
- 19ee04843deb8a3e977428d33f625e83887913e5c13b70035961\
495
- 5e00ad7bc3e7a0c98afc3e19d1360272454f8d33a9214d2fbe8b\
496
- e68d1f9821b26689312366")
497
- provider.current_resource.salt.should eq("f994ef2f73b7c5594ebd1553300976b20733ce0e24d659783d87f3d81cbbb6a9")
498
- provider.current_resource.iterations.should eq(39840)
499
- end
500
- end
501
-
502
- describe "on 10.7 upgraded to 10.8" do
503
- # In this scenario user password is still in 10.7 format
504
- let(:mac_version) {
505
- "10.8.3"
506
- }
507
-
508
- let(:user_plist_file) { "10.7-8" }
509
-
510
- it "collects the user data correctly" do
511
- provider.load_current_resource
512
- provider.current_resource.comment.should eq("vagrant")
513
- provider.current_resource.uid.should eq("11112222-3333-4444-AAAA-BBBBCCCCDDDD")
514
- provider.current_resource.gid.should eq("80")
515
- provider.current_resource.home.should eq("/Users/vagrant")
516
- provider.current_resource.shell.should eq("/bin/bash")
517
- provider.current_resource.password.should eq("6f75d7190441facc34291ebbea1fc756b242d4f\
518
- e9bcff141bccb84f1979e27e539539aa31f9f7dcc92c0cea959\
519
- ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
520
- end
521
-
522
- describe "when a plain text password is set" do
523
- it "reports password needs to be updated" do
524
- provider.load_current_resource
525
- provider.diverged_password?.should be_true
526
- end
527
- end
528
-
529
- describe "when a salted-sha512-pbkdf2 shadow is set" do
530
- let(:password) { salted_sha512_pbkdf2_password }
531
- let(:salt) { salted_sha512_pbkdf2_salt }
532
- let(:iterations) { salted_sha512_pbkdf2_iterations }
533
-
534
- it "reports password needs to be updated" do
535
- provider.load_current_resource
536
- provider.diverged_password?.should be_true
537
- end
538
- end
539
- end
540
-
541
- describe "on 10.9" do
542
- let(:mac_version) {
543
- "10.9.1"
544
- }
545
-
546
- let(:user_plist_file) { "10.9" }
547
-
548
- it "collects the user data correctly" do
549
- provider.load_current_resource
550
- provider.current_resource.comment.should eq("vagrant")
551
- provider.current_resource.uid.should eq("11112222-3333-4444-AAAA-BBBBCCCCDDDD")
552
- provider.current_resource.gid.should eq("80")
553
- provider.current_resource.home.should eq("/Users/vagrant")
554
- provider.current_resource.shell.should eq("/bin/bash")
555
- provider.current_resource.password.should eq(vagrant_sha_512_pbkdf2)
556
- provider.current_resource.salt.should eq(vagrant_sha_512_pbkdf2_salt)
557
- provider.current_resource.iterations.should eq(vagrant_sha_512_pbkdf2_iterations)
558
- end
559
-
560
- describe "when a plain password is set that is same" do
561
- let(:password) { "vagrant" }
562
-
563
- it "diverged_password? should report false" do
564
- provider.load_current_resource
565
- provider.diverged_password?.should be_false
566
- end
567
- end
568
-
569
- describe "when a plain password is set that is different" do
570
- let(:password) { "not_vagrant" }
571
-
572
- it "diverged_password? should report true" do
573
- provider.load_current_resource
574
- provider.diverged_password?.should be_true
575
- end
576
- end
577
-
578
- describe "when iterations change" do
579
- let(:password) { vagrant_sha_512_pbkdf2 }
580
- let(:salt) { vagrant_sha_512_pbkdf2_salt }
581
- let(:iterations) { 12345 }
582
-
583
- it "diverged_password? should report true" do
584
- provider.load_current_resource
585
- provider.diverged_password?.should be_true
586
- end
587
- end
588
-
589
- describe "when shadow hash changes" do
590
- let(:password) { salted_sha512_pbkdf2_password }
591
- let(:salt) { vagrant_sha_512_pbkdf2_salt }
592
- let(:iterations) { vagrant_sha_512_pbkdf2_iterations }
593
-
594
- it "diverged_password? should report true" do
595
- provider.load_current_resource
596
- provider.diverged_password?.should be_true
597
- end
598
- end
599
-
600
- describe "when salt change" do
601
- let(:password) { vagrant_sha_512_pbkdf2 }
602
- let(:salt) { salted_sha512_pbkdf2_salt }
603
- let(:iterations) { vagrant_sha_512_pbkdf2_iterations }
604
-
605
- it "diverged_password? should report true" do
606
- provider.load_current_resource
607
- provider.diverged_password?.should be_true
608
- end
609
- end
236
+ describe "with no AuthenticationAuthority key in the user account" do
237
+ it "does not use the shadow hash" do
238
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("")
239
+ @provider.shadow_hash_set?.should eql(false)
610
240
  end
611
241
  end
612
242
  end
613
243
 
614
- describe "salted_sha512_pbkdf2?" do
615
- it "should return true when the string is a salted_sha512_pbkdf2 hash" do
616
- provider.salted_sha512_pbkdf2?(salted_sha512_pbkdf2_password).should be_true
617
- end
618
-
619
- it "should return false otherwise" do
620
- provider.salted_sha512_pbkdf2?(salted_sha512_password).should be_false
621
- provider.salted_sha512_pbkdf2?("any other string").should be_false
622
- end
623
- end
624
-
625
- describe "salted_sha512?" do
626
- it "should return true when the string is a salted_sha512_pbkdf2 hash" do
627
- provider.salted_sha512_pbkdf2?(salted_sha512_pbkdf2_password).should be_true
628
- end
629
-
630
- it "should return false otherwise" do
631
- provider.salted_sha512?(salted_sha512_pbkdf2_password).should be_false
632
- provider.salted_sha512?("any other string").should be_false
633
- end
634
- end
635
-
636
- describe "prepare_password_shadow_info" do
637
- describe "when on Mac 10.7" do
638
- let(:mac_version) {
639
- "10.7.1"
640
- }
641
-
642
- describe "when the password is plain text" do
643
- let(:password) { "vagrant" }
644
-
645
- it "password_shadow_info should have salted-sha-512 format" do
646
- shadow_info = provider.prepare_password_shadow_info
647
- shadow_info.should have_key("SALTED-SHA512")
648
- info = shadow_info["SALTED-SHA512"].string.unpack('H*').first
649
- provider.salted_sha512?(info).should be_true
650
- end
651
- end
652
-
653
- describe "when the password is salted-sha-512" do
654
- let(:password) { vagrant_sha_512 }
655
-
656
- it "password_shadow_info should have salted-sha-512 format" do
657
- shadow_info = provider.prepare_password_shadow_info
658
- shadow_info.should have_key("SALTED-SHA512")
659
- info = shadow_info["SALTED-SHA512"].string.unpack('H*').first
660
- provider.salted_sha512?(info).should be_true
661
- info.should eq(vagrant_sha_512)
662
- end
663
- end
244
+ describe "when setting or modifying the user password" do
245
+ before do
246
+ @new_resource.password("password")
247
+ @output = StringIO.new
664
248
  end
665
249
 
666
- ["10.8", "10.9", "10.10"].each do |version|
667
- describe "when on Mac #{version}" do
668
- let(:mac_version) {
669
- "#{version}.1"
670
- }
671
-
672
- describe "when the password is plain text" do
673
- let(:password) { "vagrant" }
674
-
675
- it "password_shadow_info should have salted-sha-512 format" do
676
- shadow_info = provider.prepare_password_shadow_info
677
- shadow_info.should have_key("SALTED-SHA512-PBKDF2")
678
- shadow_info["SALTED-SHA512-PBKDF2"].should have_key("entropy")
679
- shadow_info["SALTED-SHA512-PBKDF2"].should have_key("salt")
680
- shadow_info["SALTED-SHA512-PBKDF2"].should have_key("iterations")
681
- info = shadow_info["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack('H*').first
682
- provider.salted_sha512_pbkdf2?(info).should be_true
683
- end
684
- end
685
-
686
- describe "when the password is salted-sha-512" do
687
- let(:password) { vagrant_sha_512_pbkdf2 }
688
- let(:iterations) { vagrant_sha_512_pbkdf2_iterations }
689
- let(:salt) { vagrant_sha_512_pbkdf2_salt }
690
-
691
- it "password_shadow_info should have salted-sha-512 format" do
692
- shadow_info = provider.prepare_password_shadow_info
693
- shadow_info.should have_key("SALTED-SHA512-PBKDF2")
694
- shadow_info["SALTED-SHA512-PBKDF2"].should have_key("entropy")
695
- shadow_info["SALTED-SHA512-PBKDF2"].should have_key("salt")
696
- shadow_info["SALTED-SHA512-PBKDF2"].should have_key("iterations")
697
- info = shadow_info["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack('H*').first
698
- provider.salted_sha512_pbkdf2?(info).should be_true
699
- info.should eq(vagrant_sha_512_pbkdf2)
700
- end
701
- end
702
- end
250
+ describe "when using a salted sha1 for the password" do
251
+ before do
252
+ @new_resource.password("F"*48)
253
+ end
254
+
255
+ it "should write a shadow hash file with the expected salted sha1" do
256
+ uuid = "B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA"
257
+ File.should_receive(:open).with('/var/db/shadow/hash/B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA', "w", 384).and_yield(@output)
258
+ @provider.should_receive(:safe_dscl).with("read /Users/toor GeneratedUID").and_return(uuid)
259
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("\nAuthenticationAuthority: ;ShadowHash;\n")
260
+ expected_salted_sha1 = @new_resource.password
261
+ expected_shadow_hash = "00000000"*155
262
+ expected_shadow_hash[168] = expected_salted_sha1
263
+ @provider.modify_password
264
+ @output.string.strip.should == expected_shadow_hash
265
+ end
266
+ end
267
+
268
+ describe "when given a shadow hash file for the password" do
269
+ it "should write the shadow hash file directly to /var/db/shadow/hash/GUID" do
270
+ shadow_hash = '0123456789ABCDE0123456789ABCDEF' * 40
271
+ raise 'oops' unless shadow_hash.size == 1240
272
+ @new_resource.password shadow_hash
273
+ uuid = "B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA"
274
+ File.should_receive(:open).with('/var/db/shadow/hash/B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA', "w", 384).and_yield(@output)
275
+ @provider.should_receive(:safe_dscl).with("read /Users/toor GeneratedUID").and_return(uuid)
276
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("\nAuthenticationAuthority: ;ShadowHash;\n")
277
+ @provider.modify_password
278
+ @output.string.strip.should == shadow_hash
279
+ end
280
+ end
281
+
282
+ describe "when given a string for the password" do
283
+ it "should output a salted sha1 and shadow hash file from the specified password" do
284
+ uuid = "B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA"
285
+ File.should_receive(:open).with('/var/db/shadow/hash/B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA', "w", 384).and_yield(@output)
286
+ @new_resource.password("password")
287
+ OpenSSL::Random.stub!(:random_bytes).and_return("\377\377\377\377\377\377\377\377")
288
+ expected_salted_sha1 = "F"*8+"SHA1-"*8
289
+ expected_shadow_hash = "00000000"*155
290
+ expected_shadow_hash[168] = expected_salted_sha1
291
+ @provider.should_receive(:safe_dscl).with("read /Users/toor GeneratedUID").and_return(uuid)
292
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("\nAuthenticationAuthority: ;ShadowHash;\n")
293
+ @provider.modify_password
294
+ @output.string.strip.should match(/^0{168}(FFFFFFFF1C1AA7935D4E1190AFEC92343F31F7671FBF126D)0{1071}$/)
295
+ end
296
+ end
297
+
298
+ it "should write the output directly to the shadow hash file at /var/db/shadow/hash/GUID" do
299
+ shadow_file = StringIO.new
300
+ uuid = "B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA"
301
+ File.should_receive(:open).with("/var/db/shadow/hash/B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA",'w',0600).and_yield(shadow_file)
302
+ @provider.should_receive(:safe_dscl).with("read /Users/toor GeneratedUID").and_return(uuid)
303
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("\nAuthenticationAuthority: ;ShadowHash;\n")
304
+ @provider.modify_password
305
+ shadow_file.string.should match(/^0{168}[0-9A-F]{48}0{1071}$/)
306
+ end
307
+
308
+ it "should run safe_dscl append /Users/user AuthenticationAuthority ;ShadowHash; when no shadow hash set" do
309
+ shadow_file = StringIO.new
310
+ uuid = "B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA"
311
+ File.should_receive(:open).with("/var/db/shadow/hash/B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA",'w',0600).and_yield(shadow_file)
312
+ @provider.should_receive(:safe_dscl).with("read /Users/toor GeneratedUID").and_return(uuid)
313
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("\nAuthenticationAuthority:\n")
314
+ @provider.should_receive(:safe_dscl).with("append /Users/toor AuthenticationAuthority ';ShadowHash;'")
315
+ @provider.modify_password
316
+ shadow_file.string.should match(/^0{168}[0-9A-F]{48}0{1071}$/)
703
317
  end
704
318
  end
705
319
 
706
- describe "set_password" do
707
- before do
708
- new_resource.password("something")
320
+ describe "load_current_resource" do
321
+ it "should raise an error if the required binary /usr/bin/dscl doesn't exist" do
322
+ ::File.should_receive(:exists?).with("/usr/bin/dscl").and_return(false)
323
+ lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::User)
709
324
  end
710
325
 
711
- it "should sleep and flush the dscl cache before saving the password" do
712
- provider.should_receive(:prepare_password_shadow_info).and_return({ })
713
- mock_shellout = double("Mock::Shellout")
714
- mock_shellout.stub(:run_command)
715
- Mixlib::ShellOut.should_receive(:new).and_return(mock_shellout)
716
- provider.should_receive(:read_user_info)
717
- provider.should_receive(:dscl_set)
718
- provider.should_receive(:sleep).with(3)
719
- provider.should_receive(:save_user_info)
720
- provider.set_password
326
+ it "shouldn't raise an error if /usr/bin/dscl exists" do
327
+ ::File.stub!(:exists?).and_return(true)
328
+ lambda { @provider.load_current_resource }.should_not raise_error(Chef::Exceptions::User)
721
329
  end
722
330
  end
723
331
 
724
332
  describe "when the user does not yet exist and chef is creating it" do
725
333
  context "with a numeric gid" do
726
334
  before do
727
- new_resource.comment "#mockssuck"
728
- new_resource.gid 1001
335
+ @new_resource.comment "#mockssuck"
336
+ @new_resource.gid 1001
729
337
  end
730
338
 
731
339
  it "creates the user, comment field, sets uid, gid, configures the home directory, sets the shell, and sets the password" do
732
- provider.should_receive :dscl_create_user
733
- provider.should_receive :dscl_create_comment
734
- provider.should_receive :dscl_set_uid
735
- provider.should_receive :dscl_set_gid
736
- provider.should_receive :dscl_set_home
737
- provider.should_receive :dscl_set_shell
738
- provider.should_receive :set_password
739
- provider.create_user
340
+ @provider.should_receive :dscl_create_user
341
+ @provider.should_receive :dscl_create_comment
342
+ @provider.should_receive :set_uid
343
+ @provider.should_receive :dscl_set_gid
344
+ @provider.should_receive :modify_home
345
+ @provider.should_receive :dscl_set_shell
346
+ @provider.should_receive :modify_password
347
+ @provider.create_user
740
348
  end
741
349
 
742
350
  it "creates the user and sets the comment field" do
743
- provider.should_receive(:run_dscl).with("create /Users/toor").and_return(true)
744
- provider.dscl_create_user
351
+ @provider.should_receive(:safe_dscl).with("create /Users/toor").and_return(true)
352
+ @provider.dscl_create_user
745
353
  end
746
354
 
747
355
  it "sets the comment field" do
748
- provider.should_receive(:run_dscl).with("create /Users/toor RealName '#mockssuck'").and_return(true)
749
- provider.dscl_create_comment
356
+ @provider.should_receive(:safe_dscl).with("create /Users/toor RealName '#mockssuck'").and_return(true)
357
+ @provider.dscl_create_comment
750
358
  end
751
359
 
752
- it "should run run_dscl with create /Users/user PrimaryGroupID to set the users primary group" do
753
- provider.should_receive(:run_dscl).with("create /Users/toor PrimaryGroupID '1001'").and_return(true)
754
- provider.dscl_set_gid
360
+ it "should run safe_dscl with create /Users/user PrimaryGroupID to set the users primary group" do
361
+ @provider.should_receive(:safe_dscl).with("create /Users/toor PrimaryGroupID '1001'").and_return(true)
362
+ @provider.dscl_set_gid
755
363
  end
756
364
 
757
- it "should run run_dscl with create /Users/user UserShell to set the users login shell" do
758
- provider.should_receive(:run_dscl).with("create /Users/toor UserShell '/usr/bin/false'").and_return(true)
759
- provider.dscl_set_shell
365
+ it "should run safe_dscl with create /Users/user UserShell to set the users login shell" do
366
+ @provider.should_receive(:safe_dscl).with("create /Users/toor UserShell '/usr/bin/false'").and_return(true)
367
+ @provider.dscl_set_shell
760
368
  end
761
369
  end
762
370
 
763
371
  context "with a non-numeric gid" do
764
372
  before do
765
- new_resource.comment "#mockssuck"
766
- new_resource.gid "newgroup"
373
+ @new_resource.comment "#mockssuck"
374
+ @new_resource.gid "newgroup"
767
375
  end
768
376
 
769
377
  it "should map the group name to a numeric ID when the group exists" do
770
- provider.should_receive(:run_dscl).with("read /Groups/newgroup PrimaryGroupID").ordered.and_return("PrimaryGroupID: 1001\n")
771
- provider.should_receive(:run_dscl).with("create /Users/toor PrimaryGroupID '1001'").ordered.and_return(true)
772
- provider.dscl_set_gid
378
+ @provider.should_receive(:safe_dscl).with("read /Groups/newgroup PrimaryGroupID").ordered.and_return("PrimaryGroupID: 1001\n")
379
+ @provider.should_receive(:safe_dscl).with("create /Users/toor PrimaryGroupID '1001'").ordered.and_return(true)
380
+ @provider.dscl_set_gid
773
381
  end
774
382
 
775
383
  it "should raise an exception when the group does not exist" do
776
384
  shell_return = ShellCmdResult.new("<dscl_cmd> DS Error: -14136 (eDSRecordNotFound)", 'err', -14136)
777
- provider.should_receive(:shell_out).with('dscl . -read /Groups/newgroup PrimaryGroupID').and_return(shell_return)
778
- lambda { provider.dscl_set_gid }.should raise_error(Chef::Exceptions::GroupIDNotFound)
385
+ @provider.should_receive(:shell_out).with('dscl . -read /Groups/newgroup PrimaryGroupID').and_return(shell_return)
386
+ lambda { @provider.dscl_set_gid }.should raise_error(Chef::Exceptions::GroupIDNotFound)
779
387
  end
780
388
  end
781
389
  end
782
390
 
783
391
  describe "when the user exists and chef is managing it" do
784
392
  before do
785
- current_resource = new_resource.dup
786
- provider.current_resource = current_resource
787
-
788
- # These are all different from current_resource
789
- new_resource.username "mud"
790
- new_resource.uid 2342
791
- new_resource.gid 2342
792
- new_resource.home '/Users/death'
793
- new_resource.password 'goaway'
794
- end
795
-
393
+ @current_resource = @new_resource.dup
394
+ @provider.current_resource = @current_resource
395
+
396
+ # These are all different from @current_resource
397
+ @new_resource.username "mud"
398
+ @new_resource.uid 2342
399
+ @new_resource.gid 2342
400
+ @new_resource.home '/Users/death'
401
+ @new_resource.password 'goaway'
402
+ end
403
+
796
404
  it "sets the user, comment field, uid, gid, moves the home directory, sets the shell, and sets the password" do
797
- provider.should_receive :dscl_create_user
798
- provider.should_receive :dscl_create_comment
799
- provider.should_receive :dscl_set_uid
800
- provider.should_receive :dscl_set_gid
801
- provider.should_receive :dscl_set_home
802
- provider.should_receive :dscl_set_shell
803
- provider.should_receive :set_password
804
- provider.create_user
405
+ @provider.should_receive :dscl_create_user
406
+ @provider.should_receive :dscl_create_comment
407
+ @provider.should_receive :set_uid
408
+ @provider.should_receive :dscl_set_gid
409
+ @provider.should_receive :modify_home
410
+ @provider.should_receive :dscl_set_shell
411
+ @provider.should_receive :modify_password
412
+ @provider.create_user
805
413
  end
806
414
  end
807
415
 
808
416
  describe "when changing the gid" do
809
417
  before do
810
- current_resource = new_resource.dup
811
- provider.current_resource = current_resource
812
-
813
- # This is different from current_resource
814
- new_resource.gid 2342
418
+ @current_resource = @new_resource.dup
419
+ @provider.current_resource = @current_resource
420
+
421
+ # This is different from @current_resource
422
+ @new_resource.gid 2342
815
423
  end
816
-
424
+
817
425
  it "sets the gid" do
818
- provider.should_receive :dscl_set_gid
819
- provider.manage_user
426
+ @provider.should_receive :dscl_set_gid
427
+ @provider.manage_user
820
428
  end
821
429
  end
822
430
 
823
- describe "when the user exists" do
824
- before do
825
- provider.should_receive(:shell_out).with("dscacheutil '-flushcache'")
826
- provider.should_receive(:shell_out).with("plutil -convert xml1 -o - /var/db/dslocal/nodes/Default/users/toor.plist") do
827
- ShellCmdResult.new(File.read(File.join(CHEF_SPEC_DATA, "mac_users/10.9.plist.xml")), "", 0)
828
- end
829
- provider.load_current_resource
830
- end
831
-
832
- describe "when Chef is removing the user" do
833
- it "removes the user from the groups and deletes home directory when the resource is configured to manage home" do
834
- new_resource.supports({ :manage_home => true })
835
- provider.should_receive(:run_dscl).with("list /Groups").and_return("my_group\nyour_group\nreal_group\n")
836
- provider.should_receive(:run_dscl).with("read /Groups/my_group").and_raise(Chef::Exceptions::DsclCommandFailed) # Empty group
837
- provider.should_receive(:run_dscl).with("read /Groups/your_group").and_return("GroupMembership: not_you")
838
- provider.should_receive(:run_dscl).with("read /Groups/real_group").and_return("GroupMembership: toor")
839
- provider.should_receive(:run_dscl).with("delete /Groups/real_group GroupMembership 'toor'")
840
- provider.should_receive(:run_dscl).with("delete /Users/toor")
841
- FileUtils.should_receive(:rm_rf).with("/Users/vagrant")
842
- provider.remove_user
843
- end
431
+ describe "when the user exists and chef is removing it" do
432
+ it "removes the user's home directory when the resource is configured to manage home" do
433
+ @new_resource.supports({ :manage_home => true })
434
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("NFSHomeDirectory: /Users/fuuuuuuuuuuuuu")
435
+ @provider.should_receive(:safe_dscl).with("delete /Users/toor")
436
+ FileUtils.should_receive(:rm_rf).with("/Users/fuuuuuuuuuuuuu")
437
+ @provider.remove_user
438
+ end
439
+
440
+ it "removes the user from any group memberships" do
441
+ Etc.stub(:group).and_yield(OpenStruct.new(:name => 'ragefisters', :mem => 'toor'))
442
+ @provider.should_receive(:safe_dscl).with("delete /Users/toor")
443
+ @provider.should_receive(:safe_dscl).with("delete /Groups/ragefisters GroupMembership 'toor'")
444
+ @provider.remove_user
844
445
  end
446
+ end
845
447
 
846
- describe "when user is not locked" do
847
- it "determines the user as not locked" do
848
- provider.should_not be_locked
849
- end
850
- end
448
+ describe "when discovering if a user is locked" do
851
449
 
852
- describe "when user is locked" do
853
- before do
854
- auth_authority = provider.instance_variable_get(:@authentication_authority)
855
- provider.instance_variable_set(:@authentication_authority, auth_authority + ";DisabledUser;")
856
- end
450
+ it "determines the user is not locked when dscl shows an AuthenticationAuthority without a DisabledUser field" do
451
+ @provider.should_receive(:safe_dscl).with("read /Users/toor")
452
+ @provider.should_not be_locked
453
+ end
857
454
 
858
- it "determines the user as not locked" do
859
- provider.should be_locked
860
- end
455
+ it "determines the user is locked when dscl shows an AuthenticationAuthority with a DisabledUser field" do
456
+ @provider.should_receive(:safe_dscl).with('read /Users/toor').and_return("\nAuthenticationAuthority: ;DisabledUser;\n")
457
+ @provider.should be_locked
458
+ end
861
459
 
862
- it "can unlock the user" do
863
- provider.should_receive(:run_dscl).with("create /Users/toor AuthenticationAuthority ';ShadowHash;HASHLIST:<SALTED-SHA512-PBKDF2>'")
864
- provider.unlock_user
865
- end
460
+ it "determines the user is not locked when dscl shows no AuthenticationAuthority" do
461
+ @provider.should_receive(:safe_dscl).with('read /Users/toor').and_return("\n")
462
+ @provider.should_not be_locked
866
463
  end
867
464
  end
868
465
 
869
466
  describe "when locking the user" do
870
- it "should run run_dscl with append /Users/user AuthenticationAuthority ;DisabledUser; to lock the user account" do
871
- provider.should_receive(:run_dscl).with("append /Users/toor AuthenticationAuthority ';DisabledUser;'")
872
- provider.lock_user
467
+ it "should run safe_dscl with append /Users/user AuthenticationAuthority ;DisabledUser; to lock the user account" do
468
+ @provider.should_receive(:safe_dscl).with("append /Users/toor AuthenticationAuthority ';DisabledUser;'")
469
+ @provider.lock_user
873
470
  end
874
471
  end
875
472
 
473
+ describe "when unlocking the user" do
474
+ it "removes DisabledUser from the authentication string" do
475
+ @provider.should_receive(:safe_dscl).with("read /Users/toor AuthenticationAuthority").and_return("\nAuthenticationAuthority: ;ShadowHash; ;DisabledUser;\n")
476
+ @provider.should_receive(:safe_dscl).with("create /Users/toor AuthenticationAuthority ';ShadowHash;'")
477
+ @provider.unlock_user
478
+ end
479
+ end
876
480
  end