summaryrefslogtreecommitdiff
path: root/spec/unit/provider/user
diff options
context:
space:
mode:
Diffstat (limited to 'spec/unit/provider/user')
-rw-r--r--spec/unit/provider/user/dscl_spec.rb480
-rw-r--r--spec/unit/provider/user/pw_spec.rb235
-rw-r--r--spec/unit/provider/user/useradd_spec.rb386
-rw-r--r--spec/unit/provider/user/windows_spec.rb178
4 files changed, 1279 insertions, 0 deletions
diff --git a/spec/unit/provider/user/dscl_spec.rb b/spec/unit/provider/user/dscl_spec.rb
new file mode 100644
index 0000000000..3894cd61b4
--- /dev/null
+++ b/spec/unit/provider/user/dscl_spec.rb
@@ -0,0 +1,480 @@
+#
+# Author:: Dreamcat4 (<dreamcat4@gmail.com>)
+# Copyright:: Copyright (c) 2009 OpsCode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+ShellCmdResult = Struct.new(:stdout, :stderr, :exitstatus)
+
+require 'spec_helper'
+require 'ostruct'
+
+describe Chef::Provider::User::Dscl do
+ before do
+ @node = Chef::Node.new
+ @events = Chef::EventDispatch::Dispatcher.new
+ @run_context = Chef::RunContext.new(@node, {}, @events)
+ @new_resource = Chef::Resource::User.new("toor")
+ @provider = Chef::Provider::User::Dscl.new(@new_resource, @run_context)
+ end
+
+ describe "when shelling out to dscl" do
+ it "should run dscl with the supplied cmd /Path args" do
+ shell_return = ShellCmdResult.new('stdout', 'err', 0)
+ @provider.should_receive(:shell_out).with("dscl . -cmd /Path args").and_return(shell_return)
+ @provider.safe_dscl("cmd /Path args").should == 'stdout'
+ end
+
+ it "returns an empty string from delete commands" do
+ shell_return = ShellCmdResult.new('out', 'err', 23)
+ @provider.should_receive(:shell_out).with("dscl . -delete /Path args").and_return(shell_return)
+ @provider.safe_dscl("delete /Path args").should == ""
+ end
+
+ it "should raise an exception for any other command" do
+ shell_return = ShellCmdResult.new('out', 'err', 23)
+ @provider.should_receive(:shell_out).with('dscl . -cmd /Path arguments').and_return(shell_return)
+ lambda { @provider.safe_dscl("cmd /Path arguments") }.should raise_error(Chef::Exceptions::DsclCommandFailed)
+ end
+
+ it "raises an exception when dscl reports 'no such key'" do
+ shell_return = ShellCmdResult.new("No such key: ", 'err', 23)
+ @provider.should_receive(:shell_out).with('dscl . -cmd /Path args').and_return(shell_return)
+ lambda { @provider.safe_dscl("cmd /Path args") }.should raise_error(Chef::Exceptions::DsclCommandFailed)
+ end
+
+ it "raises an exception when dscl reports 'eDSRecordNotFound'" do
+ shell_return = ShellCmdResult.new("<dscl_cmd> DS Error: -14136 (eDSRecordNotFound)", 'err', -14136)
+ @provider.should_receive(:shell_out).with('dscl . -cmd /Path args').and_return(shell_return)
+ lambda { @provider.safe_dscl("cmd /Path args") }.should raise_error(Chef::Exceptions::DsclCommandFailed)
+ end
+ end
+
+ describe "get_free_uid" do
+ before do
+ @provider.stub!(:safe_dscl).and_return("\nwheel 200\nstaff 201\n")
+ end
+
+ it "should run safe_dscl with list /Users uid" do
+ @provider.should_receive(:safe_dscl).with("list /Users uid")
+ @provider.get_free_uid
+ end
+
+ it "should return the first unused uid number on or above 200" do
+ @provider.get_free_uid.should == 202
+ end
+
+ it "should raise an exception when the search limit is exhausted" do
+ search_limit = 1
+ lambda { @provider.get_free_uid(search_limit) }.should raise_error(RuntimeError)
+ end
+ end
+
+ describe "uid_used?" do
+ before do
+ @provider.stub!(:safe_dscl).and_return("\naj 500\n")
+ end
+
+ it "should run safe_dscl with list /Users uid" do
+ @provider.should_receive(:safe_dscl).with("list /Users uid")
+ @provider.uid_used?(500)
+ end
+
+ it "should return true for a used uid number" do
+ @provider.uid_used?(500).should be_true
+ end
+
+ it "should return false for an unused uid number" do
+ @provider.uid_used?(501).should be_false
+ end
+
+ it "should return false if not given any valid uid number" do
+ @provider.uid_used?(nil).should be_false
+ end
+ end
+
+ describe "when determining the uid to set" do
+ it "raises RequestedUIDUnavailable if the requested uid is already in use" do
+ @provider.stub!(:uid_used?).and_return(true)
+ @provider.should_receive(:get_free_uid).and_return(501)
+ lambda { @provider.set_uid }.should raise_error(Chef::Exceptions::RequestedUIDUnavailable)
+ end
+
+ it "finds a valid, unused uid when none is specified" do
+ @provider.should_receive(:safe_dscl).with("list /Users uid").and_return('')
+ @provider.should_receive(:safe_dscl).with("create /Users/toor UniqueID 501")
+ @provider.should_receive(:get_free_uid).and_return(501)
+ @provider.set_uid
+ @new_resource.uid.should == 501
+ end
+
+ it "sets the uid specified in the resource" do
+ @new_resource.uid(1000)
+ @provider.should_receive(:safe_dscl).with("create /Users/toor UniqueID 1000").and_return(true)
+ @provider.should_receive(:safe_dscl).with("list /Users uid").and_return('')
+ @provider.set_uid
+ end
+ end
+
+ describe "when modifying the home directory" do
+ before do
+ @new_resource.supports({ :manage_home => true })
+ @new_resource.home('/Users/toor')
+
+ @current_resource = @new_resource.dup
+ @provider.current_resource = @current_resource
+ end
+
+ it "deletes the home directory when resource#home is nil" do
+ @new_resource.instance_variable_set(:@home, nil)
+ @provider.should_receive(:safe_dscl).with("delete /Users/toor NFSHomeDirectory").and_return(true)
+ @provider.modify_home
+ end
+
+
+ it "raises InvalidHomeDirectory when the resource's home directory doesn't look right" do
+ @new_resource.home('epic-fail')
+ lambda { @provider.modify_home }.should raise_error(Chef::Exceptions::InvalidHomeDirectory)
+ end
+
+ it "moves the users home to the new location if it exists and the target location is different" do
+ @new_resource.supports(:manage_home => true)
+
+ current_home = CHEF_SPEC_DATA + '/old_home_dir'
+ current_home_files = [current_home + '/my-dot-emacs', current_home + '/my-dot-vim']
+ @current_resource.home(current_home)
+ @new_resource.gid(23)
+ ::File.stub!(:exists?).with('/old/home/toor').and_return(true)
+ ::File.stub!(:exists?).with('/Users/toor').and_return(true)
+
+ FileUtils.should_receive(:mkdir_p).with('/Users/toor').and_return(true)
+ FileUtils.should_receive(:rmdir).with(current_home)
+ ::Dir.should_receive(:glob).with("#{CHEF_SPEC_DATA}/old_home_dir/*",::File::FNM_DOTMATCH).and_return(current_home_files)
+ FileUtils.should_receive(:mv).with(current_home_files, "/Users/toor", :force => true)
+ FileUtils.should_receive(:chown_R).with('toor','23','/Users/toor')
+
+ @provider.should_receive(:safe_dscl).with("create /Users/toor NFSHomeDirectory '/Users/toor'")
+ @provider.modify_home
+ end
+
+ it "should raise an exception when the systems user template dir (skel) cannot be found" do
+ ::File.stub!(:exists?).and_return(false,false,false)
+ lambda { @provider.modify_home }.should raise_error(Chef::Exceptions::User)
+ end
+
+ it "should run ditto to copy any missing files from skel to the new home dir" do
+ ::File.should_receive(:exists?).with("/System/Library/User\ Template/English.lproj").and_return(true)
+ FileUtils.should_receive(:chown_R).with('toor', '', '/Users/toor')
+ @provider.should_receive(:shell_out!).with("ditto '/System/Library/User Template/English.lproj' '/Users/toor'")
+ @provider.ditto_home
+ end
+
+ it "creates the user's NFSHomeDirectory and home directory" do
+ @provider.should_receive(:safe_dscl).with("create /Users/toor NFSHomeDirectory '/Users/toor'").and_return(true)
+ @provider.should_receive(:ditto_home)
+ @provider.modify_home
+ end
+ end
+
+ describe "osx_shadow_hash?" do
+ it "should return true when the string is a shadow hash" do
+ @provider.osx_shadow_hash?("0"*8*155).should eql(true)
+ end
+
+ it "should return false otherwise" do
+ @provider.osx_shadow_hash?("any other string").should eql(false)
+ end
+ end
+
+ describe "when detecting the format of a password" do
+ it "detects a OS X salted sha1" do
+ @provider.osx_salted_sha1?("0"*48).should eql(true)
+ @provider.osx_salted_sha1?("any other string").should eql(false)
+ end
+ end
+
+ describe "guid" do
+ it "should run safe_dscl with read /Users/user GeneratedUID to get the users GUID" do
+ expected_uuid = "b398449e-cee0-45e0-80f8-b0b5b1bfdeaa"
+ @provider.should_receive(:safe_dscl).with("read /Users/toor GeneratedUID").and_return(expected_uuid + "\n")
+ @provider.guid.should == expected_uuid
+ end
+ end
+
+ describe "shadow_hash_set?" do
+
+ it "should run safe_dscl with read /Users/user to see if the AuthenticationAuthority key exists" do
+ @provider.should_receive(:safe_dscl).with("read /Users/toor")
+ @provider.shadow_hash_set?
+ end
+
+ describe "when the user account has an AuthenticationAuthority key" do
+ it "uses the shadow hash when there is a ShadowHash field in the AuthenticationAuthority key" do
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("\nAuthenticationAuthority: ;ShadowHash;\n")
+ @provider.shadow_hash_set?.should be_true
+ end
+
+ it "does not use the shadow hash when there is no ShadowHash field in the AuthenticationAuthority key" do
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("\nAuthenticationAuthority: \n")
+ @provider.shadow_hash_set?.should be_false
+ end
+
+ end
+
+ describe "with no AuthenticationAuthority key in the user account" do
+ it "does not use the shadow hash" do
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("")
+ @provider.shadow_hash_set?.should eql(false)
+ end
+ end
+ end
+
+ describe "when setting or modifying the user password" do
+ before do
+ @new_resource.password("password")
+ @output = StringIO.new
+ end
+
+ describe "when using a salted sha1 for the password" do
+ before do
+ @new_resource.password("F"*48)
+ end
+
+ it "should write a shadow hash file with the expected salted sha1" do
+ uuid = "B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA"
+ File.should_receive(:open).with('/var/db/shadow/hash/B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA', "w", 384).and_yield(@output)
+ @provider.should_receive(:safe_dscl).with("read /Users/toor GeneratedUID").and_return(uuid)
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("\nAuthenticationAuthority: ;ShadowHash;\n")
+ expected_salted_sha1 = @new_resource.password
+ expected_shadow_hash = "00000000"*155
+ expected_shadow_hash[168] = expected_salted_sha1
+ @provider.modify_password
+ @output.string.strip.should == expected_shadow_hash
+ end
+ end
+
+ describe "when given a shadow hash file for the password" do
+ it "should write the shadow hash file directly to /var/db/shadow/hash/GUID" do
+ shadow_hash = '0123456789ABCDE0123456789ABCDEF' * 40
+ raise 'oops' unless shadow_hash.size == 1240
+ @new_resource.password shadow_hash
+ uuid = "B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA"
+ File.should_receive(:open).with('/var/db/shadow/hash/B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA', "w", 384).and_yield(@output)
+ @provider.should_receive(:safe_dscl).with("read /Users/toor GeneratedUID").and_return(uuid)
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("\nAuthenticationAuthority: ;ShadowHash;\n")
+ @provider.modify_password
+ @output.string.strip.should == shadow_hash
+ end
+ end
+
+ describe "when given a string for the password" do
+ it "should output a salted sha1 and shadow hash file from the specified password" do
+ uuid = "B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA"
+ File.should_receive(:open).with('/var/db/shadow/hash/B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA', "w", 384).and_yield(@output)
+ @new_resource.password("password")
+ OpenSSL::Random.stub!(:random_bytes).and_return("\377\377\377\377\377\377\377\377")
+ expected_salted_sha1 = "F"*8+"SHA1-"*8
+ expected_shadow_hash = "00000000"*155
+ expected_shadow_hash[168] = expected_salted_sha1
+ @provider.should_receive(:safe_dscl).with("read /Users/toor GeneratedUID").and_return(uuid)
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("\nAuthenticationAuthority: ;ShadowHash;\n")
+ @provider.modify_password
+ @output.string.strip.should match(/^0{168}(FFFFFFFF1C1AA7935D4E1190AFEC92343F31F7671FBF126D)0{1071}$/)
+ end
+ end
+
+ it "should write the output directly to the shadow hash file at /var/db/shadow/hash/GUID" do
+ shadow_file = StringIO.new
+ uuid = "B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA"
+ File.should_receive(:open).with("/var/db/shadow/hash/B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA",'w',0600).and_yield(shadow_file)
+ @provider.should_receive(:safe_dscl).with("read /Users/toor GeneratedUID").and_return(uuid)
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("\nAuthenticationAuthority: ;ShadowHash;\n")
+ @provider.modify_password
+ shadow_file.string.should match(/^0{168}[0-9A-F]{48}0{1071}$/)
+ end
+
+ it "should run safe_dscl append /Users/user AuthenticationAuthority ;ShadowHash; when no shadow hash set" do
+ shadow_file = StringIO.new
+ uuid = "B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA"
+ File.should_receive(:open).with("/var/db/shadow/hash/B398449E-CEE0-45E0-80F8-B0B5B1BFDEAA",'w',0600).and_yield(shadow_file)
+ @provider.should_receive(:safe_dscl).with("read /Users/toor GeneratedUID").and_return(uuid)
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("\nAuthenticationAuthority:\n")
+ @provider.should_receive(:safe_dscl).with("append /Users/toor AuthenticationAuthority ';ShadowHash;'")
+ @provider.modify_password
+ shadow_file.string.should match(/^0{168}[0-9A-F]{48}0{1071}$/)
+ end
+ end
+
+ describe "load_current_resource" do
+ it "should raise an error if the required binary /usr/bin/dscl doesn't exist" do
+ ::File.should_receive(:exists?).with("/usr/bin/dscl").and_return(false)
+ lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::User)
+ end
+
+ it "shouldn't raise an error if /usr/bin/dscl exists" do
+ ::File.stub!(:exists?).and_return(true)
+ lambda { @provider.load_current_resource }.should_not raise_error(Chef::Exceptions::User)
+ end
+ end
+
+ describe "when the user does not yet exist and chef is creating it" do
+ context "with a numeric gid" do
+ before do
+ @new_resource.comment "#mockssuck"
+ @new_resource.gid 1001
+ end
+
+ it "creates the user, comment field, sets uid, gid, configures the home directory, sets the shell, and sets the password" do
+ @provider.should_receive :dscl_create_user
+ @provider.should_receive :dscl_create_comment
+ @provider.should_receive :set_uid
+ @provider.should_receive :dscl_set_gid
+ @provider.should_receive :modify_home
+ @provider.should_receive :dscl_set_shell
+ @provider.should_receive :modify_password
+ @provider.create_user
+ end
+
+ it "creates the user and sets the comment field" do
+ @provider.should_receive(:safe_dscl).with("create /Users/toor").and_return(true)
+ @provider.dscl_create_user
+ end
+
+ it "sets the comment field" do
+ @provider.should_receive(:safe_dscl).with("create /Users/toor RealName '#mockssuck'").and_return(true)
+ @provider.dscl_create_comment
+ end
+
+ it "should run safe_dscl with create /Users/user PrimaryGroupID to set the users primary group" do
+ @provider.should_receive(:safe_dscl).with("create /Users/toor PrimaryGroupID '1001'").and_return(true)
+ @provider.dscl_set_gid
+ end
+
+ it "should run safe_dscl with create /Users/user UserShell to set the users login shell" do
+ @provider.should_receive(:safe_dscl).with("create /Users/toor UserShell '/usr/bin/false'").and_return(true)
+ @provider.dscl_set_shell
+ end
+ end
+
+ context "with a non-numeric gid" do
+ before do
+ @new_resource.comment "#mockssuck"
+ @new_resource.gid "newgroup"
+ end
+
+ it "should map the group name to a numeric ID when the group exists" do
+ @provider.should_receive(:safe_dscl).with("read /Groups/newgroup PrimaryGroupID").ordered.and_return("PrimaryGroupID: 1001\n")
+ @provider.should_receive(:safe_dscl).with("create /Users/toor PrimaryGroupID '1001'").ordered.and_return(true)
+ @provider.dscl_set_gid
+ end
+
+ it "should raise an exception when the group does not exist" do
+ shell_return = ShellCmdResult.new("<dscl_cmd> DS Error: -14136 (eDSRecordNotFound)", 'err', -14136)
+ @provider.should_receive(:shell_out).with('dscl . -read /Groups/newgroup PrimaryGroupID').and_return(shell_return)
+ lambda { @provider.dscl_set_gid }.should raise_error(Chef::Exceptions::GroupIDNotFound)
+ end
+ end
+ end
+
+ describe "when the user exists and chef is managing it" do
+ before do
+ @current_resource = @new_resource.dup
+ @provider.current_resource = @current_resource
+
+ # These are all different from @current_resource
+ @new_resource.username "mud"
+ @new_resource.uid 2342
+ @new_resource.gid 2342
+ @new_resource.home '/Users/death'
+ @new_resource.password 'goaway'
+ end
+
+ it "sets the user, comment field, uid, gid, moves the home directory, sets the shell, and sets the password" do
+ @provider.should_receive :dscl_create_user
+ @provider.should_receive :dscl_create_comment
+ @provider.should_receive :set_uid
+ @provider.should_receive :dscl_set_gid
+ @provider.should_receive :modify_home
+ @provider.should_receive :dscl_set_shell
+ @provider.should_receive :modify_password
+ @provider.create_user
+ end
+ end
+
+ describe "when changing the gid" do
+ before do
+ @current_resource = @new_resource.dup
+ @provider.current_resource = @current_resource
+
+ # This is different from @current_resource
+ @new_resource.gid 2342
+ end
+
+ it "sets the gid" do
+ @provider.should_receive :dscl_set_gid
+ @provider.manage_user
+ end
+ end
+
+ describe "when the user exists and chef is removing it" do
+ it "removes the user's home directory when the resource is configured to manage home" do
+ @new_resource.supports({ :manage_home => true })
+ @provider.should_receive(:safe_dscl).with("read /Users/toor").and_return("NFSHomeDirectory: /Users/fuuuuuuuuuuuuu")
+ @provider.should_receive(:safe_dscl).with("delete /Users/toor")
+ FileUtils.should_receive(:rm_rf).with("/Users/fuuuuuuuuuuuuu")
+ @provider.remove_user
+ end
+
+ it "removes the user from any group memberships" do
+ Etc.stub(:group).and_yield(OpenStruct.new(:name => 'ragefisters', :mem => 'toor'))
+ @provider.should_receive(:safe_dscl).with("delete /Users/toor")
+ @provider.should_receive(:safe_dscl).with("delete /Groups/ragefisters GroupMembership 'toor'")
+ @provider.remove_user
+ end
+ end
+
+ describe "when discovering if a user is locked" do
+
+ it "determines the user is not locked when dscl shows an AuthenticationAuthority without a DisabledUser field" do
+ @provider.should_receive(:safe_dscl).with("read /Users/toor")
+ @provider.should_not be_locked
+ end
+
+ it "determines the user is locked when dscl shows an AuthenticationAuthority with a DisabledUser field" do
+ @provider.should_receive(:safe_dscl).with('read /Users/toor').and_return("\nAuthenticationAuthority: ;DisabledUser;\n")
+ @provider.should be_locked
+ end
+
+ it "determines the user is not locked when dscl shows no AuthenticationAuthority" do
+ @provider.should_receive(:safe_dscl).with('read /Users/toor').and_return("\n")
+ @provider.should_not be_locked
+ end
+ end
+
+ describe "when locking the user" do
+ it "should run safe_dscl with append /Users/user AuthenticationAuthority ;DisabledUser; to lock the user account" do
+ @provider.should_receive(:safe_dscl).with("append /Users/toor AuthenticationAuthority ';DisabledUser;'")
+ @provider.lock_user
+ end
+ end
+
+ describe "when unlocking the user" do
+ it "removes DisabledUser from the authentication string" do
+ @provider.should_receive(:safe_dscl).with("read /Users/toor AuthenticationAuthority").and_return("\nAuthenticationAuthority: ;ShadowHash; ;DisabledUser;\n")
+ @provider.should_receive(:safe_dscl).with("create /Users/toor AuthenticationAuthority ';ShadowHash;'")
+ @provider.unlock_user
+ end
+ end
+end
diff --git a/spec/unit/provider/user/pw_spec.rb b/spec/unit/provider/user/pw_spec.rb
new file mode 100644
index 0000000000..b7503ea15f
--- /dev/null
+++ b/spec/unit/provider/user/pw_spec.rb
@@ -0,0 +1,235 @@
+#
+# Author:: Stephen Haynes (<sh@nomitor.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Provider::User::Pw do
+ before(:each) do
+ @node = Chef::Node.new
+ @events = Chef::EventDispatch::Dispatcher.new
+ @run_context = Chef::RunContext.new(@node, {}, @events)
+
+ @new_resource = Chef::Resource::User.new("adam")
+ @new_resource.comment "Adam Jacob"
+ @new_resource.uid 1000
+ @new_resource.gid 1000
+ @new_resource.home "/home/adam"
+ @new_resource.shell "/usr/bin/zsh"
+ @new_resource.password "abracadabra"
+
+ @new_resource.supports :manage_home => true
+
+ @current_resource = Chef::Resource::User.new("adam")
+ @current_resource.comment "Adam Jacob"
+ @current_resource.uid 1000
+ @current_resource.gid 1000
+ @current_resource.home "/home/adam"
+ @current_resource.shell "/usr/bin/zsh"
+ @current_resource.password "abracadabra"
+
+ @provider = Chef::Provider::User::Pw.new(@new_resource, @run_context)
+ @provider.current_resource = @current_resource
+ end
+
+ describe "setting options to the pw command" do
+ field_list = {
+ 'comment' => "-c",
+ 'home' => "-d",
+ 'gid' => "-g",
+ 'uid' => "-u",
+ 'shell' => "-s"
+ }
+ field_list.each do |attribute, option|
+ it "should check for differences in #{attribute} between the new and current resources" do
+ @current_resource.should_receive(attribute)
+ @new_resource.should_receive(attribute)
+ @provider.set_options
+ end
+
+ it "should set the option for #{attribute} if the new resources #{attribute} is not null" do
+ @new_resource.stub!(attribute).and_return("hola")
+ @provider.set_options.should eql(" #{@new_resource.username} #{option} '#{@new_resource.send(attribute)}' -m")
+ end
+
+ it "should set the option for #{attribute} if the new resources #{attribute} is not null, without homedir management" do
+ @new_resource.stub!(:supports).and_return({:manage_home => false})
+ @new_resource.stub!(attribute).and_return("hola")
+ @provider.set_options.should eql(" #{@new_resource.username} #{option} '#{@new_resource.send(attribute)}'")
+ end
+ end
+
+ it "should combine all the possible options" do
+ match_string = " adam"
+ field_list.sort{ |a,b| a[0] <=> b[0] }.each do |attribute, option|
+ @new_resource.stub!(attribute).and_return("hola")
+ match_string << " #{option} 'hola'"
+ end
+ match_string << " -m"
+ @provider.set_options.should eql(match_string)
+ end
+ end
+
+ describe "create_user" do
+ before(:each) do
+ @provider.stub!(:run_command).and_return(true)
+ @provider.stub!(:modify_password).and_return(true)
+ end
+
+ it "should run pw useradd with the return of set_options" do
+ @provider.should_receive(:run_command).with({ :command => "pw useradd adam -m" }).and_return(true)
+ @provider.create_user
+ end
+
+ it "should modify the password" do
+ @provider.should_receive(:modify_password).and_return(true)
+ @provider.create_user
+ end
+ end
+
+ describe "manage_user" do
+ before(:each) do
+ @provider.stub!(:run_command).and_return(true)
+ @provider.stub!(:modify_password).and_return(true)
+ end
+
+ it "should run pw usermod with the return of set_options" do
+ @provider.should_receive(:run_command).with({ :command => "pw usermod adam -m" }).and_return(true)
+ @provider.manage_user
+ end
+
+ it "should modify the password" do
+ @provider.should_receive(:modify_password).and_return(true)
+ @provider.create_user
+ end
+ end
+
+ describe "remove_user" do
+ it "should run pw userdel with the new resources user name" do
+ @new_resource.supports :manage_home => false
+ @provider.should_receive(:run_command).with({ :command => "pw userdel #{@new_resource.username}" }).and_return(true)
+ @provider.remove_user
+ end
+
+ it "should run pw userdel with the new resources user name and -r if manage_home is true" do
+ @provider.should_receive(:run_command).with({ :command => "pw userdel #{@new_resource.username} -r"}).and_return(true)
+ @provider.remove_user
+ end
+ end
+
+ describe "determining if the user is locked" do
+ it "should return true if user is locked" do
+ @current_resource.stub!(:password).and_return("*LOCKED*abracadabra")
+ @provider.check_lock.should eql(true)
+ end
+
+ it "should return false if user is not locked" do
+ @current_resource.stub!(:password).and_return("abracadabra")
+ @provider.check_lock.should eql(false)
+ end
+ end
+
+ describe "when locking the user" do
+ it "should run pw lock with the new resources username" do
+ @provider.should_receive(:run_command).with({ :command => "pw lock #{@new_resource.username}"})
+ @provider.lock_user
+ end
+ end
+
+ describe "when unlocking the user" do
+ it "should run pw unlock with the new resources username" do
+ @provider.should_receive(:run_command).with({ :command => "pw unlock #{@new_resource.username}"})
+ @provider.unlock_user
+ end
+ end
+
+ describe "when modifying the password" do
+ before(:each) do
+ @status = mock("Status", :exitstatus => 0)
+ @provider.stub!(:popen4).and_return(@status)
+ @pid, @stdin, @stdout, @stderr = nil, nil, nil, nil
+ end
+
+ it "should check for differences in password between the new and current resources" do
+ @current_resource.should_receive(:password)
+ @new_resource.should_receive(:password)
+ @provider.modify_password
+ end
+
+ describe "and the passwords are identical" do
+ before(:each) do
+ @new_resource.stub!(:password).and_return("abracadabra")
+ @current_resource.stub!(:password).and_return("abracadabra")
+ end
+
+ it "logs an appropriate message" do
+ Chef::Log.should_receive(:debug).with("user[adam] no change needed to password")
+ @provider.modify_password
+ end
+ end
+
+ describe "and the passwords are different" do
+ before(:each) do
+ @new_resource.stub!(:password).and_return("abracadabra")
+ @current_resource.stub!(:password).and_return("sesame")
+ end
+
+ it "should log an appropriate message" do
+ Chef::Log.should_receive(:debug).with("user[adam] updating password")
+ @provider.modify_password
+ end
+
+ it "should run pw usermod with the username and the option -H 0" do
+ @provider.should_receive(:popen4).with("pw usermod adam -H 0", :waitlast => true).and_return(@status)
+ @provider.modify_password
+ end
+
+ it "should send the new password to the stdin of pw usermod" do
+ @stdin = StringIO.new
+ @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ @provider.modify_password
+ @stdin.string.should == "abracadabra\n"
+ end
+
+ it "should raise an exception if pw usermod fails" do
+ @status.should_receive(:exitstatus).and_return(1)
+ lambda { @provider.modify_password }.should raise_error(Chef::Exceptions::User)
+ end
+
+ it "should not raise an exception if pw usermod succeeds" do
+ @status.should_receive(:exitstatus).and_return(0)
+ lambda { @provider.modify_password }.should_not raise_error(Chef::Exceptions::User)
+ end
+ end
+ end
+
+ describe "when loading the current state" do
+ before do
+ @provider.new_resource = Chef::Resource::User.new("adam")
+ end
+
+ it "should raise an error if the required binary /usr/sbin/pw doesn't exist" do
+ File.should_receive(:exists?).with("/usr/sbin/pw").and_return(false)
+ lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::User)
+ end
+
+ it "shouldn't raise an error if /usr/sbin/pw exists" do
+ File.stub!(:exists?).and_return(true)
+ lambda { @provider.load_current_resource }.should_not raise_error(Chef::Exceptions::User)
+ end
+ end
+end
diff --git a/spec/unit/provider/user/useradd_spec.rb b/spec/unit/provider/user/useradd_spec.rb
new file mode 100644
index 0000000000..ea6caf6e0a
--- /dev/null
+++ b/spec/unit/provider/user/useradd_spec.rb
@@ -0,0 +1,386 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2008, 2010 Opscode, Inc.
+#
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Provider::User::Useradd do
+ before(:each) do
+ @node = Chef::Node.new
+ @events = Chef::EventDispatch::Dispatcher.new
+ @run_context = Chef::RunContext.new(@node, {}, @events)
+
+ @new_resource = Chef::Resource::User.new("adam", @run_context)
+ @new_resource.comment "Adam Jacob"
+ @new_resource.uid 1000
+ @new_resource.gid 1000
+ @new_resource.home "/home/adam"
+ @new_resource.shell "/usr/bin/zsh"
+ @new_resource.password "abracadabra"
+ @new_resource.system false
+ @new_resource.manage_home false
+ @new_resource.non_unique false
+ @current_resource = Chef::Resource::User.new("adam", @run_context)
+ @current_resource.comment "Adam Jacob"
+ @current_resource.uid 1000
+ @current_resource.gid 1000
+ @current_resource.home "/home/adam"
+ @current_resource.shell "/usr/bin/zsh"
+ @current_resource.password "abracadabra"
+ @current_resource.system false
+ @current_resource.manage_home false
+ @current_resource.non_unique false
+ @current_resource.supports({:manage_home => false, :non_unique => false})
+ @provider = Chef::Provider::User::Useradd.new(@new_resource, @run_context)
+ @provider.current_resource = @current_resource
+ end
+
+ describe "when setting option" do
+ field_list = {
+ 'comment' => "-c",
+ 'gid' => "-g",
+ 'uid' => "-u",
+ 'shell' => "-s",
+ 'password' => "-p"
+ }
+
+ field_list.each do |attribute, option|
+ it "should check for differences in #{attribute} between the new and current resources" do
+ @current_resource.should_receive(attribute)
+ @new_resource.should_receive(attribute)
+ @provider.universal_options
+ end
+
+ it "should set the option for #{attribute} if the new resources #{attribute} is not nil" do
+ @new_resource.stub!(attribute).and_return("hola")
+ @provider.universal_options.should eql(" #{option} 'hola'")
+ end
+
+ it "should set the option for #{attribute} if the new resources #{attribute} is not nil, without homedir management" do
+ @new_resource.stub!(:supports).and_return({:manage_home => false,
+ :non_unique => false})
+ @new_resource.stub!(attribute).and_return("hola")
+ @provider.universal_options.should eql(" #{option} 'hola'")
+ end
+
+ it "should set the option for #{attribute} if the new resources #{attribute} is not nil, without homedir management (using real attributes)" do
+ @new_resource.stub!(:manage_home).and_return(false)
+ @new_resource.stub!(:non_unique).and_return(false)
+ @new_resource.stub!(attribute).and_return("hola")
+ @provider.universal_options.should eql(" #{option} 'hola'")
+ end
+ end
+
+ it "should combine all the possible options" do
+ match_string = ""
+ field_list.sort{ |a,b| a[0] <=> b[0] }.each do |attribute, option|
+ @new_resource.stub!(attribute).and_return("hola")
+ match_string << " #{option} 'hola'"
+ end
+ @provider.universal_options.should eql(match_string)
+ end
+
+ describe "when we want to create a system user" do
+ before do
+ @new_resource.manage_home(true)
+ @new_resource.non_unique(false)
+ end
+
+ it "should set useradd -r" do
+ @new_resource.system(true)
+ @provider.useradd_options.should == " -r"
+ end
+ end
+
+ describe "when the resource has a different home directory and supports home directory management" do
+ before do
+ @new_resource.stub!(:home).and_return("/wowaweea")
+ @new_resource.stub!(:supports).and_return({:manage_home => true,
+ :non_unique => false})
+ end
+
+ it "should set -m -d /homedir" do
+ @provider.universal_options.should == " -m -d '/wowaweea'"
+ @provider.useradd_options.should == ""
+ end
+ end
+
+ describe "when the resource has a different home directory and supports home directory management (using real attributes)" do
+ before do
+ @new_resource.stub!(:home).and_return("/wowaweea")
+ @new_resource.stub!(:manage_home).and_return(true)
+ @new_resource.stub!(:non_unique).and_return(false)
+ end
+
+ it "should set -m -d /homedir" do
+ @provider.universal_options.should eql(" -m -d '/wowaweea'")
+ @provider.useradd_options.should == ""
+ end
+ end
+
+ describe "when the resource supports non_unique ids" do
+ before do
+ @new_resource.stub!(:supports).and_return({:manage_home => false,
+ :non_unique => true})
+ end
+
+ it "should set -m -o" do
+ @provider.universal_options.should eql(" -o")
+ end
+ end
+
+ describe "when the resource supports non_unique ids (using real attributes)" do
+ before do
+ @new_resource.stub!(:manage_home).and_return(false)
+ @new_resource.stub!(:non_unique).and_return(true)
+ end
+
+ it "should set -m -o" do
+ @provider.universal_options.should eql(" -o")
+ end
+ end
+ end
+
+ describe "when creating a user" do
+ before(:each) do
+ @current_resource = Chef::Resource::User.new(@new_resource.name, @run_context)
+ @current_resource.username(@new_resource.username)
+ @provider.current_resource = @current_resource
+ @provider.new_resource.manage_home true
+ @provider.new_resource.home "/Users/mud"
+ @provider.new_resource.gid '23'
+ end
+
+ it "runs useradd with the computed command options" do
+ command = "useradd -c 'Adam Jacob' -g '23' -p 'abracadabra' -s '/usr/bin/zsh' -u '1000' -m -d '/Users/mud' adam"
+ @provider.should_receive(:run_command).with({ :command => command }).and_return(true)
+ @provider.create_user
+ end
+
+ describe "and home is not specified for new system user resource" do
+
+ before do
+ @provider.new_resource.system true
+ # there is no public API to set attribute's value to nil
+ @provider.new_resource.instance_variable_set("@home", nil)
+ end
+
+ it "should not include -m or -d in the command options" do
+ command = "useradd -c 'Adam Jacob' -g '23' -p 'abracadabra' -s '/usr/bin/zsh' -u '1000' -r adam"
+ @provider.should_receive(:run_command).with({ :command => command }).and_return(true)
+ @provider.create_user
+ end
+
+ end
+
+ end
+
+ describe "when managing a user" do
+ before(:each) do
+ @provider.new_resource.manage_home true
+ @provider.new_resource.home "/Users/mud"
+ @provider.new_resource.gid '23'
+ end
+
+ # CHEF-3423, -m must come before the username
+ it "runs usermod with the computed command options" do
+ @provider.should_receive(:run_command).with({ :command => "usermod -g '23' -m -d '/Users/mud' adam" }).and_return(true)
+ @provider.manage_user
+ end
+
+ it "does not set the -r option to usermod" do
+ @new_resource.system(true)
+ @provider.should_receive(:run_command).with({ :command => "usermod -g '23' -m -d '/Users/mud' adam" }).and_return(true)
+ @provider.manage_user
+ end
+
+ it "CHEF-3429: does not set -m if we aren't changing the home directory" do
+ @provider.should_receive(:updating_home?).and_return(false)
+ @provider.should_receive(:run_command).with({ :command => "usermod -g '23' adam" }).and_return(true)
+ @provider.manage_user
+ end
+ end
+
+ describe "when removing a user" do
+
+ it "should run userdel with the new resources user name" do
+ @provider.should_receive(:run_command).with({ :command => "userdel #{@new_resource.username}" }).and_return(true)
+ @provider.remove_user
+ end
+
+ it "should run userdel with the new resources user name and -r if manage_home is true" do
+ @new_resource.stub!(:supports).and_return({ :manage_home => true,
+ :non_unique => false})
+ @provider.should_receive(:run_command).with({ :command => "userdel -r #{@new_resource.username}"}).and_return(true)
+ @provider.remove_user
+ end
+
+ it "should run userdel with the new resources user name if non_unique is true" do
+ @new_resource.stub!(:supports).and_return({ :manage_home => false,
+ :non_unique => true})
+ @provider.should_receive(:run_command).with({ :command => "userdel #{@new_resource.username}"}).and_return(true)
+ @provider.remove_user
+ end
+ end
+
+ describe "when checking the lock" do
+ before(:each) do
+ # @node = Chef::Node.new
+ # @new_resource = mock("Chef::Resource::User",
+ # :nil_object => true,
+ # :username => "adam"
+ # )
+ @status = mock("Status", :exitstatus => 0)
+ #@provider = Chef::Provider::User::Useradd.new(@node, @new_resource)
+ @provider.stub!(:popen4).and_return(@status)
+ @stdin = mock("STDIN", :nil_object => true)
+ @stdout = mock("STDOUT", :nil_object => true)
+ @stdout.stub!(:gets).and_return("root P 09/02/2008 0 99999 7 -1")
+ @stderr = mock("STDERR", :nil_object => true)
+ @pid = mock("PID", :nil_object => true)
+ end
+
+ it "should call passwd -S to check the lock status" do
+ @provider.should_receive(:popen4).with("passwd -S #{@new_resource.username}").and_return(@status)
+ @provider.check_lock
+ end
+
+ it "should get the first line of passwd -S STDOUT" do
+ @provider.should_receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ @stdout.should_receive(:gets).and_return("root P 09/02/2008 0 99999 7 -1")
+ @provider.check_lock
+ end
+
+ it "should return false if status begins with P" do
+ @provider.should_receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ @provider.check_lock.should eql(false)
+ end
+
+ it "should return false if status begins with N" do
+ @stdout.stub!(:gets).and_return("root N")
+ @provider.should_receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ @provider.check_lock.should eql(false)
+ end
+
+ it "should return true if status begins with L" do
+ @stdout.stub!(:gets).and_return("root L")
+ @provider.should_receive(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ @provider.check_lock.should eql(true)
+ end
+
+ it "should raise a Chef::Exceptions::User if passwd -S fails on anything other than redhat/centos" do
+ @node.automatic_attrs[:platform] = 'ubuntu'
+ @status.should_receive(:exitstatus).and_return(1)
+ lambda { @provider.check_lock }.should raise_error(Chef::Exceptions::User)
+ end
+
+ ['redhat', 'centos'].each do |os|
+ it "should not raise a Chef::Exceptions::User if passwd -S exits with 1 on #{os} and the passwd package is version 0.73-1" do
+ @node.automatic_attrs[:platform] = os
+ @stdout.stub!(:gets).and_return("passwd-0.73-1\n")
+ @status.should_receive(:exitstatus).twice.and_return(1)
+ @provider.should_receive(:popen4).with("passwd -S #{@new_resource.username}")
+ @provider.should_receive(:popen4).with("rpm -q passwd").and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ lambda { @provider.check_lock }.should_not raise_error(Chef::Exceptions::User)
+ end
+
+ it "should raise a Chef::Exceptions::User if passwd -S exits with 1 on #{os} and the passwd package is not version 0.73-1" do
+ @node.automatic_attrs[:platform] = os
+ @stdout.stub!(:gets).and_return("passwd-0.73-2\n")
+ @status.should_receive(:exitstatus).twice.and_return(1)
+ @provider.should_receive(:popen4).with("passwd -S #{@new_resource.username}")
+ @provider.should_receive(:popen4).with("rpm -q passwd").and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status)
+ lambda { @provider.check_lock }.should raise_error(Chef::Exceptions::User)
+ end
+
+ it "should raise a Chef::Exceptions::User if passwd -S exits with something other than 0 or 1 on #{os}" do
+ @node.automatic_attrs[:platform] = os
+ @status.should_receive(:exitstatus).twice.and_return(2)
+ lambda { @provider.check_lock }.should raise_error(Chef::Exceptions::User)
+ end
+ end
+ end
+
+ describe "when locking the user" do
+ it "should run usermod -L with the new resources username" do
+ @provider.should_receive(:run_command).with({ :command => "usermod -L #{@new_resource.username}"})
+ @provider.lock_user
+ end
+ end
+
+ describe "when unlocking the user" do
+ it "should run usermod -L with the new resources username" do
+ @provider.should_receive(:run_command).with({ :command => "usermod -U #{@new_resource.username}"})
+ @provider.unlock_user
+ end
+ end
+
+ describe "when checking if home needs updating" do
+ [
+ {
+ "action" => "should return false if home matches",
+ "current_resource_home" => [ "/home/laurent" ],
+ "new_resource_home" => [ "/home/laurent" ],
+ "expected_result" => false
+ },
+ {
+ "action" => "should return true if home doesn't match",
+ "current_resource_home" => [ "/home/laurent" ],
+ "new_resource_home" => [ "/something/else" ],
+ "expected_result" => true
+ },
+ {
+ "action" => "should return false if home only differs by trailing slash",
+ "current_resource_home" => [ "/home/laurent" ],
+ "new_resource_home" => [ "/home/laurent/", "/home/laurent" ],
+ "expected_result" => false
+ },
+ {
+ "action" => "should return false if home is an equivalent path",
+ "current_resource_home" => [ "/home/laurent" ],
+ "new_resource_home" => [ "/home/./laurent", "/home/laurent" ],
+ "expected_result" => false
+ },
+ ].each do |home_check|
+ it home_check["action"] do
+ @provider.current_resource.home home_check["current_resource_home"].first
+ @current_home_mock = mock("Pathname")
+ @provider.new_resource.home home_check["new_resource_home"].first
+ @new_home_mock = mock("Pathname")
+
+ Pathname.should_receive(:new).with(@current_resource.home).and_return(@current_home_mock)
+ @current_home_mock.should_receive(:cleanpath).and_return(home_check["current_resource_home"].last)
+ Pathname.should_receive(:new).with(@new_resource.home).and_return(@new_home_mock)
+ @new_home_mock.should_receive(:cleanpath).and_return(home_check["new_resource_home"].last)
+
+ @provider.updating_home?.should == home_check["expected_result"]
+ end
+ end
+ it "should return true if the current home does not exist but a home is specified by the new resource" do
+ @new_resource = Chef::Resource::User.new("adam", @run_context)
+ @current_resource = Chef::Resource::User.new("adam", @run_context)
+ @provider = Chef::Provider::User::Useradd.new(@new_resource, @run_context)
+ @provider.current_resource = @current_resource
+ @current_resource.home nil
+ @new_resource.home "/home/kitten"
+
+ @provider.updating_home?.should == true
+ end
+ end
+end
diff --git a/spec/unit/provider/user/windows_spec.rb b/spec/unit/provider/user/windows_spec.rb
new file mode 100644
index 0000000000..6ede11b28c
--- /dev/null
+++ b/spec/unit/provider/user/windows_spec.rb
@@ -0,0 +1,178 @@
+#
+# Author:: Doug MacEachern (<dougm@vmware.com>)
+# Copyright:: Copyright (c) 2010 VMware, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+class Chef
+ class Util
+ class Windows
+ class NetUser
+ end
+ end
+ end
+end
+
+describe Chef::Provider::User::Windows do
+ before(:each) do
+ @node = Chef::Node.new
+ @new_resource = Chef::Resource::User.new("monkey")
+ @events = Chef::EventDispatch::Dispatcher.new
+ @run_context = Chef::RunContext.new(@node, {}, @events)
+ @current_resource = Chef::Resource::User.new("monkey")
+
+ @net_user = mock("Chef::Util::Windows::NetUser")
+ Chef::Util::Windows::NetUser.stub!(:new).and_return(@net_user)
+
+ @provider = Chef::Provider::User::Windows.new(@new_resource, @run_context)
+ @provider.current_resource = @current_resource
+ end
+
+ describe "when comparing the user's current attributes to the desired attributes" do
+ before do
+ @new_resource.comment "Adam Jacob"
+ @new_resource.uid 1000
+ @new_resource.gid 1000
+ @new_resource.home "/home/adam"
+ @new_resource.shell "/usr/bin/zsh"
+ @new_resource.password "abracadabra"
+
+ @provider.current_resource = @new_resource.clone
+ end
+ describe "and the attributes match" do
+ it "doesn't set the comment field to be updated" do
+ @provider.set_options.should_not have_key(:full_name)
+ end
+
+ it "doesn't set the home directory to be updated" do
+ @provider.set_options.should_not have_key(:home_dir)
+ end
+
+ it "doesn't set the group id to be updated" do
+ @provider.set_options.should_not have_key(:primary_group_id)
+ end
+
+ it "doesn't set the user id to be updated" do
+ @provider.set_options.should_not have_key(:user_id)
+ end
+
+ it "doesn't set the shell to be updated" do
+ @provider.set_options.should_not have_key(:script_path)
+ end
+
+ it "doesn't set the password to be updated" do
+ @provider.set_options.should_not have_key(:password)
+ end
+
+ end
+
+ describe "and the attributes do not match" do
+ before do
+ @current_resource = Chef::Resource::User.new("adam")
+ @current_resource.comment "Adam Jacob-foo"
+ @current_resource.uid 1111
+ @current_resource.gid 1111
+ @current_resource.home "/home/adam-foo"
+ @current_resource.shell "/usr/bin/tcsh"
+ @current_resource.password "foobarbaz"
+ @provider.current_resource = @current_resource
+ end
+
+ it "marks the full_name field to be updated" do
+ @provider.set_options[:full_name].should == "Adam Jacob"
+ end
+
+ it "marks the home_dir attribute to be updated" do
+ @provider.set_options[:home_dir].should == '/home/adam'
+ end
+
+ it "marks the primary_group_id attribute to be updated" do
+ @provider.set_options[:primary_group_id].should == 1000
+ end
+
+ it "marks the user_id attribute to be updated" do
+ @provider.set_options[:user_id].should == 1000
+ end
+
+ it "marks the script_path attribute to be updated" do
+ @provider.set_options[:script_path].should == '/usr/bin/zsh'
+ end
+
+ it "marks the password attribute to be updated" do
+ @provider.set_options[:password].should == 'abracadabra'
+ end
+ end
+ end
+
+ describe "when creating the user" do
+ it "should call @net_user.add with the return of set_options" do
+ @provider.stub!(:set_options).and_return(:name=> "monkey")
+ @net_user.should_receive(:add).with(:name=> "monkey")
+ @provider.create_user
+ end
+ end
+
+ describe "manage_user" do
+ before(:each) do
+ @provider.stub!(:set_options).and_return(:name=> "monkey")
+ end
+
+ it "should call @net_user.update with the return of set_options" do
+ @net_user.should_receive(:update).with(:name=> "monkey")
+ @provider.manage_user
+ end
+ end
+
+ describe "when removing the user" do
+ it "should call @net_user.delete" do
+ @net_user.should_receive(:delete)
+ @provider.remove_user
+ end
+ end
+
+ describe "when checking if the user is locked" do
+ before(:each) do
+ @current_resource.password "abracadabra"
+ end
+
+ it "should return true if user is locked" do
+ @net_user.stub!(:check_enabled).and_return(true)
+ @provider.check_lock.should eql(true)
+ end
+
+ it "should return false if user is not locked" do
+ @net_user.stub!(:check_enabled).and_return(false)
+ @provider.check_lock.should eql(false)
+ end
+ end
+
+ describe "locking the user" do
+ it "should call @net_user.disable_account" do
+ @net_user.stub!(:check_enabled).and_return(true)
+ @net_user.should_receive(:disable_account)
+ @provider.lock_user
+ end
+ end
+
+ describe "unlocking the user" do
+ it "should call @net_user.enable_account" do
+ @net_user.stub!(:check_enabled).and_return(false)
+ @net_user.should_receive(:enable_account)
+ @provider.unlock_user
+ end
+ end
+end