diff --git a/lib/puppet/provider/macauthorization/macauthorization.rb b/lib/puppet/provider/macauthorization/macauthorization.rb index 67e4e06bd..2cdef6c12 100644 --- a/lib/puppet/provider/macauthorization/macauthorization.rb +++ b/lib/puppet/provider/macauthorization/macauthorization.rb @@ -1,308 +1,313 @@ require 'facter' require 'facter/util/plist' require 'puppet' require 'tempfile' Puppet::Type.type(:macauthorization).provide :macauthorization, :parent => Puppet::Provider do desc "Manage Mac OS X authorization database rules and rights." commands :security => "/usr/bin/security" commands :sw_vers => "/usr/bin/sw_vers" confine :operatingsystem => :darwin - product_version = sw_vers "-productVersion" - - confine :true => if /^10.5/.match(product_version) or /^10.6/.match(product_version) - true + # This should be confined based on macosx_productversion once + # http://projects.reductivelabs.com/issues/show/1796 + # is resolved. + if FileTest.exists?("/usr/bin/sw_vers") + product_version = sw_vers "-productVersion" + + confine :true => if /^10.5/.match(product_version) or /^10.6/.match(product_version) + true + end end defaultfor :operatingsystem => :darwin AuthDB = "/etc/authorization" @rights = {} @rules = {} @parsed_auth_db = {} @comment = "" # Not implemented yet. Is there any real need to? # This map exists due to the use of hyphens and reserved words in # the authorization schema. PuppetToNativeAttributeMap = { :allow_root => "allow-root", :authenticate_user => "authenticate-user", :auth_class => "class", :k_of_n => "k-of-n", :session_owner => "session-owner", } class << self attr_accessor :parsed_auth_db attr_accessor :rights attr_accessor :rules attr_accessor :comments # Not implemented yet. def prefetch(resources) self.populate_rules_rights end def instances if self.parsed_auth_db == {} self.prefetch(nil) end self.parsed_auth_db.collect do |k,v| new(:name => k) end end def populate_rules_rights auth_plist = Plist::parse_xml(AuthDB) if not auth_plist raise Puppet::Error.new("Cannot parse: #{AuthDB}") end self.rights = auth_plist["rights"].dup self.rules = auth_plist["rules"].dup self.parsed_auth_db = self.rights.dup self.parsed_auth_db.merge!(self.rules.dup) end end # standard required provider instance methods def initialize(resource) if self.class.parsed_auth_db == {} self.class.prefetch(resource) end super end def create # we just fill the @property_hash in here and let the flush method # deal with it rather than repeating code. new_values = {} validprops = Puppet::Type.type(resource.class.name).validproperties validprops.each do |prop| next if prop == :ensure if value = resource.should(prop) and value != "" new_values[prop] = value end end @property_hash = new_values.dup end def destroy # We explicitly delete here rather than in the flush method. case resource[:auth_type] when :right destroy_right when :rule destroy_rule else raise Puppet::Error.new("Must specify auth_type when destroying.") end end def exists? if self.class.parsed_auth_db.has_key?(resource[:name]) return true else return false end end def flush # deletion happens in the destroy methods if resource[:ensure] != :absent case resource[:auth_type] when :right flush_right when :rule flush_rule else raise Puppet::Error.new("flush requested for unknown type.") end @property_hash.clear end end # utility methods below def destroy_right security "authorizationdb", :remove, resource[:name] end def destroy_rule authdb = Plist::parse_xml(AuthDB) authdb_rules = authdb["rules"].dup if authdb_rules[resource[:name]] begin authdb["rules"].delete(resource[:name]) Plist::Emit.save_plist(authdb, AuthDB) rescue Errno::EACCES => e raise Puppet::Error.new("Error saving #{AuthDB}: #{e}") end end end def flush_right # first we re-read the right just to make sure we're in sync for # values that weren't specified in the manifest. As we're supplying # the whole plist when specifying the right it seems safest to be # paranoid given the low cost of quering the db once more. cmds = [] cmds << :security << "authorizationdb" << "read" << resource[:name] output = execute(cmds, :combine => false) current_values = Plist::parse_xml(output) if current_values.nil? current_values = {} end specified_values = convert_plist_to_native_attributes(@property_hash) # take the current values, merge the specified values to obtain a # complete description of the new values. new_values = current_values.merge(specified_values) set_right(resource[:name], new_values) end def flush_rule authdb = Plist::parse_xml(AuthDB) authdb_rules = authdb["rules"].dup current_values = {} if authdb_rules[resource[:name]] current_values = authdb_rules[resource[:name]] end specified_values = convert_plist_to_native_attributes(@property_hash) new_values = current_values.merge(specified_values) set_rule(resource[:name], new_values) end def set_right(name, values) # Both creates and modifies rights as it simply overwrites them. # The security binary only allows for writes using stdin, so we # dump the values to a tempfile. values = convert_plist_to_native_attributes(values) tmp = Tempfile.new('puppet_macauthorization') begin Plist::Emit.save_plist(values, tmp.path) cmds = [] cmds << :security << "authorizationdb" << "write" << name output = execute(cmds, :combine => false, :stdinfile => tmp.path.to_s) rescue Errno::EACCES => e raise Puppet::Error.new("Cannot save right to #{tmp.path}: #{e}") ensure tmp.close tmp.unlink end end def set_rule(name, values) # Both creates and modifies rules as it overwrites the entry in the # rules dictionary. Unfortunately the security binary doesn't # support modifying rules at all so we have to twiddle the whole # plist... :( See Apple Bug #6386000 values = convert_plist_to_native_attributes(values) authdb = Plist::parse_xml(AuthDB) authdb["rules"][name] = values begin Plist::Emit.save_plist(authdb, AuthDB) rescue raise Puppet::Error.new("Error writing to: #{AuthDB}") end end def convert_plist_to_native_attributes(propertylist) # This mainly converts the keys from the puppet attributes to the # 'native' ones, but also enforces that the keys are all Strings # rather than Symbols so that any merges of the resultant Hash are # sane. newplist = {} propertylist.each_pair do |key, value| next if key == :ensure # not part of the auth db schema. next if key == :auth_type # not part of the auth db schema. new_key = key if PuppetToNativeAttributeMap.has_key?(key) new_key = PuppetToNativeAttributeMap[key].to_s elsif not key.is_a?(String) new_key = key.to_s end newplist[new_key] = value end newplist end def retrieve_value(resource_name, attribute) if not self.class.parsed_auth_db.has_key?(resource_name) raise Puppet::Error.new("Cannot find #{resource_name} in auth db") end if PuppetToNativeAttributeMap.has_key?(attribute) native_attribute = PuppetToNativeAttributeMap[attribute] else native_attribute = attribute.to_s end if self.class.parsed_auth_db[resource_name].has_key?(native_attribute) value = self.class.parsed_auth_db[resource_name][native_attribute] case value when true, "true", :true value = :true when false, "false", :false value = :false end @property_hash[attribute] = value return value else @property_hash.delete(attribute) return "" # so ralsh doesn't display it. end end # property methods below # # We define them all dynamically apart from auth_type which is a special # case due to not being in the actual authorization db schema. properties = [ :allow_root, :authenticate_user, :auth_class, :comment, :group, :k_of_n, :mechanisms, :rule, :session_owner, :shared, :timeout, :tries ] properties.each do |field| define_method(field.to_s) do retrieve_value(resource[:name], field) end define_method(field.to_s + "=") do |value| @property_hash[field] = value end end def auth_type if resource.should(:auth_type) != nil return resource.should(:auth_type) elsif self.exists? # this is here just for ralsh, so it can work out what type it is. if self.class.rights.has_key?(resource[:name]) return :right elsif self.class.rules.has_key?(resource[:name]) return :rule else raise Puppet::Error.new("#{resource[:name]} is unknown type.") end else raise Puppet::Error.new("auth_type required for new resources.") end end def auth_type=(value) @property_hash[:auth_type] = value end end \ No newline at end of file diff --git a/lib/rake b/lib/rake deleted file mode 120000 index 0ac8c7681..000000000 --- a/lib/rake +++ /dev/null @@ -1 +0,0 @@ -/Qualia/Repos/github/reductive-build/lib/rake \ No newline at end of file diff --git a/spec/unit/provider/macauthorization.rb b/spec/unit/provider/macauthorization.rb old mode 100644 new mode 100755 index 4754b11c7..8c9636f16 --- a/spec/unit/provider/macauthorization.rb +++ b/spec/unit/provider/macauthorization.rb @@ -1,153 +1,147 @@ #!/usr/bin/env ruby # # Unit testing for the macauthorization provider # require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet' require 'facter/util/plist' provider_class = Puppet::Type.type(:macauthorization).provider(:macauthorization) describe provider_class do before :each do # Create a mock resource @resource = stub 'resource' - @provider = provider_class.new(@resource) - @authname = "foo.spam.eggs.puppettest" @authplist = {} @rules = {@authname => @authplist} - @authdb = {} - @authdb["rules"] = @rules + + authdb = {} + authdb["rules"] = { "foorule" => "foo" } + authdb["rights"] = { "fooright" => "foo" } + + # Stub out Plist::parse_xml + Plist.stubs(:parse_xml).returns(authdb) # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name, ensure @resource.stubs(:[]).with(:name).returns @authname - @resource.stubs(:[]).with(:ensure).returns :present - @resource.stubs(:ref).returns "MacAuthorization[#{@authname}]" - # stub out the provider methods that actually touch the filesystem - # or execute commands - @provider.stubs(:populate_rules_rights).returns("") - - # Stub out Plist::parse_xml - Plist.stubs("parse_xml").returns(@authdb) + @provider = provider_class.new(@resource) end it "should have a create method" do @provider.should respond_to(:create) end it "should have a destroy method" do @provider.should respond_to(:destroy) end it "should have an exists? method" do @provider.should respond_to(:exists?) end it "should have a flush method" do @provider.should respond_to(:flush) end properties = [ :allow_root, :authenticate_user, :auth_class, :comment, - :group, :k_of_n, :mechanisms, :rule, :session_owner, - :shared, :timeout, :tries, :auth_type ] - + :group, :k_of_n, :mechanisms, :rule, :session_owner, + :shared, :timeout, :tries, :auth_type ] + properties.each do |prop| it "should have a #{prop.to_s} method" do @provider.should respond_to(prop.to_s) end it "should have a #{prop.to_s}= method" do @provider.should respond_to(prop.to_s + "=") end end describe "when destroying a right" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:right) end it "should call the internal method destroy_right" do - @provider.expects("destroy_right") + @provider.expects(:destroy_right) @provider.destroy end it "should call the external command 'security authorizationdb remove @authname" do @provider.expects(:security).with("authorizationdb", :remove, @authname) @provider.destroy end end describe "when destroying a rule" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:rule) end it "should call the internal method destroy_rule" do - @provider.expects("destroy_rule") + @provider.expects(:destroy_rule) @provider.destroy end end describe "when flushing a right" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:right) end it "should call the internal method flush_right" do - @provider.expects("flush_right") + @provider.expects(:flush_right) @provider.flush end it "should call the internal method set_right" do - @provider.expects("set_right") + @provider.expects(:set_right) @provider.flush end it "should read and write to the auth database with the right arguments" do @provider.expects(:execute).with() { |cmds, args| cmds.include?("read") and cmds.include?(@authname) and args[:combine] == false }.once @provider.expects(:execute).with() { |cmds, args| cmds.include?("write") and cmds.include?(@authname) and args[:combine] == false and args[:stdinfile] != nil }.once @provider.flush end end describe "when flushing a rule" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:rule) end it "should call the internal method flush_rule" do - @provider.expects("flush_rule") + @provider.expects(:flush_rule) @provider.flush end it "should call the internal method set_rule" do - @provider.expects("set_rule") + @provider.expects(:set_rule) @provider.flush end end - - end \ No newline at end of file diff --git a/spec/unit/type/macauthorization.rb b/spec/unit/type/macauthorization.rb old mode 100644 new mode 100755 index a27841c82..1c7f122b8 --- a/spec/unit/type/macauthorization.rb +++ b/spec/unit/type/macauthorization.rb @@ -1,80 +1,78 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' macauth_type = Puppet::Type.type(:macauthorization) - -describe macauth_type, "when validating attributes" do - - parameters = [:name,] - properties = [:auth_type, :allow_root, :authenticate_user, :auth_class, - :comment, :group, :k_of_n, :mechanisms, :rule, - :session_owner, :shared, :timeout, :tries] - - parameters.each do |parameter| - it "should have a %s parameter" % parameter do - macauth_type.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) - end - - it "should have documentation for its %s parameter" % parameter do - macauth_type.attrclass(parameter).doc.should be_instance_of(String) - end - end - - properties.each do |property| - it "should have a %s property" % property do - macauth_type.attrclass(property).ancestors.should be_include(Puppet::Property) - end - - it "should have documentation for its %s property" % property do - macauth_type.attrclass(property).doc.should be_instance_of(String) - end - end - -end - -describe macauth_type, "when validating properties" do +describe Puppet.type(:macauthorization), "when checking macauthorization objects" do before do - @provider = stub 'provider' - @resource = stub 'resource', :resource => nil, :provider => @provider, :line => nil, :file => nil + authplist = {} + authplist["rules"] = { "foorule" => "foo" } + authplist["rights"] = { "fooright" => "foo" } + provider_class = macauth_type.provider(macauth_type.providers[0]) + Plist.stubs(:parse_xml).with("/etc/authorization").returns(authplist) + macauth_type.stubs(:defaultprovider).returns provider_class end after do macauth_type.clear end + - it "should have a default provider inheriting from Puppet::Provider" do - macauth_type.defaultprovider.ancestors.should be_include(Puppet::Provider) - end - - it "should be able to create a instance" do - macauth_type.create(:name => "foo").should_not be_nil - end + describe "when validating attributes" do + + parameters = [:name,] + properties = [:auth_type, :allow_root, :authenticate_user, :auth_class, + :comment, :group, :k_of_n, :mechanisms, :rule, + :session_owner, :shared, :timeout, :tries] + + parameters.each do |parameter| + it "should have a %s parameter" % parameter do + macauth_type.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) + end + + it "should have documentation for its %s parameter" % parameter do + macauth_type.attrclass(parameter).doc.should be_instance_of(String) + end + end + + properties.each do |property| + it "should have a %s property" % property do + macauth_type.attrclass(property).ancestors.should be_include(Puppet::Property) + end + + it "should have documentation for its %s property" % property do + macauth_type.attrclass(property).doc.should be_instance_of(String) + end + end - it "should be able to create an instance" do - lambda { - macauth_type.create(:name => 'foo') - }.should_not raise_error - end - - it "should support :present as a value to :ensure" do - lambda { - macauth_type.create(:name => "foo", :ensure => :present) - }.should_not raise_error end - - it "should support :absent as a value to :ensure" do - lambda { - macauth_type.create(:name => "foo", :ensure => :absent) - }.should_not raise_error + + describe "when validating properties" do + + it "should have a default provider inheriting from Puppet::Provider" do + macauth_type.defaultprovider.ancestors.should be_include(Puppet::Provider) + end + + it "should be able to create an instance" do + lambda { + macauth_type.create(:name => 'foo') + }.should_not raise_error + end + + it "should support :present as a value to :ensure" do + lambda { + macauth_type.create(:name => "foo", :ensure => :present) + }.should_not raise_error + end + + it "should support :absent as a value to :ensure" do + lambda { + macauth_type.create(:name => "foo", :ensure => :absent) + }.should_not raise_error + end + end -end - -describe "instances" do - it "should have a valid provider" do - macauth_type.create(:name => "foo").provider.class.ancestors.should be_include(Puppet::Provider) - end end \ No newline at end of file