diff --git a/lib/puppet/provider/file/windows.rb b/lib/puppet/provider/file/windows.rb index bb31df98c..235a3025f 100644 --- a/lib/puppet/provider/file/windows.rb +++ b/lib/puppet/provider/file/windows.rb @@ -1,107 +1,92 @@ Puppet::Type.type(:file).provide :windows do desc "Uses Microsoft Windows functionality to manage file ownership and permissions." confine :operatingsystem => :windows include Puppet::Util::Warnings if Puppet.features.microsoft_windows? require 'puppet/util/windows' require 'puppet/util/adsi' include Puppet::Util::Windows::Security end - ERROR_INVALID_SID_STRUCTURE = 1337 + # Determine if the account is valid, and if so, return the UID + def name2id(value) + Puppet::Util::Windows::Security.name_to_sid(value) + end + # If it's a valid SID, get the name. Otherwise, it's already a name, + # so just return it. def id2name(id) - # If it's a valid sid, get the name. Otherwise, it's already a name, so - # just return it. begin - if string_to_sid_ptr(id) - name = nil - Puppet::Util::ADSI.execquery( - "SELECT Name FROM Win32_Account WHERE SID = '#{id}' - AND LocalAccount = true" - ).each { |a| name ||= a.name } - return name + if Puppet::Util::Windows::Security.string_to_sid_ptr(id) + return Puppet::Util::Windows::Security.sid_to_name(id) end rescue Puppet::Util::Windows::Error => e raise unless e.code == ERROR_INVALID_SID_STRUCTURE end id end - # Determine if the account is valid, and if so, return the UID - def name2id(value) - # If it's a valid sid, then return it. Else, it's a name we need to convert - # to sid. - begin - return value if string_to_sid_ptr(value) - rescue Puppet::Util::Windows::Error => e - raise unless e.code == ERROR_INVALID_SID_STRUCTURE - end - - Puppet::Util::ADSI.sid_for_account(value) rescue nil - end - # We use users and groups interchangeably, so use the same methods for both # (the type expects different methods, so we have to oblige). alias :uid2name :id2name alias :gid2name :id2name alias :name2gid :name2id alias :name2uid :name2id def owner return :absent unless resource.exist? get_owner(resource[:path]) end def owner=(should) begin set_owner(should, resource[:path]) rescue => detail raise Puppet::Error, "Failed to set owner to '#{should}': #{detail}" end end def group return :absent unless resource.exist? get_group(resource[:path]) end def group=(should) begin set_group(should, resource[:path]) rescue => detail raise Puppet::Error, "Failed to set group to '#{should}': #{detail}" end end def mode if resource.exist? mode = get_mode(resource[:path]) mode ? mode.to_s(8) : :absent else :absent end end def mode=(value) begin set_mode(value.to_i(8), resource[:path]) rescue => detail error = Puppet::Error.new("failed to set mode #{mode} on #{resource[:path]}: #{detail.message}") error.set_backtrace detail.backtrace raise error end :file_changed end def validate if [:owner, :group, :mode].any?{|p| resource[p]} and !supports_acl?(resource[:path]) resource.fail("Can only manage owner, group, and mode on filesystems that support Windows ACLs, such as NTFS") end end end diff --git a/spec/unit/provider/file/windows_spec.rb b/spec/unit/provider/file/windows_spec.rb index f9ab78615..aec62cb61 100755 --- a/spec/unit/provider/file/windows_spec.rb +++ b/spec/unit/provider/file/windows_spec.rb @@ -1,154 +1,153 @@ #!/usr/bin/env rspec require 'spec_helper' if Puppet.features.microsoft_windows? require 'puppet/util/windows' class WindowsSecurity extend Puppet::Util::Windows::Security end end describe Puppet::Type.type(:file).provider(:windows), :if => Puppet.features.microsoft_windows? do include PuppetSpec::Files let(:path) { tmpfile('windows_file_spec') } let(:resource) { Puppet::Type.type(:file).new :path => path, :mode => 0777, :provider => described_class.name } let(:provider) { resource.provider } + let(:sid) { 'S-1-1-50' } + let(:account) { 'quinn' } describe "#mode" do it "should return a string with the higher-order bits stripped away" do FileUtils.touch(path) WindowsSecurity.set_mode(0644, path) provider.mode.should == '644' end it "should return absent if the file doesn't exist" do provider.mode.should == :absent end end describe "#mode=" do it "should chmod the file to the specified value" do FileUtils.touch(path) WindowsSecurity.set_mode(0644, path) provider.mode = '0755' provider.mode.should == '755' end it "should pass along any errors encountered" do expect do provider.mode = '644' end.to raise_error(Puppet::Error, /failed to set mode/) end end describe "#id2name" do it "should return the name of the user identified by the sid" do - result = [stub('user', :name => 'quinn')] - Puppet::Util::ADSI.stubs(:execquery).returns(result) + Puppet::Util::Windows::Security.expects(:string_to_sid_ptr).with(sid).returns(true) + Puppet::Util::Windows::Security.expects(:sid_to_name).with(sid).returns(account) - provider.id2name('S-1-1-50').should == 'quinn' + provider.id2name(sid).should == account end it "should return the argument if it's already a name" do - provider.id2name('flannigan').should == 'flannigan' + Puppet::Util::Windows::Security.expects(:string_to_sid_ptr).with(account).returns(false) + + provider.id2name(account).should == account end it "should return nil if the user doesn't exist" do - Puppet::Util::ADSI.stubs(:execquery).returns [] + Puppet::Util::Windows::Security.expects(:string_to_sid_ptr).with(sid).returns(true) + Puppet::Util::Windows::Security.expects(:sid_to_name).with(sid).returns(nil) - provider.id2name('S-1-1-50').should == nil + provider.id2name(sid).should == nil end end describe "#name2id" do - it "should return the sid of the user" do - Puppet::Util::ADSI.stubs(:execquery).returns [stub('account', :Sid => 'S-1-1-50')] - - provider.name2id('anybody').should == 'S-1-1-50' - end - - it "should return the argument if it's already a sid" do - provider.name2id('S-1-1-50').should == 'S-1-1-50' - end - - it "should return nil if the user doesn't exist" do - Puppet::Util::ADSI.stubs(:execquery).returns [] + it "should delegate to name_to_sid" do + Puppet::Util::Windows::Security.expects(:name_to_sid).with(account).returns(sid) - provider.name2id('someone').should == nil + provider.name2id(account).should == sid end end describe "#owner" do it "should return the sid of the owner if the file does exist" do FileUtils.touch(resource[:path]) - provider.stubs(:get_owner).with(resource[:path]).returns('S-1-1-50') + provider.stubs(:get_owner).with(resource[:path]).returns(sid) - provider.owner.should == 'S-1-1-50' + provider.owner.should == sid end it "should return absent if the file doesn't exist" do provider.owner.should == :absent end end describe "#owner=" do it "should set the owner to the specified value" do - provider.expects(:set_owner).with('S-1-1-50', resource[:path]) - provider.owner = 'S-1-1-50' + provider.expects(:set_owner).with(sid, resource[:path]) + provider.owner = sid end it "should propagate any errors encountered when setting the owner" do provider.stubs(:set_owner).raises(ArgumentError) - expect { provider.owner = 'S-1-1-50' }.to raise_error(Puppet::Error, /Failed to set owner/) + expect { + provider.owner = sid + }.to raise_error(Puppet::Error, /Failed to set owner/) end end describe "#group" do it "should return the sid of the group if the file does exist" do FileUtils.touch(resource[:path]) - provider.stubs(:get_group).with(resource[:path]).returns('S-1-1-50') + provider.stubs(:get_group).with(resource[:path]).returns(sid) - provider.group.should == 'S-1-1-50' + provider.group.should == sid end it "should return absent if the file doesn't exist" do provider.group.should == :absent end end describe "#group=" do it "should set the group to the specified value" do - provider.expects(:set_group).with('S-1-1-50', resource[:path]) - provider.group = 'S-1-1-50' + provider.expects(:set_group).with(sid, resource[:path]) + provider.group = sid end it "should propagate any errors encountered when setting the group" do provider.stubs(:set_group).raises(ArgumentError) - expect { provider.group = 'S-1-1-50' }.to raise_error(Puppet::Error, /Failed to set group/) + expect { + provider.group = sid + }.to raise_error(Puppet::Error, /Failed to set group/) end end describe "when validating" do {:owner => 'foo', :group => 'foo', :mode => 0777}.each do |k,v| it "should fail if the filesystem doesn't support ACLs and we're managing #{k}" do described_class.any_instance.stubs(:supports_acl?).returns false expect { Puppet::Type.type(:file).new :path => path, k => v }.to raise_error(Puppet::Error, /Can only manage owner, group, and mode on filesystems that support Windows ACLs, such as NTFS/) end end it "should not fail if the filesystem doesn't support ACLs and we're not managing permissions" do described_class.any_instance.stubs(:supports_acl?).returns false Puppet::Type.type(:file).new :path => path end end end