puppet 3.0.2.rc1 → 3.0.2.rc2

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

Potentially problematic release.


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

@@ -65,11 +65,11 @@ module Puppet
65
65
 
66
66
  class WindowsRunMode < RunMode
67
67
  def conf_dir
68
- which_dir(File.join(windows_common_base("etc")), File.join(*windows_local_base))
68
+ which_dir(File.join(windows_common_base("etc")), "~/.puppet")
69
69
  end
70
70
 
71
71
  def var_dir
72
- which_dir(File.join(windows_common_base("var")), File.join(*windows_local_base("var")))
72
+ which_dir(File.join(windows_common_base("var")), "~/.puppet/var")
73
73
  end
74
74
 
75
75
  private
@@ -77,10 +77,6 @@ module Puppet
77
77
  def windows_common_base(*extra)
78
78
  [Dir::COMMON_APPDATA, "PuppetLabs", "puppet"] + extra
79
79
  end
80
-
81
- def windows_local_base(*extra)
82
- [Dir::LOCAL_APPDATA, "PuppetLabs", "puppet"] + extra
83
- end
84
80
  end
85
81
  end
86
82
  end
@@ -6,7 +6,7 @@
6
6
  # Raketasks and such to set the version based on the output of `git describe`
7
7
  #
8
8
  module Puppet
9
- PUPPETVERSION = '3.0.2-rc1'
9
+ PUPPETVERSION = '3.0.2-rc2'
10
10
 
11
11
  def self.version
12
12
  @puppet_version || PUPPETVERSION
@@ -60,6 +60,7 @@ describe Puppet::Type.type(:service).provider(:launchd) do
60
60
  provider.expects(:get_macosx_version_major).returns("10.6")
61
61
  subject.expects(:plist_from_label).returns([joblabel, {"Disabled" => true}])
62
62
  provider.expects(:read_plist).returns({joblabel => {"Disabled" => false}})
63
+ provider.stubs(:launchd_overrides).returns(launchd_overrides)
63
64
  FileTest.expects(:file?).with(launchd_overrides).returns(true)
64
65
  subject.enabled?.should == :true
65
66
  end
@@ -67,6 +68,7 @@ describe Puppet::Type.type(:service).provider(:launchd) do
67
68
  provider.expects(:get_macosx_version_major).returns("10.6")
68
69
  subject.expects(:plist_from_label).returns([joblabel, {"Disabled" => false}])
69
70
  provider.expects(:read_plist).returns({joblabel => {"Disabled" => true}})
71
+ provider.stubs(:launchd_overrides).returns(launchd_overrides)
70
72
  FileTest.expects(:file?).with(launchd_overrides).returns(true)
71
73
  subject.enabled?.should == :false
72
74
  end
@@ -74,6 +76,7 @@ describe Puppet::Type.type(:service).provider(:launchd) do
74
76
  provider.expects(:get_macosx_version_major).returns("10.6")
75
77
  subject.expects(:plist_from_label).returns([joblabel, {}])
76
78
  provider.expects(:read_plist).returns({})
79
+ provider.stubs(:launchd_overrides).returns(launchd_overrides)
77
80
  FileTest.expects(:file?).with(launchd_overrides).returns(true)
78
81
  subject.enabled?.should == :true
79
82
  end
@@ -92,7 +95,7 @@ describe Puppet::Type.type(:service).provider(:launchd) do
92
95
  subject.expects(:execute).with([:launchctl, :load, joblabel])
93
96
  subject.start
94
97
  end
95
- it "should execute 'launchctl load' once without writing to the plist if the job is enabled" do
98
+ it "should execute 'launchctl load' once without writing to the plist if the job is enabled" do
96
99
  subject.expects(:plist_from_label).returns([joblabel, {}])
97
100
  subject.expects(:enabled?).returns :true
98
101
  subject.expects(:execute).with([:launchctl, :load, joblabel]).once
@@ -188,6 +191,7 @@ describe Puppet::Type.type(:service).provider(:launchd) do
188
191
  resource[:enable] = true
189
192
  provider.expects(:get_macosx_version_major).returns("10.6")
190
193
  provider.expects(:read_plist).returns({})
194
+ provider.stubs(:launchd_overrides).returns(launchd_overrides)
191
195
  Plist::Emit.expects(:save_plist).once
192
196
  subject.enable
193
197
  end
@@ -198,8 +202,35 @@ describe Puppet::Type.type(:service).provider(:launchd) do
198
202
  resource[:enable] = false
199
203
  provider.stubs(:get_macosx_version_major).returns("10.6")
200
204
  provider.stubs(:read_plist).returns({})
205
+ provider.stubs(:launchd_overrides).returns(launchd_overrides)
201
206
  Plist::Emit.expects(:save_plist).once
202
207
  subject.enable
203
208
  end
204
209
  end
210
+
211
+ describe "when encountering malformed plists" do
212
+ let(:plist_without_label) do
213
+ {
214
+ 'LimitLoadToSessionType' => 'Aqua'
215
+ }
216
+ end
217
+ let(:busted_plist_path) { '/Library/LaunchAgents/org.busted.plist' }
218
+
219
+ it "[17624] should warn that the plist in question is being skipped" do
220
+ provider.expects(:launchd_paths).returns(['/Library/LaunchAgents'])
221
+ provider.expects(:return_globbed_list_of_file_paths).with('/Library/LaunchAgents').returns([busted_plist_path])
222
+ provider.expects(:read_plist).with(busted_plist_path).returns(plist_without_label)
223
+ Puppet.expects(:warning).with("The #{busted_plist_path} plist does not contain a 'label' key; Puppet is skipping it")
224
+ provider.jobsearch
225
+ end
226
+
227
+ it "[15929] should skip plists that plutil cannot read" do
228
+ provider.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout',
229
+ busted_plist_path).raises(Puppet::ExecutionFailure, 'boom')
230
+ Puppet.expects(:warning).with("Cannot read file #{busted_plist_path}; " +
231
+ "Puppet is skipping it. \n" +
232
+ "Details: boom")
233
+ provider.read_plist(busted_plist_path)
234
+ end
235
+ end
205
236
  end
@@ -13,6 +13,7 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do
13
13
  end
14
14
  let(:provider) { resource.provider }
15
15
  let(:users_plist_dir) { '/var/db/dslocal/nodes/Default/users' }
16
+ let(:stringio_object) { StringIO.new('new_stringio_object') }
16
17
 
17
18
  # This is the output of doing `dscl -plist . read /Users/<username>` which
18
19
  # will return a hash of keys whose values are all arrays.
@@ -145,98 +146,33 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do
145
146
  24752
146
147
  end
147
148
 
148
- # The below represents output of 'dscl -plist . readall /Users' if
149
- # only one user were installed on the system. This lets us check
150
- # the behavior of all the methods necessary to return a user's
151
- # groups property by controlling the data provided by dscl
152
- let(:testuser_plist) do
153
- '<?xml version="1.0" encoding="UTF-8"?>
154
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
155
- <plist version="1.0">
156
- <array>
157
- <dict>
158
- <key>dsAttrTypeNative:KerberosKeys</key>
159
- <array>
160
- <string>30820157 a1030201 02a08201 4e308201 4a3074a1 2b3029a0 03020112 a1220420 54af3992 1c198bf8 94585a6b 2fba445b c8482228 0dcad666 ea62e038 99e59c45 a2453043 a0030201 03a13c04 3a4c4b44 433a5348 41312e34 33383345 31353244 39443339 34414133 32443133 41453938 46364636 45314645 38443030 46383174 65737475 73657230 64a11b30 19a00302 0111a112 04106375 7d97b2ce ca8343a6 3b0f73d5 1001a245 3043a003 020103a1 3c043a4c 4b44433a 53484131 2e343338 33453135 32443944 33393441 41333244 31334145 39384636 46364531 46453844 30304638 31746573 74757365 72306ca1 233021a0 03020110 a11a0418 67b09be3 5131b670 f8e9265e 62459b4c 19435419 fe918519 a2453043 a0030201 03a13c04 3a4c4b44 433a5348 41312e34 33383345 31353244 39443339 34414133 32443133 41453938 46364636 45314645 38443030 46383174 65737475 736572</string>
161
- </array>
162
- <key>dsAttrTypeNative:ShadowHashData</key>
163
- <array>
164
- <string>62706c69 73743030 d101025d 53414c54 45442d53 48413531 324f1044 7ea7d592 131f57b2 c8f8bdbc ec8d9df1 2128a386 393a4f00 c7619bac 2622a44d 451419d1 1da512d5 915ab98e 39718ac9 4083fe2e fd6bf710 a54d477f 8ff735b1 2587192d 080b1900 00000000 00010100 00000000 00000300 00000000 00000000 00000000 000060</string>
165
- </array>
166
- <key>dsAttrTypeStandard:AppleMetaNodeLocation</key>
167
- <array>
168
- <string>/Local/Default</string>
169
- </array>
170
- <key>dsAttrTypeStandard:AuthenticationAuthority</key>
171
- <array>
172
- <string>;Kerberosv5;;testuser@LKDC:SHA1.4383E152D9D394AA32D13AE98F6F6E1FE8D00F81;LKDC:SHA1.4383E152D9D394AA32D13AE98F6F6E1FE8D00F81</string>
173
- <string>;ShadowHash;HASHLIST:&lt;SALTED-SHA512&gt;</string>
174
- </array>
175
- <key>dsAttrTypeStandard:AuthenticationHint</key>
176
- <array>
177
- <string></string>
178
- </array>
179
- <key>dsAttrTypeStandard:GeneratedUID</key>
180
- <array>
181
- <string>0A7D5B63-3AD4-4CA7-B03E-85876F1D1FB3</string>
182
- </array>
183
- <key>dsAttrTypeStandard:NFSHomeDirectory</key>
184
- <array>
185
- <string>/Users/nonexistant_user</string>
186
- </array>
187
- <key>dsAttrTypeStandard:Password</key>
188
- <array>
189
- <string>********</string>
190
- </array>
191
- <key>dsAttrTypeStandard:PasswordPolicyOptions</key>
192
- <array>
193
- <string>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
194
- &lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
195
- &lt;plist version="1.0"&gt;
196
- &lt;dict&gt;
197
- &lt;key&gt;failedLoginCount&lt;/key&gt;
198
- &lt;integer&gt;0&lt;/integer&gt;
199
- &lt;key&gt;failedLoginTimestamp&lt;/key&gt;
200
- &lt;date&gt;2001-01-01T00:00:00Z&lt;/date&gt;
201
- &lt;key&gt;lastLoginTimestamp&lt;/key&gt;
202
- &lt;date&gt;2001-01-01T00:00:00Z&lt;/date&gt;
203
- &lt;key&gt;passwordTimestamp&lt;/key&gt;
204
- &lt;date&gt;2012-08-10T23:53:50Z&lt;/date&gt;
205
- &lt;/dict&gt;
206
- &lt;/plist&gt;
207
- </string>
208
- </array>
209
- <key>dsAttrTypeStandard:PrimaryGroupID</key>
210
- <array>
211
- <string>22</string>
212
- </array>
213
- <key>dsAttrTypeStandard:RealName</key>
214
- <array>
215
- <string>nonexistant_user</string>
216
- </array>
217
- <key>dsAttrTypeStandard:RecordName</key>
218
- <array>
219
- <string>nonexistant_user</string>
220
- </array>
221
- <key>dsAttrTypeStandard:RecordType</key>
222
- <array>
223
- <string>dsRecTypeStandard:Users</string>
224
- </array>
225
- <key>dsAttrTypeStandard:UniqueID</key>
226
- <array>
227
- <string>1000</string>
228
- </array>
229
- <key>dsAttrTypeStandard:UserShell</key>
230
- <array>
231
- <string>/bin/bash</string>
232
- </array>
233
- </dict>
234
- </array>
235
- </plist>'
149
+ # The below represents output of 'dscl -plist . readall /Users' converted to
150
+ # a native Ruby hash if only one user were installed on the system.
151
+ # This lets us check the behavior of all the methods necessary to return a
152
+ # user's groups property by controlling the data provided by dscl
153
+ let(:testuser_hash) do
154
+ [{"dsAttrTypeStandard:RecordName" =>["nonexistant_user"],
155
+ "dsAttrTypeStandard:UniqueID" =>["1000"],
156
+ "dsAttrTypeStandard:AuthenticationAuthority"=>
157
+ [";Kerberosv5;;testuser@LKDC:SHA1.4383E152D9D394AA32D13AE98F6F6E1FE8D00F81;LKDC:SHA1.4383E152D9D394AA32D13AE98F6F6E1FE8D00F81",
158
+ ";ShadowHash;HASHLIST:<SALTED-SHA512>"],
159
+ "dsAttrTypeStandard:AppleMetaNodeLocation" =>["/Local/Default"],
160
+ "dsAttrTypeStandard:NFSHomeDirectory" =>["/Users/nonexistant_user"],
161
+ "dsAttrTypeStandard:RecordType" =>["dsRecTypeStandard:Users"],
162
+ "dsAttrTypeStandard:RealName" =>["nonexistant_user"],
163
+ "dsAttrTypeStandard:Password" =>["********"],
164
+ "dsAttrTypeStandard:PrimaryGroupID" =>["22"],
165
+ "dsAttrTypeStandard:GeneratedUID" =>["0A7D5B63-3AD4-4CA7-B03E-85876F1D1FB3"],
166
+ "dsAttrTypeStandard:AuthenticationHint" =>[""],
167
+ "dsAttrTypeNative:KerberosKeys" =>
168
+ ["30820157 a1030201 02a08201 4e308201 4a3074a1 2b3029a0 03020112 a1220420 54af3992 1c198bf8 94585a6b 2fba445b c8482228 0dcad666 ea62e038 99e59c45 a2453043 a0030201 03a13c04 3a4c4b44 433a5348 41312e34 33383345 31353244 39443339 34414133 32443133 41453938 46364636 45314645 38443030 46383174 65737475 73657230 64a11b30 19a00302 0111a112 04106375 7d97b2ce ca8343a6 3b0f73d5 1001a245 3043a003 020103a1 3c043a4c 4b44433a 53484131 2e343338 33453135 32443944 33393441 41333244 31334145 39384636 46364531 46453844 30304638 31746573 74757365 72306ca1 233021a0 03020110 a11a0418 67b09be3 5131b670 f8e9265e 62459b4c 19435419 fe918519 a2453043 a0030201 03a13c04 3a4c4b44 433a5348 41312e34 33383345 31353244 39443339 34414133 32443133 41453938 46364636 45314645 38443030 46383174 65737475 736572"],
169
+ "dsAttrTypeStandard:PasswordPolicyOptions" =>
170
+ ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n <plist version=\"1.0\">\n <dict>\n <key>failedLoginCount</key>\n <integer>0</integer>\n <key>failedLoginTimestamp</key>\n <date>2001-01-01T00:00:00Z</date>\n <key>lastLoginTimestamp</key>\n <date>2001-01-01T00:00:00Z</date>\n <key>passwordTimestamp</key>\n <date>2012-08-10T23:53:50Z</date>\n </dict>\n </plist>\n "],
171
+ "dsAttrTypeStandard:UserShell" =>["/bin/bash"],
172
+ "dsAttrTypeNative:ShadowHashData" =>
173
+ ["62706c69 73743030 d101025d 53414c54 45442d53 48413531 324f1044 7ea7d592 131f57b2 c8f8bdbc ec8d9df1 2128a386 393a4f00 c7619bac 2622a44d 451419d1 1da512d5 915ab98e 39718ac9 4083fe2e fd6bf710 a54d477f 8ff735b1 2587192d 080b1900 00000000 00010100 00000000 00000300 00000000 00000000 00000000 000060"]}]
236
174
  end
237
175
 
238
-
239
-
240
176
  # The below represents the result of running Plist.parse_xml on XML
241
177
  # data returned from the `dscl -plist . readall /Groups` command.
242
178
  # (AKA: What the get_list_of_groups method returns)
@@ -300,14 +236,14 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do
300
236
  before :each do
301
237
  # Stub out all calls to dscl with default values from above
302
238
  defaults.each do |key, val|
303
- provider.expects(:merge_attribute_with_dscl).with('Users', username, key, val)
239
+ provider.stubs(:merge_attribute_with_dscl).with('Users', username, key, val)
304
240
  end
305
241
 
306
242
  # Mock the rest of the dscl calls. We can't assume that our Linux
307
243
  # build system will have the dscl binary
308
- provider.expects(:create_new_user).with(username)
309
- provider.class.expects(:get_attribute_from_dscl).with('Users', username, 'GeneratedUID').returns({'dsAttrTypeStandard:GeneratedUID' => ['GUID']})
310
- provider.expects(:next_system_id).returns('1000')
244
+ provider.stubs(:create_new_user).with(username)
245
+ provider.class.stubs(:get_attribute_from_dscl).with('Users', username, 'GeneratedUID').returns({'dsAttrTypeStandard:GeneratedUID' => ['GUID']})
246
+ provider.stubs(:next_system_id).returns('1000')
311
247
  end
312
248
 
313
249
  it 'should not raise any errors when creating a user with default values' do
@@ -337,7 +273,12 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do
337
273
  ['foo', 'bar'].each do |user|
338
274
  provider.class.expects(:generate_attribute_hash).with(user).returns({})
339
275
  end
340
- provider.class.instances.size.should == 2
276
+ instances = provider.class.instances
277
+
278
+ instances.should be_a_kind_of Array
279
+ instances.each do |instance|
280
+ instance.should be_a_kind_of Puppet::Provider
281
+ end
341
282
  end
342
283
  end
343
284
 
@@ -379,20 +320,20 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do
379
320
  end
380
321
 
381
322
  before :each do
382
- Facter.expects(:value).with(:macosx_productversion_major).twice.returns('10.7')
383
- provider.class.expects(:dscl).with('-plist', '.', 'readall', '/Users').returns(testuser_plist)
384
- provider.class.expects(:get_attribute_from_dscl).with('Users', username, 'ShadowHashData').returns(sha512_shadowhashdata_hash).twice
385
- provider.class.expects(:get_list_of_groups).returns(group_plist_hash_guid).twice
386
- provider.class.expects(:convert_binary_to_xml).with(sha512_embedded_bplist).twice.returns(sha512_embedded_bplist_hash)
323
+ provider.class.stubs(:get_os_version).returns('10.7')
324
+ provider.class.stubs(:get_all_users).returns(testuser_hash)
325
+ provider.class.stubs(:get_attribute_from_dscl).with('Users', username, 'ShadowHashData').returns(sha512_shadowhashdata_hash)
326
+ provider.class.stubs(:get_list_of_groups).returns(group_plist_hash_guid)
327
+ provider.class.stubs(:convert_binary_to_xml).with(sha512_embedded_bplist).returns(sha512_embedded_bplist_hash)
387
328
  provider.class.prefetch({})
388
329
  end
389
330
 
390
331
  it 'should return :uid values as a Fixnum' do
391
- provider.class.generate_attribute_hash(user_plist_hash)[:uid].class.should == Fixnum
332
+ provider.class.generate_attribute_hash(user_plist_hash)[:uid].should be_a_kind_of Fixnum
392
333
  end
393
334
 
394
335
  it 'should return :gid values as a Fixnum' do
395
- provider.class.generate_attribute_hash(user_plist_hash)[:gid].class.should == Fixnum
336
+ provider.class.generate_attribute_hash(user_plist_hash)[:gid].should be_a_kind_of Fixnum
396
337
  end
397
338
 
398
339
  it 'should return a hash of resource attributes' do
@@ -467,9 +408,9 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do
467
408
 
468
409
 
469
410
  before :each do
470
- provider.class.expects(:dscl).with('-plist', '.', 'readall', '/Users').returns(testuser_plist)
471
- provider.class.expects(:get_attribute_from_dscl).with('Users', username, 'ShadowHashData').returns([])
472
- Facter.expects(:value).with(:macosx_productversion_major).returns('10.7')
411
+ provider.class.stubs(:get_all_users).returns(testuser_hash)
412
+ provider.class.stubs(:get_attribute_from_dscl).with('Users', username, 'ShadowHashData').returns([])
413
+ provider.class.stubs(:get_os_version).returns('10.7')
473
414
  end
474
415
 
475
416
  it "should return a list of groups if the user's name matches GroupMembership" do
@@ -523,12 +464,12 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do
523
464
  end
524
465
 
525
466
  before :each do
526
- provider.class.expects(:dscl).with('-plist', '.', 'readall', '/Users').returns(testuser_plist)
527
- provider.class.expects(:get_list_of_groups).returns(group_plist_one_two_three)
467
+ provider.class.stubs(:get_all_users).returns(testuser_hash)
468
+ provider.class.stubs(:get_list_of_groups).returns(group_plist_one_two_three)
528
469
  end
529
470
 
530
471
  it 'should call dscl to add necessary groups' do
531
- Facter.expects(:value).with(:macosx_productversion_major).returns('10.7')
472
+ provider.class.expects(:get_os_version).returns('10.7')
532
473
  provider.class.expects(:get_attribute_from_dscl).with('Users', username, 'ShadowHashData').returns([])
533
474
  provider.class.expects(:get_attribute_from_dscl).with('Users', username, 'GeneratedUID').returns({'dsAttrTypeStandard:GeneratedUID' => ['guidnonexistant_user']})
534
475
  provider.expects(:groups).returns('two,three')
@@ -541,7 +482,7 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do
541
482
  #describe how passwords are fetched in 10.5 and 10.6
542
483
  ['10.5', '10.6'].each do |os_ver|
543
484
  it "should call the get_sha1 method on #{os_ver}" do
544
- Facter.expects(:value).with(:macosx_productversion_major).returns(os_ver)
485
+ provider.class.expects(:get_os_version).returns(os_ver)
545
486
  provider.class.expects(:get_attribute_from_dscl).with('Users', username, 'ShadowHashData').returns([])
546
487
  provider.class.expects(:get_sha1).with('0A7D5B63-3AD4-4CA7-B03E-85876F1D1FB3').returns('password')
547
488
  provider.class.prefetch({}).first.password.should == 'password'
@@ -549,14 +490,14 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do
549
490
  end
550
491
 
551
492
  it 'should call the get_salted_sha512 method on 10.7 and return the correct hash' do
552
- Facter.expects(:value).with(:macosx_productversion_major).returns('10.7')
493
+ provider.class.expects(:get_os_version).returns('10.7')
553
494
  provider.class.expects(:convert_binary_to_xml).with(sha512_embedded_bplist).returns(sha512_embedded_bplist_hash)
554
495
  provider.class.expects(:get_attribute_from_dscl).with('Users', username, 'ShadowHashData').returns(sha512_shadowhashdata_hash)
555
496
  provider.class.prefetch({}).first.password.should == sha512_password_hash
556
497
  end
557
498
 
558
499
  it 'should call the get_salted_sha512_pbkdf2 method on 10.8 and return the correct hash' do
559
- Facter.expects(:value).with(:macosx_productversion_major).returns('10.8')
500
+ provider.class.expects(:get_os_version).returns('10.8')
560
501
  provider.class.expects(:get_attribute_from_dscl).with('Users', username,'ShadowHashData').returns(pbkdf2_shadowhashdata_hash)
561
502
  provider.class.expects(:convert_binary_to_xml).with(pbkdf2_embedded_plist).returns(pbkdf2_embedded_bplist_hash)
562
503
  provider.class.prefetch({}).first.password.should == pbkdf2_password_hash
@@ -565,35 +506,38 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do
565
506
  end
566
507
 
567
508
  describe '#password=' do
509
+ before :each do
510
+ provider.stubs(:sleep)
511
+ provider.stubs(:flush_dscl_cache)
512
+ end
513
+
568
514
  ['10.5', '10.6'].each do |os_ver|
569
515
  it "should call write_sha1_hash when setting the password on #{os_ver}" do
570
- Facter.expects(:value).with(:macosx_productversion_major).returns(os_ver)
516
+ provider.class.stubs(:get_os_version).returns(os_ver)
571
517
  provider.expects(:write_sha1_hash).with('password')
572
518
  provider.password = 'password'
573
519
  end
574
520
  end
575
521
 
576
522
  it 'should call write_password_to_users_plist when setting the password on 10.7' do
577
- Facter.expects(:value).with(:macosx_productversion_major).twice.returns('10.7')
523
+ provider.class.stubs(:get_os_version).returns('10.7')
578
524
  provider.expects(:write_password_to_users_plist).with(sha512_password_hash)
579
- provider.expects(:flush_dscl_cache).twice
580
525
  provider.password = sha512_password_hash
581
526
  end
582
527
 
583
528
  it 'should call write_password_to_users_plist when setting the password on 10.8' do
584
- Facter.expects(:value).with(:macosx_productversion_major).twice.returns('10.8')
529
+ provider.class.stubs(:get_os_version).returns('10.8')
585
530
  provider.expects(:write_password_to_users_plist).with(pbkdf2_password_hash)
586
- provider.expects(:flush_dscl_cache).twice
587
531
  provider.password = pbkdf2_password_hash
588
532
  end
589
533
 
590
534
  it "should raise an error on 10.7 if a password hash that doesn't contain 136 characters is passed" do
591
- Facter.expects(:value).with(:macosx_productversion_major).twice.returns('10.7')
535
+ provider.class.stubs(:get_os_version).returns('10.7')
592
536
  expect { provider.password = 'password' }.to raise_error Puppet::Error, /OS X 10\.7 requires a Salted SHA512 hash password of 136 characters\. Please check your password and try again/
593
537
  end
594
538
 
595
539
  it "should raise an error on 10.8 if a password hash that doesn't contain 256 characters is passed" do
596
- Facter.expects(:value).with(:macosx_productversion_major).twice.returns('10.8')
540
+ provider.class.stubs(:get_os_version).returns('10.8')
597
541
  expect { provider.password = 'password' }.to raise_error Puppet::Error, /OS X versions > 10\.7 require a Salted SHA512 PBKDF2 password hash of 256 characters\. Please check your password and try again\./
598
542
  end
599
543
  end
@@ -740,7 +684,7 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do
740
684
  provider.class.get_salted_sha512_pbkdf2('iterations', pbkdf2_embedded_bplist_hash).should == pbkdf2_iterations_value
741
685
  end
742
686
  it "should return a Fixnum value when looking up the PBKDF2 iterations value" do
743
- provider.class.get_salted_sha512_pbkdf2('iterations', pbkdf2_embedded_bplist_hash).class.should == Fixnum
687
+ provider.class.get_salted_sha512_pbkdf2('iterations', pbkdf2_embedded_bplist_hash).should be_a_kind_of Fixnum
744
688
  end
745
689
  it "should raise an error if a field other than 'entropy', 'salt', or 'iterations' is passed" do
746
690
  expect { provider.class.get_salted_sha512_pbkdf2('othervalue', pbkdf2_embedded_bplist_hash) }.to raise_error Puppet::Error, /Puppet has tried to read an incorrect value from the SALTED-SHA512-PBKDF2 hash. Acceptable fields are 'salt', 'entropy', or 'iterations'/
@@ -805,97 +749,114 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do
805
749
  }
806
750
  end
807
751
 
808
- let(:stub_shadowhashdata) { stub('connection') }
752
+ let(:sample_users_plist) do
753
+ {
754
+ "shell" => ["/bin/zsh"],
755
+ "passwd" => ["********"],
756
+ "picture" => ["/Library/User Pictures/Animals/Eagle.tif"],
757
+ "_writers_LinkedIdentity" => ["puppet"], "name"=>["puppet"],
758
+ "home" => ["/Users/puppet"],
759
+ "_writers_UserCertificate" => ["puppet"],
760
+ "_writers_passwd" => ["puppet"],
761
+ "gid" => ["20"],
762
+ "generateduid" => ["DA8A0E67-E9BE-4B4F-B34E-8977BAE0D3D4"],
763
+ "realname" => ["Puppet"],
764
+ "_writers_picture" => ["puppet"],
765
+ "uid" => ["501"],
766
+ "hint" => [""],
767
+ "authentication_authority" => [";ShadowHash;HASHLIST:<SALTED-SHA512>",
768
+ ";Kerberosv5;;puppet@LKDC:S HA1.35580B1D6366D2890A35D430373FF653297F377D;LKDC:SHA1.35580B1D6366D2890A35D430373FF653297F377D"],
769
+ "_writers_realname" => ["puppet"],
770
+ "_writers_hint" => ["puppet"],
771
+ "ShadowHashData" => [StringIO.new('blank')]
772
+ }
773
+ end
809
774
 
810
775
  it 'should call set_salted_sha512 on 10.7 when given a a salted-SHA512 password hash' do
811
- provider.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout', "#{users_plist_dir}/nonexistant_user.plist").returns(sha512_plist_xml)
812
- Facter.expects(:value).with(:macosx_productversion_major).returns('10.7')
813
- # The below line is not as tight as I would like. It would be
814
- # nice to set the expectation using .with and passing the hash
815
- # we're expecting, but there are several StringIO objects that
816
- # report with a hex identifier. Even though the string data
817
- # matches, frequently the hex identifiers vary slightly. I
818
- # feel like the work I'd need to do to keep the StringIO objects
819
- # in sync would result in a test with staged data.
820
- provider.expects(:set_salted_sha512)
821
- provider.class.expects(:convert_binary_to_xml).returns(sha512_embedded_bplist_hash)
776
+ provider.expects(:get_users_plist).returns(sample_users_plist)
777
+ provider.expects(:get_shadow_hash_data).with(sample_users_plist).returns(sha512_shadowhashdata)
778
+ provider.class.expects(:get_os_version).returns('10.7')
779
+ provider.expects(:set_salted_sha512).with(sample_users_plist, sha512_shadowhashdata, sha512_password_hash)
822
780
  provider.write_password_to_users_plist(sha512_password_hash)
823
781
  end
824
782
 
825
783
  it 'should call set_salted_pbkdf2 on 10.8 when given a PBKDF2 password hash' do
826
- provider.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout', "#{users_plist_dir}/nonexistant_user.plist").returns(pbkdf2_plist_xml)
827
- Facter.expects(:value).with(:macosx_productversion_major).returns('10.8')
828
- # See comment in previous test...
829
- provider.expects(:set_salted_pbkdf2)
830
- provider.class.expects(:convert_binary_to_xml).returns(pbkdf2_embedded_bplist_hash)
784
+ provider.expects(:get_users_plist).returns(sample_users_plist)
785
+ provider.expects(:get_shadow_hash_data).with(sample_users_plist).returns(pbkdf2_shadowhashdata)
786
+ provider.class.expects(:get_os_version).returns('10.8')
787
+ provider.expects(:set_salted_pbkdf2).with(sample_users_plist, pbkdf2_shadowhashdata, 'entropy', pbkdf2_password_hash)
831
788
  provider.write_password_to_users_plist(pbkdf2_password_hash)
832
789
  end
833
790
 
834
791
  it "should delete the SALTED-SHA512 key in the shadow_hash_data hash if it exists on a 10.8 system and write_password_to_users_plist has been called to set the user's password" do
835
- provider.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout', "#{users_plist_dir}/nonexistant_user.plist").returns('xml_data')
836
- Plist.expects(:parse_xml).with('xml_data').returns('ruby_hash')
837
- Facter.expects(:value).with(:macosx_productversion_major).returns('10.8')
838
- provider.expects(:get_shadow_hash_data).with('ruby_hash').returns(stub_shadowhashdata)
839
- stub_shadowhashdata.expects(:[]).with('SALTED-SHA512').returns(true)
840
- stub_shadowhashdata.expects(:delete).with('SALTED-SHA512')
841
- provider.expects(:set_salted_pbkdf2).with('ruby_hash', stub_shadowhashdata, 'entropy', pbkdf2_password_hash)
792
+ provider.expects(:get_users_plist).returns('users_plist')
793
+ provider.expects(:get_shadow_hash_data).with('users_plist').returns(sha512_shadowhashdata)
794
+ provider.class.expects(:get_os_version).returns('10.8')
795
+ provider.expects(:set_salted_pbkdf2).with('users_plist', {}, 'entropy', pbkdf2_password_hash)
842
796
  provider.write_password_to_users_plist(pbkdf2_password_hash)
843
797
  end
844
798
  end
845
799
 
846
800
  describe '#set_salted_sha512' do
847
801
  let(:users_plist) { {'ShadowHashData' => [StringIO.new('string_data')] } }
848
- let(:converted_string) { "fqfVkhMfV7LI+L287I2d8SEoo4Y5Ok8Ax2GbrCYipE1FFBnRHaUS1ZFauY45\ncYrJQIP+Lv1r9xClTUd/j/c1sSWHGS0=" }
802
+ let(:sha512_shadow_hash_data) do
803
+ {
804
+ 'SALTED-SHA512' => stringio_object
805
+ }
806
+ end
849
807
 
850
- it 'should set the SALTED-SHA512 password hash for a user in 10.7 and call the write_users_plist_to_disk method to write the plist to disk' do
851
- Hash.expects(:new).never
852
- Base64.expects(:decode64).with(converted_string).returns(sha512_pw_string)
808
+ it 'should set the SALTED-SHA512 password hash for a user in 10.7 and call the set_shadow_hash_data method to write the plist to disk' do
853
809
  provider.class.expects(:convert_xml_to_binary).with(sha512_embedded_bplist_hash).returns(sha512_embedded_bplist)
854
- # Again, here's another test that's loose because of StringIO objects...
855
- provider.expects(:write_users_plist_to_disk)
810
+ provider.expects(:set_shadow_hash_data).with(users_plist, sha512_embedded_bplist)
856
811
  provider.set_salted_sha512(users_plist, sha512_embedded_bplist_hash, sha512_password_hash)
857
812
  end
858
813
 
859
814
  it 'should set the salted-SHA512 password, even if a blank shadow_hash_data hash is passed' do
860
- # The only thing that sets this aside from the previous test is the
861
- # Hash.new call that's expected if a shadow_hash_data argument is
862
- # passed that doesn't have a 'SALTED-SHA512' key.
863
- Hash.expects(:new).returns({})
864
- Base64.expects(:decode64).with(converted_string).returns(sha512_pw_string)
865
- provider.class.expects(:convert_xml_to_binary).returns(sha512_embedded_bplist)
866
- provider.expects(:write_users_plist_to_disk)
815
+ provider.expects(:new_stringio_object).returns(stringio_object)
816
+ provider.class.expects(:convert_xml_to_binary).with(sha512_shadow_hash_data).returns(sha512_embedded_bplist)
817
+ provider.expects(:set_shadow_hash_data).with(users_plist, sha512_embedded_bplist)
867
818
  provider.set_salted_sha512(users_plist, false, sha512_password_hash)
868
819
  end
869
820
  end
870
821
 
871
822
  describe '#set_salted_pbkdf2' do
872
823
  let(:users_plist) { {'ShadowHashData' => [StringIO.new('string_data')] } }
824
+ let(:entropy_shadow_hash_data) do
825
+ {
826
+ 'SALTED-SHA512-PBKDF2' =>
827
+ {
828
+ 'entropy' => stringio_object
829
+ }
830
+ }
831
+ end
873
832
 
874
- # The below are the result of running "[[value].pack("H*")].pack("m").strip"
875
- # where value is a hex string passed by pbkdf2_password_hash and
876
- # pbkdf2_salt_value
877
- let(:converted_pw_string) { "BZCt4Z5pU8E1rocq53YYI1331Gxj3n+aD83yzZ59heS3yoaBASNbYVjgWjCY\nBe5IFLAnpL6cI+wpJryBciaa/7pcmlmF6BCR+miYB20pfx+qdfphdVHvFnHX\nUgBVxKDZe5ucWAWqMiuu282O6cUjgRZTrC6p6cjY8axRmg8rWV4=" }
878
- let(:converted_salt_string) { "k3fEaQihyKwsPkXA1E2orQ/NhexcFNmln/xAydox8Ow=" }
833
+ # This will also catch the edge-case where a 10.6-style user exists on
834
+ # a 10.8 system and Puppet attempts to set a password
835
+ it 'should not fail if shadow_hash_data is not a Hash' do
836
+ provider.expects(:new_stringio_object).returns(stringio_object)
837
+ provider.expects(:base64_decode_string).with(pbkdf2_password_hash).returns('binary_string')
838
+ provider.class.expects(:convert_xml_to_binary).with(entropy_shadow_hash_data).returns('binary_plist')
839
+ provider.expects(:set_shadow_hash_data).with({'passwd' => '********'}, 'binary_plist')
840
+ provider.set_salted_pbkdf2({}, false, 'entropy', pbkdf2_password_hash)
841
+ end
879
842
 
880
843
  it "should set the PBKDF2 password hash when the 'entropy' field is passed with a valid password hash" do
881
- Base64.expects(:decode64).with(converted_pw_string).returns(pbkdf2_pw_string)
882
- provider.class.expects(:convert_xml_to_binary).returns(pbkdf2_embedded_plist)
883
- provider.expects(:write_users_plist_to_disk)
844
+ provider.class.expects(:convert_xml_to_binary).with(pbkdf2_embedded_bplist_hash).returns(pbkdf2_embedded_plist)
845
+ provider.expects(:set_shadow_hash_data).with(users_plist, pbkdf2_embedded_plist)
884
846
  users_plist.expects(:[]=).with('passwd', '********')
885
847
  provider.set_salted_pbkdf2(users_plist, pbkdf2_embedded_bplist_hash, 'entropy', pbkdf2_password_hash)
886
848
  end
887
849
 
888
850
  it "should set the PBKDF2 password hash when the 'salt' field is passed with a valid password hash" do
889
- Base64.expects(:decode64).with(converted_salt_string).returns(pbkdf2_salt_string)
890
- provider.class.expects(:convert_xml_to_binary).returns(pbkdf2_embedded_plist)
891
- provider.expects(:write_users_plist_to_disk)
851
+ provider.class.expects(:convert_xml_to_binary).with(pbkdf2_embedded_bplist_hash).returns(pbkdf2_embedded_plist)
852
+ provider.expects(:set_shadow_hash_data).with(users_plist, pbkdf2_embedded_plist)
892
853
  users_plist.expects(:[]=).with('passwd', '********')
893
854
  provider.set_salted_pbkdf2(users_plist, pbkdf2_embedded_bplist_hash, 'salt', pbkdf2_salt_value)
894
855
  end
895
856
 
896
857
  it "should set the PBKDF2 password hash when the 'iterations' field is passed with a valid password hash" do
897
- provider.class.expects(:convert_xml_to_binary).returns(pbkdf2_embedded_plist)
898
- provider.expects(:write_users_plist_to_disk)
858
+ provider.class.expects(:convert_xml_to_binary).with(pbkdf2_embedded_bplist_hash).returns(pbkdf2_embedded_plist)
859
+ provider.expects(:set_shadow_hash_data).with(users_plist, pbkdf2_embedded_plist)
899
860
  users_plist.expects(:[]=).with('passwd', '********')
900
861
  provider.set_salted_pbkdf2(users_plist, pbkdf2_embedded_bplist_hash, 'iterations', pbkdf2_iterations_value)
901
862
  end
@@ -938,6 +899,157 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do
938
899
  expect { provider.merge_attribute_with_dscl('Users', username, 'GeneratedUID', 'GUID') }.to raise_error Puppet::Error, /Could not set the dscl GeneratedUID key with value: GUID/
939
900
  end
940
901
  end
941
- end
942
902
 
903
+ describe '#get_users_plist' do
904
+ let(:test_plist) do
905
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>shell</key>\n\t<string>/bin/bash</string>\n\t<key>user</key>\n\t<string>puppet</string>\n</dict>\n</plist>\n"
906
+ end
907
+
908
+ let(:test_hash) do
909
+ {
910
+ 'user' => 'puppet',
911
+ 'shell' => '/bin/bash'
912
+ }
913
+ end
914
+
915
+ it 'should convert a plist to a valid Ruby hash' do
916
+ provider.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout', "#{users_plist_dir}/#{username}.plist").returns(test_plist)
917
+ provider.get_users_plist(username).should == test_hash
918
+ end
919
+ end
920
+
921
+ describe '#get_shadow_hash_data' do
922
+ let(:shadow_hash) do
923
+ {
924
+ 'ShadowHashData' => [StringIO.new('test')]
925
+ }
926
+ end
927
+
928
+ let(:no_shadow_hash) do
929
+ {
930
+ 'no' => 'Shadow Hash Data'
931
+ }
932
+ end
933
+
934
+ it 'should return false if the passed users_plist does NOT have a ShadowHashData key' do
935
+ provider.get_shadow_hash_data(no_shadow_hash).should == false
936
+ end
937
+
938
+ it 'should call convert_binary_to_xml() with the contents of the StringIO Object ' +
939
+ 'located in the first element of the array of the ShadowHashData key if the ' +
940
+ 'passed users_plist contains a ShadowHashData key' do
941
+ provider.class.expects(:convert_binary_to_xml).with('test').returns('returnvalue')
942
+ provider.get_shadow_hash_data(shadow_hash).should == 'returnvalue'
943
+ end
944
+ end
945
+
946
+ describe 'self#get_os_version' do
947
+ it 'should call Facter.value(:macosx_productversion_major) ONLY ONCE no matter how ' +
948
+ 'many times get_os_version() is called' do
949
+ Facter.expects(:value).with(:macosx_productversion_major).once.returns('10.8')
950
+ provider.class.get_os_version.should == '10.8'
951
+ provider.class.get_os_version.should == '10.8'
952
+ provider.class.get_os_version.should == '10.8'
953
+ provider.class.get_os_version.should == '10.8'
954
+ end
955
+ end
956
+
957
+ describe '#base64_decode_string' do
958
+ it 'should return a Base64-decoded string appropriate for use in a user\'s plist' do
959
+ provider.base64_decode_string(sha512_password_hash).should == sha512_pw_string
960
+ end
961
+ end
962
+
963
+ describe '(#12833) 10.6-style users on 10.8' do
964
+ # The below represents output of 'dscl -plist . readall /Users'
965
+ # converted to a Ruby hash if only one user were installed on the system.
966
+ # This lets us check the behavior of all the methods necessary to return
967
+ # a user's groups property by controlling the data provided by dscl. The
968
+ # differentiating aspect about this plist is that it's from a 10.6-style
969
+ # user. There's an edge case whereby a user that was created in 10.6, but
970
+ # who hasn't attempted to login to the system until after it's been
971
+ # upgraded to 10.8, will experience errors due to assumptions in Puppet
972
+ # based solely on operatingsystem.
973
+ let(:all_users_hash) do
974
+ [
975
+ {
976
+ "dsAttrTypeNative:_writers_UserCertificate" => ["testuser"],
977
+ "dsAttrTypeStandard:RealName" => ["testuser"],
978
+ "dsAttrTypeStandard:NFSHomeDirectory" => ["/Users/testuser"],
979
+ "dsAttrTypeNative:_writers_realname" => ["testuser"],
980
+ "dsAttrTypeNative:_writers_picture" => ["testuser"],
981
+ "dsAttrTypeStandard:AppleMetaNodeLocation" => ["/Local/Default"],
982
+ "dsAttrTypeStandard:PrimaryGroupID" => ["20"],
983
+ "dsAttrTypeNative:_writers_LinkedIdentity" => ["testuser"],
984
+ "dsAttrTypeStandard:UserShell" => ["/bin/bash"],
985
+ "dsAttrTypeStandard:UniqueID" => ["1234"],
986
+ "dsAttrTypeStandard:RecordName" => ["testuser"],
987
+ "dsAttrTypeStandard:Password" => ["********"],
988
+ "dsAttrTypeNative:_writers_jpegphoto" => ["testuser"],
989
+ "dsAttrTypeNative:_writers_hint" => ["testuser"],
990
+ "dsAttrTypeNative:_writers_passwd" => ["testuser"],
991
+ "dsAttrTypeStandard:RecordType" => ["dsRecTypeStandard:Users"],
992
+ "dsAttrTypeStandard:AuthenticationAuthority" => [
993
+ ";ShadowHash;",
994
+ ";Kerberosv5;;testuser@LKDC:SHA1.48AC4BCFEFE9 D66847B5E7D813BC4B12C5513A07;LKDC:SHA1.48AC4BCFEFE9D66847B5E7D813BC4B12C5513A07;"
995
+ ],
996
+ "dsAttrTypeStandard:GeneratedUID" => ["D1AC2ECC-F177-4B45-8B18-59CF002F97FF"]
997
+ }
998
+ ]
999
+ end
1000
+
1001
+ let(:username) { 'testuser' }
1002
+ let(:user_path) { "/Users/#{username}" }
1003
+ let(:resource) do
1004
+ Puppet::Type.type(:user).new(
1005
+ :name => username,
1006
+ :provider => :directoryservice
1007
+ )
1008
+ end
1009
+ let(:provider) { resource.provider }
1010
+
1011
+ # The below represents the result of get_users_plist on the testuser
1012
+ # account from the 'all_users_hash' helper method. The get_users_plist
1013
+ # method calls the `plutil` binary to do its work, so we want to stub
1014
+ # that out
1015
+ let(:user_plist_hash) do
1016
+ {
1017
+ 'realname' => ['testuser'],
1018
+ 'authentication_authority' => [';ShadowHash;', ';Kerberosv5;;testuser@LKDC:SHA1.48AC4BCFEFE9D66847B5E7D813BC4B12C5513A07;LKDC:SHA1.48AC4BCFEFE9D66847B5E7D813BC4B12C5513A07;'],
1019
+ 'home' => ['/Users/testuser'],
1020
+ '_writers_realname' => ['testuser'],
1021
+ 'passwd' => '********',
1022
+ '_writers_LinkedIdentity' => ['testuser'],
1023
+ '_writers_picture' => ['testuser'],
1024
+ 'gid' => ['20'],
1025
+ '_writers_passwd' => ['testuser'],
1026
+ '_writers_hint' => ['testuser'],
1027
+ '_writers_UserCertificate' => ['testuser'],
1028
+ '_writers_jpegphoto' => ['testuser'],
1029
+ 'shell' => ['/bin/bash'],
1030
+ 'uid' => ['1234'],
1031
+ 'generateduid' => ['D1AC2ECC-F177-4B45-8B18-59CF002F97FF'],
1032
+ 'name' => ['testuser']
1033
+ }
1034
+ end
1035
+
1036
+ before :each do
1037
+ provider.class.stubs(:get_all_users).returns(all_users_hash)
1038
+ provider.class.stubs(:get_list_of_groups).returns(group_plist_hash_guid)
1039
+ provider.class.stubs(:get_attribute_from_dscl).with('Users', 'testuser', 'ShadowHashData').returns({})
1040
+ provider.class.prefetch({})
1041
+ end
1042
+
1043
+ it 'should not raise an error if the password=() method is called on ' +
1044
+ 'a user without a ShadowHashData key in their user\'s plist on OS X ' +
1045
+ 'version 10.8' do
1046
+ provider.class.stubs(:get_os_version).returns('10.8')
1047
+ provider.stubs(:sleep)
1048
+ provider.stubs(:flush_dscl_cache)
1049
+ provider.expects(:get_users_plist).with('testuser').returns(user_plist_hash)
1050
+ provider.expects(:set_salted_pbkdf2).with(user_plist_hash, false, 'entropy', pbkdf2_password_hash)
1051
+ provider.password = pbkdf2_password_hash
1052
+ end
1053
+ end
1054
+ end
943
1055