chef 10.32.2-x86-mingw32 → 10.34.0-x86-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/distro/common/html/chef-client.8.html +4 -4
- data/distro/common/html/chef-expander.8.html +4 -4
- data/distro/common/html/chef-expanderctl.8.html +4 -4
- data/distro/common/html/chef-server-webui.8.html +4 -4
- data/distro/common/html/chef-server.8.html +4 -4
- data/distro/common/html/chef-solo.8.html +4 -4
- data/distro/common/html/chef-solr.8.html +5 -5
- data/distro/common/html/knife-bootstrap.1.html +4 -4
- data/distro/common/html/knife-client.1.html +4 -4
- data/distro/common/html/knife-configure.1.html +4 -4
- data/distro/common/html/knife-cookbook-site.1.html +4 -4
- data/distro/common/html/knife-cookbook.1.html +4 -4
- data/distro/common/html/knife-data-bag.1.html +4 -4
- data/distro/common/html/knife-environment.1.html +4 -4
- data/distro/common/html/knife-exec.1.html +4 -4
- data/distro/common/html/knife-index.1.html +4 -4
- data/distro/common/html/knife-node.1.html +4 -4
- data/distro/common/html/knife-role.1.html +4 -4
- data/distro/common/html/knife-search.1.html +4 -4
- data/distro/common/html/knife-ssh.1.html +4 -4
- data/distro/common/html/knife-status.1.html +4 -4
- data/distro/common/html/knife-tag.1.html +4 -4
- data/distro/common/html/knife.1.html +4 -4
- data/distro/common/html/shef.1.html +4 -4
- data/distro/common/man/man1/knife-bootstrap.1 +1 -1
- data/distro/common/man/man1/knife-client.1 +1 -1
- data/distro/common/man/man1/knife-configure.1 +1 -1
- data/distro/common/man/man1/knife-cookbook-site.1 +1 -1
- data/distro/common/man/man1/knife-cookbook.1 +1 -1
- data/distro/common/man/man1/knife-data-bag.1 +1 -1
- data/distro/common/man/man1/knife-environment.1 +1 -1
- data/distro/common/man/man1/knife-exec.1 +1 -1
- data/distro/common/man/man1/knife-index.1 +1 -1
- data/distro/common/man/man1/knife-node.1 +1 -1
- data/distro/common/man/man1/knife-role.1 +1 -1
- data/distro/common/man/man1/knife-search.1 +1 -1
- data/distro/common/man/man1/knife-ssh.1 +1 -1
- data/distro/common/man/man1/knife-status.1 +1 -1
- data/distro/common/man/man1/knife-tag.1 +1 -1
- data/distro/common/man/man1/knife.1 +1 -1
- data/distro/common/man/man1/shef.1 +1 -1
- data/distro/common/man/man8/chef-client.8 +1 -1
- data/distro/common/man/man8/chef-expander.8 +1 -1
- data/distro/common/man/man8/chef-expanderctl.8 +1 -1
- data/distro/common/man/man8/chef-server-webui.8 +1 -1
- data/distro/common/man/man8/chef-server.8 +1 -1
- data/distro/common/man/man8/chef-solo.8 +1 -1
- data/distro/common/man/man8/chef-solr.8 +1 -1
- data/lib/chef/client.rb +1 -2
- data/lib/chef/exceptions.rb +1 -0
- data/lib/chef/node.rb +1 -1
- data/lib/chef/provider/group/dscl.rb +27 -9
- data/lib/chef/provider/package/rpm.rb +2 -2
- data/lib/chef/provider/service/macosx.rb +5 -1
- data/lib/chef/provider/user/dscl.rb +569 -172
- data/lib/chef/resource/mount.rb +11 -10
- data/lib/chef/resource/user.rb +18 -0
- data/lib/chef/rest/rest_request.rb +1 -0
- data/lib/chef/shef.rb +7 -0
- data/lib/chef/shef/shef_session.rb +4 -4
- data/lib/chef/version.rb +1 -1
- data/spec/data/mac_users/10.7-8.plist.xml +559 -0
- data/spec/data/mac_users/10.7-8.shadow.xml +11 -0
- data/spec/data/mac_users/10.7.plist.xml +559 -0
- data/spec/data/mac_users/10.7.shadow.xml +11 -0
- data/spec/data/mac_users/10.8.plist.xml +559 -0
- data/spec/data/mac_users/10.8.shadow.xml +21 -0
- data/spec/data/mac_users/10.9.plist.xml +560 -0
- data/spec/data/mac_users/10.9.shadow.xml +21 -0
- data/spec/functional/resource/user/dscl_spec.rb +199 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/unit/client_spec.rb +23 -4
- data/spec/unit/provider/group/dscl_spec.rb +35 -0
- data/spec/unit/provider/package/rpm_spec.rb +12 -0
- data/spec/unit/provider/service/macosx_spec.rb +37 -0
- data/spec/unit/provider/user/dscl_spec.rb +705 -284
- data/spec/unit/resource/mount_spec.rb +11 -0
- data/spec/unit/rest/auth_credentials_spec.rb +5 -0
- data/spec/unit/shef/shef_session_spec.rb +44 -2
- metadata +223 -116
- checksums.yaml +0 -7
@@ -0,0 +1,21 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
+
<plist version="1.0">
|
4
|
+
<dict>
|
5
|
+
<key>SALTED-SHA512-PBKDF2</key>
|
6
|
+
<dict>
|
7
|
+
<key>entropy</key>
|
8
|
+
<data>
|
9
|
+
EmAakNsXy/i6SAjmOC+w07nYpsGhkEd79oCrIa+2BlRnE25VzCCKb3QVbj2v
|
10
|
+
IPsTNp70t7r6BH2ANZ+0akikrczVSOuzOFGwk0fMqENBp/k6JxRzQ/ifuEP7
|
11
|
+
RsABfSZK+kl2uqz5QbkVvR7ByiTDCz51ngJAPgL1n+f/WTinY2w=
|
12
|
+
</data>
|
13
|
+
<key>iterations</key>
|
14
|
+
<integer>34482</integer>
|
15
|
+
<key>salt</key>
|
16
|
+
<data>
|
17
|
+
7pVL5HL9xg3fiUhHgUM5k2JfAGr27IEMCPSafkE5RqE=
|
18
|
+
</data>
|
19
|
+
</dict>
|
20
|
+
</dict>
|
21
|
+
</plist>
|
@@ -0,0 +1,199 @@
|
|
1
|
+
#
|
2
|
+
# Copyright:: Copyright (c) 2014 Chef Software, Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
require 'spec_helper'
|
19
|
+
require 'chef/mixin/shell_out'
|
20
|
+
require 'functional/resource/base'
|
21
|
+
|
22
|
+
metadata = {
|
23
|
+
:unix_only => true,
|
24
|
+
:requires_root => true,
|
25
|
+
:provider => {:user => Chef::Provider::User::Dscl}
|
26
|
+
}
|
27
|
+
|
28
|
+
describe "Chef::Resource::User with Chef::Provider::User::Dscl provider", metadata do
|
29
|
+
include Chef::Mixin::ShellOut
|
30
|
+
|
31
|
+
def clean_user
|
32
|
+
begin
|
33
|
+
shell_out!("/usr/bin/dscl . -delete '/Users/#{username}'")
|
34
|
+
rescue Mixlib::ShellOut::ShellCommandFailed
|
35
|
+
# Raised when the user is already cleaned
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def user_should_exist
|
40
|
+
shell_out("/usr/bin/dscl . -ls /Users").stdout.should include username
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_password(pass)
|
44
|
+
# In order to test the password we use dscl passwd command since
|
45
|
+
# that's the only command that gets the user password from CLI.
|
46
|
+
shell_out("dscl . -passwd /Users/greatchef #{pass} new_password").exitstatus.should == 0
|
47
|
+
# Now reset the password back
|
48
|
+
shell_out("dscl . -passwd /Users/greatchef new_password #{pass}").exitstatus.should == 0
|
49
|
+
end
|
50
|
+
|
51
|
+
let(:node) do
|
52
|
+
n = Chef::Node.new
|
53
|
+
n.consume_external_attrs(ohai.data.dup, {})
|
54
|
+
n
|
55
|
+
end
|
56
|
+
|
57
|
+
let(:events) do
|
58
|
+
Chef::EventDispatch::Dispatcher.new
|
59
|
+
end
|
60
|
+
|
61
|
+
let(:run_context) do
|
62
|
+
Chef::RunContext.new(node, {}, events)
|
63
|
+
end
|
64
|
+
|
65
|
+
let(:username) do
|
66
|
+
"greatchef"
|
67
|
+
end
|
68
|
+
|
69
|
+
let(:uid) { nil }
|
70
|
+
let(:gid) { 20 }
|
71
|
+
let(:home) { nil }
|
72
|
+
let(:manage_home) { false }
|
73
|
+
let(:password) { "XXXYYYZZZ" }
|
74
|
+
let(:comment) { "Great Chef" }
|
75
|
+
let(:shell) { "/bin/bash" }
|
76
|
+
let(:salt) { nil }
|
77
|
+
let(:iterations) { nil }
|
78
|
+
|
79
|
+
let(:user_resource) do
|
80
|
+
r = Chef::Resource::User.new("TEST USER RESOURCE", run_context)
|
81
|
+
r.username(username)
|
82
|
+
r.uid(uid)
|
83
|
+
r.gid(gid)
|
84
|
+
r.home(home)
|
85
|
+
r.shell(shell)
|
86
|
+
r.comment(comment)
|
87
|
+
r.manage_home(manage_home)
|
88
|
+
r.password(password)
|
89
|
+
r.salt(salt)
|
90
|
+
r.iterations(iterations)
|
91
|
+
r
|
92
|
+
end
|
93
|
+
|
94
|
+
before do
|
95
|
+
clean_user
|
96
|
+
end
|
97
|
+
|
98
|
+
after(:each) do
|
99
|
+
clean_user
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "action :create" do
|
103
|
+
it "should create the user" do
|
104
|
+
user_resource.run_action(:create)
|
105
|
+
user_should_exist
|
106
|
+
check_password(password)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "when user exists" do
|
111
|
+
before do
|
112
|
+
existing_resource = user_resource.dup
|
113
|
+
existing_resource.run_action(:create)
|
114
|
+
user_should_exist
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "when password is updated" do
|
118
|
+
it "should update the password of the user" do
|
119
|
+
user_resource.password("mykitchen")
|
120
|
+
user_resource.run_action(:create)
|
121
|
+
check_password("mykitchen")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "when password is being set via shadow hash" do
|
127
|
+
let(:password) {
|
128
|
+
if node[:platform_version].start_with?("10.7.")
|
129
|
+
# On Mac 10.7 we only need to set the password
|
130
|
+
"c9b3bd1a0cde797eef0eff16c580dab996ba3a21961cccc\
|
131
|
+
d0f5e65c61558243e50b1a490088bd4824e3b35562d383ca02260398\
|
132
|
+
ef1979b302212ec1c5383d1d05fc8d843"
|
133
|
+
else
|
134
|
+
"c734b6e4787c3727bb35e29fdd92b97c\
|
135
|
+
1de12df509577a045728255ec7c6c5f5\
|
136
|
+
c18efa05ed02b682ffa7ebc05119900e\
|
137
|
+
b1d4880833aa7a190afc13e2bf0936b8\
|
138
|
+
20123e8c98f0f9bcac2a629d9163caac\
|
139
|
+
9464a8c234f3919082400b4f939bb77b\
|
140
|
+
c5adbbac718b7eb99463a7b679571e0f\
|
141
|
+
1c9fef2ef08d0b9e9c2bcf644eed2ffc"
|
142
|
+
end
|
143
|
+
}
|
144
|
+
|
145
|
+
let(:iterations) { 25000 }
|
146
|
+
let(:salt) { "9e2e7d5ee473b496fd24cf0bbfcaedfcb291ee21740e570d1e917e874f8788ca" }
|
147
|
+
|
148
|
+
it "action :create should create the user" do
|
149
|
+
user_resource.run_action(:create)
|
150
|
+
user_should_exist
|
151
|
+
check_password("soawesome")
|
152
|
+
end
|
153
|
+
|
154
|
+
describe "when user exists" do
|
155
|
+
before do
|
156
|
+
existing_resource = user_resource.dup
|
157
|
+
existing_resource.run_action(:create)
|
158
|
+
user_should_exist
|
159
|
+
end
|
160
|
+
|
161
|
+
describe "when password is updated" do
|
162
|
+
it "should update the password of the user" do
|
163
|
+
user_resource.password("mykitchen")
|
164
|
+
user_resource.run_action(:create)
|
165
|
+
check_password("mykitchen")
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "when a user is member of some groups" do
|
172
|
+
let(:groups) { ["staff", "operator"] }
|
173
|
+
|
174
|
+
before do
|
175
|
+
existing_resource = user_resource.dup
|
176
|
+
existing_resource.run_action(:create)
|
177
|
+
|
178
|
+
groups.each do |group|
|
179
|
+
shell_out!("/usr/bin/dscl . -append '/Groups/#{group}' GroupMembership #{username}")
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
after do
|
184
|
+
groups.each do |group|
|
185
|
+
# Do not raise an error when user is correctly removed
|
186
|
+
shell_out("/usr/bin/dscl . -delete '/Groups/#{group}' GroupMembership #{username}")
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
it ":remove action removes the user from the groups and deletes the user"do
|
191
|
+
user_resource.run_action(:remove)
|
192
|
+
groups.each do |group|
|
193
|
+
# Do not raise an error when group is empty
|
194
|
+
shell_out("dscl . read /Groups/staff GroupMembership").stdout.should_not include(group)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -51,6 +51,13 @@ require 'spec/support/local_gems.rb' if File.exists?(File.join(File.dirname(__FI
|
|
51
51
|
# Explicitly require spec helpers that need to load first
|
52
52
|
require 'spec/support/platform_helpers'
|
53
53
|
|
54
|
+
|
55
|
+
OHAI_SYSTEM = Ohai::System.new
|
56
|
+
OHAI_SYSTEM.require_plugin("os")
|
57
|
+
OHAI_SYSTEM.require_plugin("platform")
|
58
|
+
TEST_PLATFORM = OHAI_SYSTEM["platform"].dup.freeze
|
59
|
+
TEST_PLATFORM_VERSION = OHAI_SYSTEM["platform_version"].dup.freeze
|
60
|
+
|
54
61
|
# Autoloads support files
|
55
62
|
# Excludes support/platforms by default
|
56
63
|
# Do not change the gsub.
|
@@ -81,6 +88,22 @@ RSpec.configure do |config|
|
|
81
88
|
config.filter_run_excluding :requires_unprivileged_user => true if ENV['USER'] == 'root'
|
82
89
|
config.filter_run_excluding :uses_diff => true unless has_diff?
|
83
90
|
|
91
|
+
# Functional Resource tests that are provider-specific:
|
92
|
+
# context "on platforms that use useradd", :provider => {:user => Chef::Provider::User::Useradd}} do #...
|
93
|
+
config.filter_run_excluding :provider => lambda {|criteria|
|
94
|
+
type, target_provider = criteria.first
|
95
|
+
|
96
|
+
platform = TEST_PLATFORM.dup
|
97
|
+
platform_version = TEST_PLATFORM_VERSION.dup
|
98
|
+
|
99
|
+
begin
|
100
|
+
provider_for_running_platform = Chef::Platform.find_provider(platform, platform_version, type)
|
101
|
+
provider_for_running_platform != target_provider
|
102
|
+
rescue ArgumentError # no provider for platform
|
103
|
+
true
|
104
|
+
end
|
105
|
+
}
|
106
|
+
|
84
107
|
config.run_all_when_everything_filtered = true
|
85
108
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
86
109
|
end
|
data/spec/unit/client_spec.rb
CHANGED
@@ -92,7 +92,7 @@ shared_examples_for Chef::Client do
|
|
92
92
|
Chef::REST.should_receive(:new).with(Chef::Config[:client_url], Chef::Config[:validation_client_name], Chef::Config[:validation_key]).exactly(1).and_return(mock_chef_rest_for_client)
|
93
93
|
mock_chef_rest_for_client.should_receive(:register).with(@fqdn, Chef::Config[:client_key]).exactly(1).and_return(true)
|
94
94
|
# Client.register will then turn around create another
|
95
|
-
|
95
|
+
|
96
96
|
# Chef::REST object, this time with the client key it got from the
|
97
97
|
# previous step.
|
98
98
|
Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url], @fqdn, Chef::Config[:client_key]).exactly(1).and_return(mock_chef_rest_for_node)
|
@@ -150,7 +150,7 @@ shared_examples_for Chef::Client do
|
|
150
150
|
block.call
|
151
151
|
end
|
152
152
|
end
|
153
|
-
|
153
|
+
|
154
154
|
# This is what we're testing.
|
155
155
|
@client.run
|
156
156
|
|
@@ -159,7 +159,7 @@ shared_examples_for Chef::Client do
|
|
159
159
|
@node.automatic_attrs[:platform_version].should == "example-platform-1.0"
|
160
160
|
end
|
161
161
|
end
|
162
|
-
|
162
|
+
|
163
163
|
describe "when notifying other objects of the status of the chef run" do
|
164
164
|
before do
|
165
165
|
Chef::Client.clear_notifications
|
@@ -232,6 +232,25 @@ shared_examples_for Chef::Client do
|
|
232
232
|
end
|
233
233
|
end
|
234
234
|
|
235
|
+
describe "should set single lettered environments correctly" do
|
236
|
+
before do
|
237
|
+
@original_env = Chef::Config[:environment]
|
238
|
+
end
|
239
|
+
|
240
|
+
after do
|
241
|
+
Chef::Config[:environment] = @original_env
|
242
|
+
end
|
243
|
+
|
244
|
+
it "should set the environment correctly" do
|
245
|
+
@node.chef_environment.should == "_default"
|
246
|
+
Chef::Config[:environment] = "A"
|
247
|
+
|
248
|
+
@client.build_node
|
249
|
+
|
250
|
+
@node.chef_environment.should == "A"
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
235
254
|
describe "when a run list override is provided" do
|
236
255
|
before do
|
237
256
|
@node = Chef::Node.new(@hostname)
|
@@ -264,7 +283,7 @@ shared_examples_for Chef::Client do
|
|
264
283
|
@node.should_receive(:save).and_return(nil)
|
265
284
|
|
266
285
|
@client.build_node
|
267
|
-
|
286
|
+
|
268
287
|
@node[:roles].should_not be_nil
|
269
288
|
@node[:roles].should eql(['test_role'])
|
270
289
|
@node[:recipes].should eql(['cookbook1'])
|
@@ -293,3 +293,38 @@ describe Chef::Provider::Group::Dscl do
|
|
293
293
|
end
|
294
294
|
end
|
295
295
|
end
|
296
|
+
|
297
|
+
describe 'Test DSCL loading' do
|
298
|
+
before do
|
299
|
+
@node = Chef::Node.new
|
300
|
+
@events = Chef::EventDispatch::Dispatcher.new
|
301
|
+
@run_context = Chef::RunContext.new(@node, {}, @events)
|
302
|
+
@new_resource = Chef::Resource::Group.new("aj")
|
303
|
+
@provider = Chef::Provider::Group::Dscl.new(@new_resource, @run_context)
|
304
|
+
@output = <<-EOF
|
305
|
+
AppleMetaNodeLocation: /Local/Default
|
306
|
+
Comment:
|
307
|
+
Test Group
|
308
|
+
GeneratedUID: AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA
|
309
|
+
NestedGroups: AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAB
|
310
|
+
Password: *
|
311
|
+
PrimaryGroupID: 999
|
312
|
+
RealName:
|
313
|
+
TestGroup
|
314
|
+
RecordName: com.apple.aj
|
315
|
+
RecordType: dsRecTypeStandard:Groups
|
316
|
+
GroupMembership: waka bar
|
317
|
+
EOF
|
318
|
+
@provider.stub(:safe_dscl).with("read /Groups/aj").and_return(@output)
|
319
|
+
@current_resource = @provider.load_current_resource
|
320
|
+
end
|
321
|
+
|
322
|
+
it 'should parse gid properly' do
|
323
|
+
File.stub(:exists?).and_return(true)
|
324
|
+
@current_resource.gid.should eq("999")
|
325
|
+
end
|
326
|
+
it 'should parse members properly' do
|
327
|
+
File.stub(:exists?).and_return(true)
|
328
|
+
@current_resource.members.should eq(['waka', 'bar'])
|
329
|
+
end
|
330
|
+
end
|
@@ -80,6 +80,18 @@ describe Chef::Provider::Package::Rpm do
|
|
80
80
|
@provider.stub!(:popen4).and_return(status)
|
81
81
|
lambda { @provider.run_action(:any) }.should raise_error(Chef::Exceptions::Package)
|
82
82
|
end
|
83
|
+
|
84
|
+
it "should not detect the package name as version when not installed" do
|
85
|
+
@status = double("Status", :exitstatus => -1)
|
86
|
+
@stdout = StringIO.new("package openssh-askpass is not installed")
|
87
|
+
@new_resource = Chef::Resource::Package.new("openssh-askpass")
|
88
|
+
@new_resource.source 'openssh-askpass'
|
89
|
+
@provider = Chef::Provider::Package::Rpm.new(@new_resource, @run_context)
|
90
|
+
@provider.should_receive(:popen4).with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' openssh-askpass").and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
|
91
|
+
@provider.should_receive(:popen4).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' openssh-askpass").and_return(@status)
|
92
|
+
@provider.load_current_resource
|
93
|
+
@provider.current_resource.version.should be_nil
|
94
|
+
end
|
83
95
|
end
|
84
96
|
|
85
97
|
describe "after the current resource is loaded" do
|
@@ -58,6 +58,43 @@ XML
|
|
58
58
|
let!(:current_resource) { Chef::Resource::Service.new(service_name) }
|
59
59
|
|
60
60
|
describe "#load_current_resource" do
|
61
|
+
|
62
|
+
# CHEF-5223 "you can't glob for a file that hasn't been converged
|
63
|
+
# onto the node yet."
|
64
|
+
context "when the plist doesn't exist" do
|
65
|
+
|
66
|
+
def run_resource_setup_for_action(action)
|
67
|
+
new_resource.action(action)
|
68
|
+
provider.action = action
|
69
|
+
provider.load_current_resource
|
70
|
+
provider.define_resource_requirements
|
71
|
+
provider.process_resource_requirements
|
72
|
+
end
|
73
|
+
|
74
|
+
before do
|
75
|
+
Dir.stub(:glob).and_return([])
|
76
|
+
provider.stub(:shell_out!).
|
77
|
+
with(/plutil -convert xml1 -o/).
|
78
|
+
and_raise(Mixlib::ShellOut::ShellCommandFailed)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "works for action :nothing" do
|
82
|
+
lambda { run_resource_setup_for_action(:nothing) }.should_not raise_error
|
83
|
+
end
|
84
|
+
|
85
|
+
it "works for action :start" do
|
86
|
+
lambda { run_resource_setup_for_action(:start) }.should_not raise_error
|
87
|
+
end
|
88
|
+
|
89
|
+
it "errors if action is :enable" do
|
90
|
+
lambda { run_resource_setup_for_action(:enable) }.should raise_error(Chef::Exceptions::Service)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "errors if action is :disable" do
|
94
|
+
lambda { run_resource_setup_for_action(:disable) }.should raise_error(Chef::Exceptions::Service)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
61
98
|
context "when launchctl returns pid in service list" do
|
62
99
|
let(:launchctl_stdout) { StringIO.new <<-SVC_LIST }
|
63
100
|
12761 - 0x100114220.old.machinit.thing
|
@@ -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,435 +20,856 @@ ShellCmdResult = Struct.new(:stdout, :stderr, :exitstatus)
|
|
20
20
|
|
21
21
|
require 'spec_helper'
|
22
22
|
require 'ostruct'
|
23
|
+
require 'mixlib/shellout'
|
23
24
|
|
24
25
|
describe Chef::Provider::User::Dscl do
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
+
|
33
112
|
describe "when shelling out to dscl" do
|
34
113
|
it "should run dscl with the supplied cmd /Path args" do
|
35
114
|
shell_return = ShellCmdResult.new('stdout', 'err', 0)
|
36
|
-
|
37
|
-
|
115
|
+
provider.should_receive(:shell_out).with("dscl . -cmd /Path args").and_return(shell_return)
|
116
|
+
provider.run_dscl("cmd /Path args").should == 'stdout'
|
38
117
|
end
|
39
118
|
|
40
119
|
it "returns an empty string from delete commands" do
|
41
120
|
shell_return = ShellCmdResult.new('out', 'err', 23)
|
42
|
-
|
43
|
-
|
121
|
+
provider.should_receive(:shell_out).with("dscl . -delete /Path args").and_return(shell_return)
|
122
|
+
provider.run_dscl("delete /Path args").should == ""
|
44
123
|
end
|
45
124
|
|
46
125
|
it "should raise an exception for any other command" do
|
47
126
|
shell_return = ShellCmdResult.new('out', 'err', 23)
|
48
|
-
|
49
|
-
lambda {
|
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)
|
50
129
|
end
|
51
130
|
|
52
131
|
it "raises an exception when dscl reports 'no such key'" do
|
53
132
|
shell_return = ShellCmdResult.new("No such key: ", 'err', 23)
|
54
|
-
|
55
|
-
lambda {
|
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)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "raises an exception when dscl reports 'eDSRecordNotFound'" do
|
138
|
+
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)
|
56
141
|
end
|
57
142
|
end
|
58
143
|
|
59
144
|
describe "get_free_uid" do
|
60
145
|
before do
|
61
|
-
|
146
|
+
provider.should_receive(:run_dscl).with("list /Users uid").and_return("\nwheel 200\nstaff 201\nbrahms 500\nchopin 501\n")
|
62
147
|
end
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
67
157
|
end
|
68
158
|
|
69
159
|
it "should return the first unused uid number on or above 200" do
|
70
|
-
|
160
|
+
provider.get_free_uid.should eq(502)
|
71
161
|
end
|
72
|
-
|
162
|
+
|
73
163
|
it "should raise an exception when the search limit is exhausted" do
|
74
164
|
search_limit = 1
|
75
|
-
lambda {
|
165
|
+
lambda { provider.get_free_uid(search_limit) }.should raise_error(RuntimeError)
|
76
166
|
end
|
77
167
|
end
|
78
168
|
|
79
169
|
describe "uid_used?" do
|
80
|
-
|
81
|
-
|
170
|
+
it "should return false if not given any valid uid number" do
|
171
|
+
provider.uid_used?(nil).should be_false
|
82
172
|
end
|
83
173
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
it "should return true for a used uid number" do
|
90
|
-
@provider.uid_used?(500).should be_true
|
91
|
-
end
|
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
|
92
178
|
|
93
|
-
|
94
|
-
|
95
|
-
|
179
|
+
it "should return true for a used uid number" do
|
180
|
+
provider.uid_used?(500).should be_true
|
181
|
+
end
|
96
182
|
|
97
|
-
|
98
|
-
|
183
|
+
it "should return false for an unused uid number" do
|
184
|
+
provider.uid_used?(501).should be_false
|
185
|
+
end
|
99
186
|
end
|
100
187
|
end
|
101
188
|
|
102
189
|
describe "when determining the uid to set" do
|
103
190
|
it "raises RequestedUIDUnavailable if the requested uid is already in use" do
|
104
|
-
|
105
|
-
|
106
|
-
lambda {
|
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)
|
107
194
|
end
|
108
|
-
|
195
|
+
|
109
196
|
it "finds a valid, unused uid when none is specified" do
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
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)
|
115
202
|
end
|
116
|
-
|
203
|
+
|
117
204
|
it "sets the uid specified in the resource" do
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|
122
209
|
end
|
123
210
|
end
|
124
211
|
|
125
212
|
describe "when modifying the home directory" do
|
213
|
+
let(:current_resource) {
|
214
|
+
new_resource.dup
|
215
|
+
}
|
216
|
+
|
126
217
|
before do
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
@provider.current_resource = @current_resource
|
218
|
+
new_resource.supports({ :manage_home => true })
|
219
|
+
new_resource.home('/Users/toor')
|
220
|
+
|
221
|
+
provider.current_resource = current_resource
|
132
222
|
end
|
133
223
|
|
134
224
|
it "deletes the home directory when resource#home is nil" do
|
135
|
-
|
136
|
-
|
137
|
-
|
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
|
138
228
|
end
|
139
|
-
|
229
|
+
|
140
230
|
|
141
231
|
it "raises InvalidHomeDirectory when the resource's home directory doesn't look right" do
|
142
|
-
|
143
|
-
lambda {
|
232
|
+
new_resource.home('epic-fail')
|
233
|
+
lambda { provider.dscl_set_home }.should raise_error(Chef::Exceptions::InvalidHomeDirectory)
|
144
234
|
end
|
145
235
|
|
146
236
|
it "moves the users home to the new location if it exists and the target location is different" do
|
147
|
-
|
148
|
-
|
237
|
+
new_resource.supports(:manage_home => true)
|
238
|
+
|
149
239
|
current_home = CHEF_SPEC_DATA + '/old_home_dir'
|
150
240
|
current_home_files = [current_home + '/my-dot-emacs', current_home + '/my-dot-vim']
|
151
|
-
|
152
|
-
|
153
|
-
::File.stub
|
154
|
-
::File.stub
|
155
|
-
|
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
|
+
|
156
246
|
FileUtils.should_receive(:mkdir_p).with('/Users/toor').and_return(true)
|
157
247
|
FileUtils.should_receive(:rmdir).with(current_home)
|
158
248
|
::Dir.should_receive(:glob).with("#{CHEF_SPEC_DATA}/old_home_dir/*",::File::FNM_DOTMATCH).and_return(current_home_files)
|
159
249
|
FileUtils.should_receive(:mv).with(current_home_files, "/Users/toor", :force => true)
|
160
250
|
FileUtils.should_receive(:chown_R).with('toor','23','/Users/toor')
|
161
|
-
|
162
|
-
|
163
|
-
|
251
|
+
|
252
|
+
provider.should_receive(:run_dscl).with("create /Users/toor NFSHomeDirectory '/Users/toor'")
|
253
|
+
provider.dscl_set_home
|
164
254
|
end
|
165
255
|
|
166
256
|
it "should raise an exception when the systems user template dir (skel) cannot be found" do
|
167
|
-
::File.stub
|
168
|
-
lambda {
|
257
|
+
::File.stub(:exists?).and_return(false,false,false)
|
258
|
+
lambda { provider.dscl_set_home }.should raise_error(Chef::Exceptions::User)
|
169
259
|
end
|
170
260
|
|
171
261
|
it "should run ditto to copy any missing files from skel to the new home dir" do
|
172
262
|
::File.should_receive(:exists?).with("/System/Library/User\ Template/English.lproj").and_return(true)
|
173
263
|
FileUtils.should_receive(:chown_R).with('toor', '', '/Users/toor')
|
174
|
-
|
175
|
-
|
264
|
+
provider.should_receive(:shell_out!).with("ditto '/System/Library/User Template/English.lproj' '/Users/toor'")
|
265
|
+
provider.ditto_home
|
176
266
|
end
|
177
267
|
|
178
268
|
it "creates the user's NFSHomeDirectory and home directory" do
|
179
|
-
|
180
|
-
|
181
|
-
|
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
|
182
272
|
end
|
183
273
|
end
|
184
274
|
|
185
|
-
describe "
|
186
|
-
|
187
|
-
|
188
|
-
end
|
275
|
+
describe "resource_requirements" do
|
276
|
+
let(:dscl_exists) { true }
|
277
|
+
let(:plutil_exists) { true }
|
189
278
|
|
190
|
-
|
191
|
-
|
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)
|
192
282
|
end
|
193
|
-
end
|
194
283
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
284
|
+
def run_requirements
|
285
|
+
provider.define_resource_requirements
|
286
|
+
provider.action = :create
|
287
|
+
provider.process_resource_requirements
|
199
288
|
end
|
200
|
-
end
|
201
289
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
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
|
207
296
|
end
|
208
|
-
end
|
209
297
|
|
210
|
-
|
298
|
+
describe "when plutil doesn't exist" do
|
299
|
+
let(:plutil_exists) { false }
|
211
300
|
|
212
|
-
|
213
|
-
|
214
|
-
|
301
|
+
it "should raise an error" do
|
302
|
+
lambda { run_requirements }.should raise_error
|
303
|
+
end
|
215
304
|
end
|
216
305
|
|
217
|
-
describe "when
|
218
|
-
|
219
|
-
|
220
|
-
|
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
|
221
313
|
end
|
314
|
+
end
|
315
|
+
|
316
|
+
describe "when on Mac 10.7" do
|
317
|
+
let(:mac_version) {
|
318
|
+
"10.7.5"
|
319
|
+
}
|
222
320
|
|
223
|
-
|
224
|
-
|
225
|
-
|
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
|
226
327
|
end
|
227
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
|
228
336
|
end
|
229
337
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
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
|
234
370
|
end
|
235
371
|
end
|
236
372
|
end
|
237
373
|
|
238
|
-
describe "
|
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
|
+
|
239
378
|
before do
|
240
|
-
|
241
|
-
|
379
|
+
provider.should_receive(:shell_out).with("plutil -convert xml1 -o - /var/db/dslocal/nodes/Default/users/toor.plist") do
|
380
|
+
if user_plist_file.nil?
|
381
|
+
ShellCmdResult.new('Can not find the file', 'Sorry!!', 1)
|
382
|
+
else
|
383
|
+
ShellCmdResult.new(File.read(File.join(CHEF_SPEC_DATA, "mac_users/#{user_plist_file}.plist.xml")), "", 0)
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
if !user_plist_file.nil?
|
388
|
+
provider.should_receive(:convert_binary_plist_to_xml).and_return(File.read(File.join(CHEF_SPEC_DATA, "mac_users/#{user_plist_file}.shadow.xml")))
|
389
|
+
end
|
242
390
|
end
|
243
391
|
|
244
|
-
describe "when
|
245
|
-
|
246
|
-
|
247
|
-
end
|
248
|
-
|
249
|
-
it "should
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
392
|
+
describe "when user is not there" do
|
393
|
+
it "shouldn't raise an error" do
|
394
|
+
lambda { provider.load_current_resource }.should_not raise_error
|
395
|
+
end
|
396
|
+
|
397
|
+
it "should set @user_exists" do
|
398
|
+
provider.load_current_resource
|
399
|
+
provider.instance_variable_get(:@user_exists).should be_false
|
400
|
+
end
|
401
|
+
|
402
|
+
it "should set username" do
|
403
|
+
provider.load_current_resource
|
404
|
+
provider.current_resource.username.should eq("toor")
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
describe "when user is there" do
|
409
|
+
let(:password) { "something" } # Load password during load_current_resource
|
410
|
+
|
411
|
+
describe "on 10.7" do
|
412
|
+
let(:mac_version) {
|
413
|
+
"10.7.5"
|
414
|
+
}
|
415
|
+
|
416
|
+
let(:user_plist_file) { "10.7" }
|
417
|
+
|
418
|
+
it "collects the user data correctly" do
|
419
|
+
provider.load_current_resource
|
420
|
+
provider.current_resource.comment.should eq("vagrant")
|
421
|
+
provider.current_resource.uid.should eq("11112222-3333-4444-AAAA-BBBBCCCCDDDD")
|
422
|
+
provider.current_resource.gid.should eq("80")
|
423
|
+
provider.current_resource.home.should eq("/Users/vagrant")
|
424
|
+
provider.current_resource.shell.should eq("/bin/bash")
|
425
|
+
provider.current_resource.password.should eq(vagrant_sha_512)
|
426
|
+
end
|
427
|
+
|
428
|
+
describe "when a plain password is set that is same" do
|
429
|
+
let(:password) { "vagrant" }
|
430
|
+
|
431
|
+
it "diverged_password? should report false" do
|
432
|
+
provider.load_current_resource
|
433
|
+
provider.diverged_password?.should be_false
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
describe "when a plain password is set that is different" do
|
438
|
+
let(:password) { "not_vagrant" }
|
439
|
+
|
440
|
+
it "diverged_password? should report true" do
|
441
|
+
provider.load_current_resource
|
442
|
+
provider.diverged_password?.should be_true
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
describe "when iterations change" do
|
447
|
+
let(:password) { vagrant_sha_512 }
|
448
|
+
let(:iterations) { 12345 }
|
449
|
+
|
450
|
+
it "diverged_password? should report false" do
|
451
|
+
provider.load_current_resource
|
452
|
+
provider.diverged_password?.should be_false
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
describe "when shadow hash changes" do
|
457
|
+
let(:password) { salted_sha512_password }
|
458
|
+
|
459
|
+
it "diverged_password? should report true" do
|
460
|
+
provider.load_current_resource
|
461
|
+
provider.diverged_password?.should be_true
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
describe "when salt change" do
|
466
|
+
let(:password) { vagrant_sha_512 }
|
467
|
+
let(:salt) { "SOMETHINGRANDOM" }
|
468
|
+
|
469
|
+
it "diverged_password? should report false" do
|
470
|
+
provider.load_current_resource
|
471
|
+
provider.diverged_password?.should be_false
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
describe "on 10.8" do
|
477
|
+
let(:mac_version) {
|
478
|
+
"10.8.3"
|
479
|
+
}
|
480
|
+
|
481
|
+
let(:user_plist_file) { "10.8" }
|
482
|
+
|
483
|
+
it "collects the user data correctly" do
|
484
|
+
provider.load_current_resource
|
485
|
+
provider.current_resource.comment.should eq("vagrant")
|
486
|
+
provider.current_resource.uid.should eq("11112222-3333-4444-AAAA-BBBBCCCCDDDD")
|
487
|
+
provider.current_resource.gid.should eq("80")
|
488
|
+
provider.current_resource.home.should eq("/Users/vagrant")
|
489
|
+
provider.current_resource.shell.should eq("/bin/bash")
|
490
|
+
provider.current_resource.password.should eq("ea4c2d265d801ba0ec0dfccd\
|
491
|
+
253dfc1de91cbe0806b4acc1ed7fe22aebcf6beb5344d0f442e590\
|
492
|
+
ffa04d679075da3afb119e41b72b5eaf08ee4aa54693722646d5\
|
493
|
+
19ee04843deb8a3e977428d33f625e83887913e5c13b70035961\
|
494
|
+
5e00ad7bc3e7a0c98afc3e19d1360272454f8d33a9214d2fbe8b\
|
495
|
+
e68d1f9821b26689312366")
|
496
|
+
provider.current_resource.salt.should eq("f994ef2f73b7c5594ebd1553300976b20733ce0e24d659783d87f3d81cbbb6a9")
|
497
|
+
provider.current_resource.iterations.should eq(39840)
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
describe "on 10.7 upgraded to 10.8" do
|
502
|
+
# In this scenario user password is still in 10.7 format
|
503
|
+
let(:mac_version) {
|
504
|
+
"10.8.3"
|
505
|
+
}
|
506
|
+
|
507
|
+
let(:user_plist_file) { "10.7-8" }
|
508
|
+
|
509
|
+
it "collects the user data correctly" do
|
510
|
+
provider.load_current_resource
|
511
|
+
provider.current_resource.comment.should eq("vagrant")
|
512
|
+
provider.current_resource.uid.should eq("11112222-3333-4444-AAAA-BBBBCCCCDDDD")
|
513
|
+
provider.current_resource.gid.should eq("80")
|
514
|
+
provider.current_resource.home.should eq("/Users/vagrant")
|
515
|
+
provider.current_resource.shell.should eq("/bin/bash")
|
516
|
+
provider.current_resource.password.should eq("6f75d7190441facc34291ebbea1fc756b242d4f\
|
517
|
+
e9bcff141bccb84f1979e27e539539aa31f9f7dcc92c0cea959\
|
518
|
+
ea18e18b720e358e7fbe3cfbeaa561456f6ba008937a30")
|
519
|
+
end
|
520
|
+
|
521
|
+
describe "when a plain text password is set" do
|
522
|
+
it "reports password needs to be updated" do
|
523
|
+
provider.load_current_resource
|
524
|
+
provider.diverged_password?.should be_true
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
describe "when a salted-sha512-pbkdf2 shadow is set" do
|
529
|
+
let(:password) { salted_sha512_pbkdf2_password }
|
530
|
+
let(:salt) { salted_sha512_pbkdf2_salt }
|
531
|
+
let(:iterations) { salted_sha512_pbkdf2_iterations }
|
532
|
+
|
533
|
+
it "reports password needs to be updated" do
|
534
|
+
provider.load_current_resource
|
535
|
+
provider.diverged_password?.should be_true
|
536
|
+
end
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
describe "on 10.9" do
|
541
|
+
let(:mac_version) {
|
542
|
+
"10.9.1"
|
543
|
+
}
|
544
|
+
|
545
|
+
let(:user_plist_file) { "10.9" }
|
546
|
+
|
547
|
+
it "collects the user data correctly" do
|
548
|
+
provider.load_current_resource
|
549
|
+
provider.current_resource.comment.should eq("vagrant")
|
550
|
+
provider.current_resource.uid.should eq("11112222-3333-4444-AAAA-BBBBCCCCDDDD")
|
551
|
+
provider.current_resource.gid.should eq("80")
|
552
|
+
provider.current_resource.home.should eq("/Users/vagrant")
|
553
|
+
provider.current_resource.shell.should eq("/bin/bash")
|
554
|
+
provider.current_resource.password.should eq(vagrant_sha_512_pbkdf2)
|
555
|
+
provider.current_resource.salt.should eq(vagrant_sha_512_pbkdf2_salt)
|
556
|
+
provider.current_resource.iterations.should eq(vagrant_sha_512_pbkdf2_iterations)
|
557
|
+
end
|
558
|
+
|
559
|
+
describe "when a plain password is set that is same" do
|
560
|
+
let(:password) { "vagrant" }
|
561
|
+
|
562
|
+
it "diverged_password? should report false" do
|
563
|
+
provider.load_current_resource
|
564
|
+
provider.diverged_password?.should be_false
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
describe "when a plain password is set that is different" do
|
569
|
+
let(:password) { "not_vagrant" }
|
570
|
+
|
571
|
+
it "diverged_password? should report true" do
|
572
|
+
provider.load_current_resource
|
573
|
+
provider.diverged_password?.should be_true
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
describe "when iterations change" do
|
578
|
+
let(:password) { vagrant_sha_512_pbkdf2 }
|
579
|
+
let(:salt) { vagrant_sha_512_pbkdf2_salt }
|
580
|
+
let(:iterations) { 12345 }
|
581
|
+
|
582
|
+
it "diverged_password? should report true" do
|
583
|
+
provider.load_current_resource
|
584
|
+
provider.diverged_password?.should be_true
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
describe "when shadow hash changes" do
|
589
|
+
let(:password) { salted_sha512_pbkdf2_password }
|
590
|
+
let(:salt) { vagrant_sha_512_pbkdf2_salt }
|
591
|
+
let(:iterations) { vagrant_sha_512_pbkdf2_iterations }
|
592
|
+
|
593
|
+
it "diverged_password? should report true" do
|
594
|
+
provider.load_current_resource
|
595
|
+
provider.diverged_password?.should be_true
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
describe "when salt change" do
|
600
|
+
let(:password) { vagrant_sha_512_pbkdf2 }
|
601
|
+
let(:salt) { salted_sha512_pbkdf2_salt }
|
602
|
+
let(:iterations) { vagrant_sha_512_pbkdf2_iterations }
|
603
|
+
|
604
|
+
it "diverged_password? should report true" do
|
605
|
+
provider.load_current_resource
|
606
|
+
provider.diverged_password?.should be_true
|
607
|
+
end
|
608
|
+
end
|
609
|
+
end
|
311
610
|
end
|
312
611
|
end
|
313
612
|
|
314
|
-
describe "
|
315
|
-
it "should
|
316
|
-
|
317
|
-
lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::User)
|
613
|
+
describe "salted_sha512_pbkdf2?" do
|
614
|
+
it "should return true when the string is a salted_sha512_pbkdf2 hash" do
|
615
|
+
provider.salted_sha512_pbkdf2?(salted_sha512_pbkdf2_password).should be_true
|
318
616
|
end
|
319
617
|
|
320
|
-
it "
|
321
|
-
|
322
|
-
|
618
|
+
it "should return false otherwise" do
|
619
|
+
provider.salted_sha512_pbkdf2?(salted_sha512_password).should be_false
|
620
|
+
provider.salted_sha512_pbkdf2?("any other string").should be_false
|
323
621
|
end
|
324
622
|
end
|
325
623
|
|
326
|
-
describe "
|
327
|
-
|
328
|
-
|
329
|
-
|
624
|
+
describe "salted_sha512?" do
|
625
|
+
it "should return true when the string is a salted_sha512_pbkdf2 hash" do
|
626
|
+
provider.salted_sha512_pbkdf2?(salted_sha512_pbkdf2_password).should be_true
|
627
|
+
end
|
628
|
+
|
629
|
+
it "should return false otherwise" do
|
630
|
+
provider.salted_sha512?(salted_sha512_pbkdf2_password).should be_false
|
631
|
+
provider.salted_sha512?("any other string").should be_false
|
330
632
|
end
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
633
|
+
end
|
634
|
+
|
635
|
+
describe "prepare_password_shadow_info" do
|
636
|
+
describe "when on Mac 10.7" do
|
637
|
+
let(:mac_version) {
|
638
|
+
"10.7.1"
|
639
|
+
}
|
640
|
+
|
641
|
+
describe "when the password is plain text" do
|
642
|
+
let(:password) { "vagrant" }
|
643
|
+
|
644
|
+
it "password_shadow_info should have salted-sha-512 format" do
|
645
|
+
shadow_info = provider.prepare_password_shadow_info
|
646
|
+
shadow_info.should have_key("SALTED-SHA512")
|
647
|
+
info = shadow_info["SALTED-SHA512"].string.unpack('H*').first
|
648
|
+
provider.salted_sha512?(info).should be_true
|
649
|
+
end
|
650
|
+
end
|
651
|
+
|
652
|
+
describe "when the password is salted-sha-512" do
|
653
|
+
let(:password) { vagrant_sha_512 }
|
654
|
+
|
655
|
+
it "password_shadow_info should have salted-sha-512 format" do
|
656
|
+
shadow_info = provider.prepare_password_shadow_info
|
657
|
+
shadow_info.should have_key("SALTED-SHA512")
|
658
|
+
info = shadow_info["SALTED-SHA512"].string.unpack('H*').first
|
659
|
+
provider.salted_sha512?(info).should be_true
|
660
|
+
info.should eq(vagrant_sha_512)
|
661
|
+
end
|
662
|
+
end
|
341
663
|
end
|
342
664
|
|
343
|
-
|
344
|
-
|
345
|
-
|
665
|
+
["10.8", "10.9", "10.10"].each do |version|
|
666
|
+
describe "when on Mac #{version}" do
|
667
|
+
let(:mac_version) {
|
668
|
+
"#{version}.1"
|
669
|
+
}
|
670
|
+
|
671
|
+
describe "when the password is plain text" do
|
672
|
+
let(:password) { "vagrant" }
|
673
|
+
|
674
|
+
it "password_shadow_info should have salted-sha-512 format" do
|
675
|
+
shadow_info = provider.prepare_password_shadow_info
|
676
|
+
shadow_info.should have_key("SALTED-SHA512-PBKDF2")
|
677
|
+
shadow_info["SALTED-SHA512-PBKDF2"].should have_key("entropy")
|
678
|
+
shadow_info["SALTED-SHA512-PBKDF2"].should have_key("salt")
|
679
|
+
shadow_info["SALTED-SHA512-PBKDF2"].should have_key("iterations")
|
680
|
+
info = shadow_info["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack('H*').first
|
681
|
+
provider.salted_sha512_pbkdf2?(info).should be_true
|
682
|
+
end
|
683
|
+
end
|
684
|
+
|
685
|
+
describe "when the password is salted-sha-512" do
|
686
|
+
let(:password) { vagrant_sha_512_pbkdf2 }
|
687
|
+
let(:iterations) { vagrant_sha_512_pbkdf2_iterations }
|
688
|
+
let(:salt) { vagrant_sha_512_pbkdf2_salt }
|
689
|
+
|
690
|
+
it "password_shadow_info should have salted-sha-512 format" do
|
691
|
+
shadow_info = provider.prepare_password_shadow_info
|
692
|
+
shadow_info.should have_key("SALTED-SHA512-PBKDF2")
|
693
|
+
shadow_info["SALTED-SHA512-PBKDF2"].should have_key("entropy")
|
694
|
+
shadow_info["SALTED-SHA512-PBKDF2"].should have_key("salt")
|
695
|
+
shadow_info["SALTED-SHA512-PBKDF2"].should have_key("iterations")
|
696
|
+
info = shadow_info["SALTED-SHA512-PBKDF2"]["entropy"].string.unpack('H*').first
|
697
|
+
provider.salted_sha512_pbkdf2?(info).should be_true
|
698
|
+
info.should eq(vagrant_sha_512_pbkdf2)
|
699
|
+
end
|
700
|
+
end
|
701
|
+
end
|
346
702
|
end
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
703
|
+
end
|
704
|
+
|
705
|
+
describe "set_password" do
|
706
|
+
before do
|
707
|
+
new_resource.password("something")
|
351
708
|
end
|
352
709
|
|
353
|
-
it "should
|
354
|
-
|
355
|
-
|
710
|
+
it "should sleep and flush the dscl cache before saving the password" do
|
711
|
+
provider.should_receive(:prepare_password_shadow_info).and_return({ })
|
712
|
+
mock_shellout = double("Mock::Shellout")
|
713
|
+
mock_shellout.stub(:run_command)
|
714
|
+
Mixlib::ShellOut.should_receive(:new).and_return(mock_shellout)
|
715
|
+
provider.should_receive(:read_user_info)
|
716
|
+
provider.should_receive(:dscl_set)
|
717
|
+
provider.should_receive(:sleep).with(3)
|
718
|
+
provider.should_receive(:shell_out).with("dscacheutil '-flushcache'")
|
719
|
+
provider.should_receive(:save_user_info)
|
720
|
+
provider.set_password
|
356
721
|
end
|
722
|
+
end
|
723
|
+
|
724
|
+
describe "when the user does not yet exist and chef is creating it" do
|
725
|
+
context "with a numeric gid" do
|
726
|
+
before do
|
727
|
+
new_resource.comment "#mockssuck"
|
728
|
+
new_resource.gid 1001
|
729
|
+
end
|
730
|
+
|
731
|
+
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
|
740
|
+
end
|
741
|
+
|
742
|
+
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
|
745
|
+
end
|
746
|
+
|
747
|
+
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
|
750
|
+
end
|
751
|
+
|
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
|
755
|
+
end
|
357
756
|
|
358
|
-
|
359
|
-
|
360
|
-
|
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
|
760
|
+
end
|
361
761
|
end
|
362
762
|
|
763
|
+
context "with a non-numeric gid" do
|
764
|
+
before do
|
765
|
+
new_resource.comment "#mockssuck"
|
766
|
+
new_resource.gid "newgroup"
|
767
|
+
end
|
768
|
+
|
769
|
+
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
|
773
|
+
end
|
774
|
+
|
775
|
+
it "should raise an exception when the group does not exist" do
|
776
|
+
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)
|
779
|
+
end
|
780
|
+
end
|
363
781
|
end
|
364
782
|
|
365
783
|
describe "when the user exists and chef is managing it" do
|
366
784
|
before do
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
# These are all different from
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
end
|
377
|
-
|
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
|
+
|
378
796
|
it "sets the user, comment field, uid, gid, moves the home directory, sets the shell, and sets the password" do
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
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
|
387
805
|
end
|
388
806
|
end
|
389
807
|
|
390
808
|
describe "when changing the gid" do
|
391
809
|
before do
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
# This is different from
|
396
|
-
|
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
|
397
815
|
end
|
398
|
-
|
816
|
+
|
399
817
|
it "sets the gid" do
|
400
|
-
|
401
|
-
|
818
|
+
provider.should_receive :dscl_set_gid
|
819
|
+
provider.manage_user
|
402
820
|
end
|
403
821
|
end
|
404
822
|
|
405
|
-
describe "when the user exists
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
823
|
+
describe "when the user exists" do
|
824
|
+
before do
|
825
|
+
provider.should_receive(:shell_out).with("plutil -convert xml1 -o - /var/db/dslocal/nodes/Default/users/toor.plist") do
|
826
|
+
ShellCmdResult.new(File.read(File.join(CHEF_SPEC_DATA, "mac_users/10.9.plist.xml")), "", 0)
|
827
|
+
end
|
828
|
+
provider.load_current_resource
|
829
|
+
end
|
830
|
+
|
831
|
+
describe "when Chef is removing the user" do
|
832
|
+
it "removes the user from the groups and deletes home directory when the resource is configured to manage home" do
|
833
|
+
new_resource.supports({ :manage_home => true })
|
834
|
+
provider.should_receive(:run_dscl).with("list /Groups").and_return("my_group\nyour_group\nreal_group\n")
|
835
|
+
provider.should_receive(:run_dscl).with("read /Groups/my_group").and_raise(Chef::Exceptions::DsclCommandFailed) # Empty group
|
836
|
+
provider.should_receive(:run_dscl).with("read /Groups/your_group").and_return("GroupMembership: not_you")
|
837
|
+
provider.should_receive(:run_dscl).with("read /Groups/real_group").and_return("GroupMembership: toor")
|
838
|
+
provider.should_receive(:run_dscl).with("delete /Groups/real_group GroupMembership 'toor'")
|
839
|
+
provider.should_receive(:run_dscl).with("delete /Users/toor")
|
840
|
+
FileUtils.should_receive(:rm_rf).with("/Users/vagrant")
|
841
|
+
provider.remove_user
|
842
|
+
end
|
419
843
|
end
|
420
|
-
end
|
421
844
|
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
@provider.should_not be_locked
|
845
|
+
describe "when user is not locked" do
|
846
|
+
it "determines the user as not locked" do
|
847
|
+
provider.should_not be_locked
|
848
|
+
end
|
427
849
|
end
|
428
850
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
851
|
+
describe "when user is locked" do
|
852
|
+
before do
|
853
|
+
auth_authority = provider.instance_variable_get(:@authentication_authority)
|
854
|
+
provider.instance_variable_set(:@authentication_authority, auth_authority + ";DisabledUser;")
|
855
|
+
end
|
856
|
+
|
857
|
+
it "determines the user as not locked" do
|
858
|
+
provider.should be_locked
|
859
|
+
end
|
433
860
|
|
434
|
-
|
435
|
-
|
436
|
-
|
861
|
+
it "can unlock the user" do
|
862
|
+
provider.should_receive(:run_dscl).with("create /Users/toor AuthenticationAuthority ';ShadowHash;HASHLIST:<SALTED-SHA512-PBKDF2>'")
|
863
|
+
provider.unlock_user
|
864
|
+
end
|
437
865
|
end
|
438
866
|
end
|
439
867
|
|
440
868
|
describe "when locking the user" do
|
441
|
-
it "should run
|
442
|
-
|
443
|
-
|
869
|
+
it "should run run_dscl with append /Users/user AuthenticationAuthority ;DisabledUser; to lock the user account" do
|
870
|
+
provider.should_receive(:run_dscl).with("append /Users/toor AuthenticationAuthority ';DisabledUser;'")
|
871
|
+
provider.lock_user
|
444
872
|
end
|
445
873
|
end
|
446
874
|
|
447
|
-
describe "when unlocking the user" do
|
448
|
-
it "removes DisabledUser from the authentication string" do
|
449
|
-
@provider.should_receive(:safe_dscl).with("read /Users/toor AuthenticationAuthority").and_return("\nAuthenticationAuthority: ;ShadowHash; ;DisabledUser;\n")
|
450
|
-
@provider.should_receive(:safe_dscl).with("create /Users/toor AuthenticationAuthority ';ShadowHash;'")
|
451
|
-
@provider.unlock_user
|
452
|
-
end
|
453
|
-
end
|
454
875
|
end
|