# # Author:: Chirag Jog () # Author:: Siddheshwar More () # Copyright:: Copyright (c) 2013 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" require "functional/resource/base" require "chef/mixin/shell_out" # Chef::Resource::Group are turned off on Mac OS X 10.6 due to caching # issues around Etc.getgrnam() not picking up the group membership # changes that are done on the system. Etc.endgrent is not functioning # correctly on certain 10.6 boxes. describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supported_on_mac_osx_106 do include Chef::Mixin::ShellOut def group_should_exist(group) case ohai[:platform_family] when "debian", "fedora", "rhel", "suse", "gentoo", "slackware", "arch" expect { Etc::getgrnam(group) }.not_to raise_error expect(group).to eq(Etc::getgrnam(group).name) when "windows" expect { Chef::Util::Windows::NetGroup.new(group).local_get_members }.not_to raise_error end end def user_exist_in_group?(user) case ohai[:platform_family] when "windows" user_sid = sid_string_from_user(user) user_sid.nil? ? false : Chef::Util::Windows::NetGroup.new(group_name).local_get_members.include?(user_sid) when "mac_os_x" membership_info = shell_out("dscl . -read /Groups/#{group_name}").stdout members = membership_info.split(" ") members.shift # Get rid of GroupMembership: string members.include?(user) else Etc::getgrnam(group_name).mem.include?(user) end end def group_should_not_exist(group) case ohai[:platform_family] when "debian", "fedora", "rhel", "suse", "gentoo", "slackware", "arch" expect { Etc::getgrnam(group) }.to raise_error(ArgumentError, "can't find group for #{group}") when "windows" expect { Chef::Util::Windows::NetGroup.new(group).local_get_members }.to raise_error(ArgumentError, "The group name could not be found.") end end def compare_gid(resource, gid) return resource.gid == Etc::getgrnam(resource.name).gid if unix? end def sid_string_from_user(user) begin sid = Chef::ReservedNames::Win32::Security.lookup_account_name(user) rescue Chef::Exceptions::Win32APIError sid = nil end sid.nil? ? nil : sid[1].to_s end def windows_domain_user?(user_name) domain, user = user_name.split('\\') if user && domain != "." computer_name = ENV["computername"] domain.downcase != computer_name.downcase end end def user(username) usr = Chef::Resource::User.new("#{username}", run_context) if ohai[:platform_family] == "windows" usr.password("ComplexPass11!") end usr end def create_user(username) user(username).run_action(:create) if ! windows_domain_user?(username) # TODO: User should exist end def remove_user(username) user(username).run_action(:remove) if ! windows_domain_user?(username) # TODO: User shouldn't exist end shared_examples_for "correct group management" do def add_members_to_group(members) temp_resource = group_resource.dup temp_resource.members(members) temp_resource.excluded_members([ ]) temp_resource.append(true) temp_resource.run_action(:modify) members.each do |member| expect(user_exist_in_group?(member)).to eq(true) end end def create_group temp_resource = group_resource.dup temp_resource.members([ ]) temp_resource.excluded_members([ ]) temp_resource.run_action(:create) group_should_exist(group_name) included_members.each do |member| expect(user_exist_in_group?(member)).to eq(false) end end before(:each) do create_group end after(:each) do group_resource.run_action(:remove) group_should_not_exist(group_name) end # dscl doesn't perform any error checking and will let you add users that don't exist. describe "when no users exist", :not_supported_on_mac_osx do describe "when append is not set" do # excluded_members can only be used when append is set. It is ignored otherwise. let(:excluded_members) { [] } it "should raise an error" do expect { group_resource.run_action(tested_action) }.to raise_error() end end describe "when append is set" do before do group_resource.append(true) end it "should raise an error" do expect { group_resource.run_action(tested_action) }.to raise_error() end end end describe "when the users exist" do before do (spec_members).each do |member| create_user(member) end end after do (spec_members).each do |member| remove_user(member) end end describe "when append is not set" do it "should set the group to to contain given members" do group_resource.run_action(tested_action) included_members.each do |member| expect(user_exist_in_group?(member)).to eq(true) end (spec_members - included_members).each do |member| expect(user_exist_in_group?(member)).to eq(false) end end describe "when group already contains some users" do before do add_members_to_group([included_members[0]]) add_members_to_group(spec_members - included_members) end it "should remove all existing users and only add the new users to the group" do group_resource.run_action(tested_action) included_members.each do |member| expect(user_exist_in_group?(member)).to eq(true) end (spec_members - included_members).each do |member| expect(user_exist_in_group?(member)).to eq(false) end end end end describe "when append is set" do before(:each) do group_resource.append(true) end it "should add included members to the group" do group_resource.run_action(tested_action) included_members.each do |member| expect(user_exist_in_group?(member)).to eq(true) end excluded_members.each do |member| expect(user_exist_in_group?(member)).to eq(false) end end describe "when group already contains some users" do before(:each) do add_members_to_group([included_members[0], excluded_members[0]]) end it "should add the included users and remove excluded users" do group_resource.run_action(tested_action) included_members.each do |member| expect(user_exist_in_group?(member)).to eq(true) end excluded_members.each do |member| expect(user_exist_in_group?(member)).to eq(false) end end end end end end shared_examples_for "an expected invalid domain error case" do let(:invalid_domain_user_name) { "no space\\administrator" } let(:nonexistent_domain_user_name) { "xxfakedom\\administrator" } before(:each) do group_resource.members [] group_resource.excluded_members [] group_resource.append(true) group_resource.run_action(:create) group_should_exist(group_name) end after(:each) do group_resource.run_action(:remove) end # TODO: The ones below might actually return ArgumentError now - but I don't have # a way to verify that. Change it and delete this comment if that's the case. describe "when updating membership" do it "raises an error for a non well-formed domain name" do group_resource.members [invalid_domain_user_name] expect { group_resource.run_action(tested_action) }.to raise_error Chef::Exceptions::Win32APIError end it "raises an error for a nonexistent domain" do group_resource.members [nonexistent_domain_user_name] expect { group_resource.run_action(tested_action) }.to raise_error Chef::Exceptions::Win32APIError end end describe "when removing members" do it "raises an error for a non well-formed domain name" do group_resource.excluded_members [invalid_domain_user_name] expect { group_resource.run_action(tested_action) }.to raise_error Chef::Exceptions::Win32APIError end it "raises an error for a nonexistent domain" do group_resource.excluded_members [nonexistent_domain_user_name] expect { group_resource.run_action(tested_action) }.to raise_error Chef::Exceptions::Win32APIError end end end let(:group_name) { "group#{SecureRandom.random_number(9999)}" } let(:included_members) { nil } let(:excluded_members) { nil } let(:group_resource) { group = Chef::Resource::Group.new(group_name, run_context) group.members(included_members) group.excluded_members(excluded_members) group } it "append should be false by default" do expect(group_resource.append).to eq(false) end describe "group create action" do after(:each) do group_resource.run_action(:remove) group_should_not_exist(group_name) end it "should create a group" do group_resource.run_action(:create) group_should_exist(group_name) end describe "when group name is length 256", :windows_only do let!(:group_name) { "theoldmanwalkingdownthestreetalwayshadagood\ smileonhisfacetheoldmanwalkingdownthestreetalwayshadagoodsmileonhisface\ theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking\ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestree" } it "should create a group" do group_resource.run_action(:create) group_should_exist(group_name) end end describe "when group name length is more than 256", :windows_only do let!(:group_name) { "theoldmanwalkingdownthestreetalwayshadagood\ smileonhisfacetheoldmanwalkingdownthestreetalwayshadagoodsmileonhisface\ theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking\ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" } it "should not create a group" do expect { group_resource.run_action(:create) }.to raise_error(ArgumentError) group_should_not_exist(group_name) end end # not_supported_on_solaris because of the use of excluded_members describe "should raise an error when same member is included in the members and excluded_members", :not_supported_on_solaris do it "should raise an error" do invalid_resource = group_resource.dup invalid_resource.members(["Jack"]) invalid_resource.excluded_members(["Jack"]) expect { invalid_resource.run_action(:create)}.to raise_error(Chef::Exceptions::ConflictingMembersInGroup) end end end describe "group remove action" do describe "when there is a group" do before do group_resource.run_action(:create) group_should_exist(group_name) end it "should remove a group" do group_resource.run_action(:remove) group_should_not_exist(group_name) end end describe "when there is no group" do it "should be no-op" do group_resource.run_action(:remove) group_should_not_exist(group_name) end end end describe "group modify action", :not_supported_on_solaris do let(:spec_members){ ["mnou5sdz", "htulrvwq", "x4c3g1lu"] } let(:included_members) { [spec_members[0], spec_members[1]] } let(:excluded_members) { [spec_members[2]] } let(:tested_action) { :modify } describe "when there is no group" do it "should raise an error" do expect { group_resource.run_action(:modify) }.to raise_error end end describe "when there is a group" do it_behaves_like "correct group management" end describe "when running on Windows", :windows_only do describe "when members are Active Directory domain identities", :windows_domain_joined_only do let(:computer_domain) { ohai[:kernel]["cs_info"]["domain"].split(".")[0] } let(:spec_members){ ["#{computer_domain}\\Domain Admins", "#{computer_domain}\\Domain Users", "#{computer_domain}\\Domain Computers"] } include_examples "correct group management" end it_behaves_like "an expected invalid domain error case" end end describe "group manage action", :not_supported_on_solaris do let(:spec_members){ ["mnou5sdz", "htulrvwq", "x4c3g1lu"] } let(:included_members) { [spec_members[0], spec_members[1]] } let(:excluded_members) { [spec_members[2]] } let(:tested_action) { :manage } describe "when there is no group" do before(:each) do group_resource.run_action(:remove) group_should_not_exist(group_name) end it "raises an error on modify" do expect { group_resource.run_action(:modify) }.to raise_error end it "does not raise an error on manage" do expect { group_resource.run_action(:manage) }.not_to raise_error end end describe "when there is a group" do it_behaves_like "correct group management" end describe "running on windows", :windows_only do describe "when members are Windows domain identities", :windows_domain_joined_only do let(:computer_domain) { ohai[:kernel]["cs_info"]["domain"].split(".")[0] } let(:spec_members){ ["#{computer_domain}\\Domain Admins", "#{computer_domain}\\Domain Users", "#{computer_domain}\\Domain Computers"] } include_examples "correct group management" end it_behaves_like "an expected invalid domain error case" end end describe "group resource with Usermod provider", :solaris_only do describe "when excluded_members is set" do let(:excluded_members) { ["x4c3g1lu"] } it ":manage should raise an error" do expect {group_resource.run_action(:manage) }.to raise_error end it ":modify should raise an error" do expect {group_resource.run_action(:modify) }.to raise_error end it ":create should raise an error" do expect {group_resource.run_action(:create) }.to raise_error end end describe "when append is not set" do let(:included_members) { ["gordon", "eric"] } before(:each) do group_resource.append(false) end it ":manage should raise an error" do expect {group_resource.run_action(:manage) }.to raise_error end it ":modify should raise an error" do expect {group_resource.run_action(:modify) }.to raise_error end end end end