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.
- data/lib/puppet/provider/service/launchd.rb +59 -18
- data/lib/puppet/provider/user/directoryservice.rb +162 -115
- data/lib/puppet/util/run_mode.rb +2 -6
- data/lib/puppet/version.rb +1 -1
- data/spec/unit/provider/service/launchd_spec.rb +32 -1
- data/spec/unit/provider/user/directoryservice_spec.rb +282 -170
- data/spec/unit/util/run_mode_spec.rb +4 -6
- metadata +4 -4
data/lib/puppet/util/run_mode.rb
CHANGED
@@ -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")),
|
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")),
|
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
|
data/lib/puppet/version.rb
CHANGED
@@ -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'
|
149
|
-
# only one user were installed on the system.
|
150
|
-
# the behavior of all the methods necessary to return a
|
151
|
-
# groups property by controlling the data provided by dscl
|
152
|
-
let(:
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
</
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
<string>;ShadowHash;HASHLIST:<SALTED-SHA512></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><?xml version="1.0" encoding="UTF-8"?>
|
194
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
195
|
-
<plist version="1.0">
|
196
|
-
<dict>
|
197
|
-
<key>failedLoginCount</key>
|
198
|
-
<integer>0</integer>
|
199
|
-
<key>failedLoginTimestamp</key>
|
200
|
-
<date>2001-01-01T00:00:00Z</date>
|
201
|
-
<key>lastLoginTimestamp</key>
|
202
|
-
<date>2001-01-01T00:00:00Z</date>
|
203
|
-
<key>passwordTimestamp</key>
|
204
|
-
<date>2012-08-10T23:53:50Z</date>
|
205
|
-
</dict>
|
206
|
-
</plist>
|
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.
|
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.
|
309
|
-
provider.class.
|
310
|
-
provider.
|
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
|
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
|
-
|
383
|
-
provider.class.
|
384
|
-
provider.class.
|
385
|
-
provider.class.
|
386
|
-
provider.class.
|
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].
|
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].
|
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.
|
471
|
-
provider.class.
|
472
|
-
|
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.
|
527
|
-
provider.class.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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).
|
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(:
|
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(:
|
812
|
-
|
813
|
-
|
814
|
-
|
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(:
|
827
|
-
|
828
|
-
|
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(:
|
836
|
-
|
837
|
-
|
838
|
-
provider.expects(:
|
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(:
|
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
|
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
|
-
|
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
|
-
|
861
|
-
|
862
|
-
|
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
|
-
#
|
875
|
-
#
|
876
|
-
|
877
|
-
|
878
|
-
|
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
|
-
|
882
|
-
provider.
|
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
|
-
|
890
|
-
provider.
|
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(:
|
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
|
|