diff options
author | Brice Figureau <brice-puppet@daysofwonder.com> | 2009-03-31 20:29:37 +0200 |
---|---|---|
committer | Brice Figureau <brice-puppet@daysofwonder.com> | 2009-04-23 20:52:02 +0200 |
commit | 85233768f080b4cbc4e20eb0c354b6d859a2fb23 (patch) | |
tree | 19d32e670fe84cfb53f31adfd63953dd3a04fd5c | |
parent | 22b82abcd27834e43426f2758fba5728c146be61 (diff) | |
download | puppet-85233768f080b4cbc4e20eb0c354b6d859a2fb23.tar.gz puppet-85233768f080b4cbc4e20eb0c354b6d859a2fb23.tar.xz puppet-85233768f080b4cbc4e20eb0c354b6d859a2fb23.zip |
Enhance authconfig format to support uri paths and regex
This patch introduces a new set of directive to the authconfig
parser/file format:
path /uripath or patch ~ <regex>
This directive declares a new kind of ACL based on the uri path.
method save, find
This directive which is to be used under path directive restricts a
path ACL to only some REST verbs.
The ACL path system matches on path prefix possible, or
on regex matches (first match wins).
If no path are matching, then the authorization is not allowed.
The same if no ACL matches for the given REST verb.
The old namespace right matching still works as usual.
Signed-off-by: Brice Figureau <brice-puppet@daysofwonder.com>
-rw-r--r-- | lib/puppet/network/authconfig.rb | 82 | ||||
-rwxr-xr-x | lib/puppet/network/rights.rb | 202 | ||||
-rwxr-xr-x | spec/unit/network/authconfig.rb | 82 | ||||
-rwxr-xr-x | spec/unit/network/rights.rb | 392 |
4 files changed, 673 insertions, 85 deletions
diff --git a/lib/puppet/network/authconfig.rb b/lib/puppet/network/authconfig.rb index dc67723c4..f78cdc621 100644 --- a/lib/puppet/network/authconfig.rb +++ b/lib/puppet/network/authconfig.rb @@ -44,7 +44,7 @@ module Puppet end def initialize(file = nil, parsenow = true) - @file ||= Puppet[:authconfig] + @file = file || Puppet[:authconfig] unless @file raise Puppet::DevError, "No authconfig file defined" @@ -99,44 +99,21 @@ module Puppet count = 1 f.each { |line| case line - when /^\s*#/; next # skip comments - when /^\s*$/; next # skip blank lines - when /\[([\w.]+)\]/ # "namespace" or "namespace.method" - name = $1 - if newrights.include?(name) - raise FileServerError, "%s is already set at %s" % - [newrights[name], name] - end - newrights.newright(name) - right = newrights[name] - when /^\s*(\w+)\s+(.+)$/ - var = $1 - value = $2 - case var - when "allow" - value.split(/\s*,\s*/).each { |val| - begin - right.info "allowing %s access" % val - right.allow(val) - rescue AuthStoreError => detail - raise ConfigurationError, "%s at line %s of %s" % - [detail.to_s, count, @config] - end - } - when "deny" - value.split(/\s*,\s*/).each { |val| - begin - right.info "denying %s access" % val - right.deny(val) - rescue AuthStoreError => detail - raise ConfigurationError, "%s at line %s of %s" % - [detail.to_s, count, @config] - end - } - else - raise ConfigurationError, - "Invalid argument '%s' at line %s" % [var, count] + when /^\s*#/ # skip comments + count += 1 + next + when /^\s*$/ # skip blank lines + count += 1 + next + when /^(?:(\[[\w.]+\])|(path)\s+((?:~\s+)?[^ ]+))\s*$/ # "namespace" or "namespace.method" or "path /path" or "path ~ regex" + name = $1 + if $2 == "path" + name = $3 end + name.chomp! + right = newrights.newright(name, count) + when /^\s*(allow|deny|method)\s+(.+)$/ + parse_right_directive(right, $1, $2, count) else raise ConfigurationError, "Invalid line %s: %s" % [count, line] end @@ -162,6 +139,35 @@ module Puppet } @rights = newrights end + + def parse_right_directive(right, var, value, count) + case var + when "allow" + modify_right(right, :allow, value, "allowing %s access", count) + when "deny" + modify_right(right, :deny, value, "denying %s access", count) + when "method" + unless right.acl_type == :regex + raise ConfigurationError, "'method' directive not allowed in namespace ACL at line %s of %s" % [count, @config] + end + modify_right(right, :restrict_method, value, "allowing method %s access", count) + else + raise ConfigurationError, + "Invalid argument '%s' at line %s" % [var, count] + end + end + + def modify_right(right, method, value, msg, count) + value.split(/\s*,\s*/).each do |val| + begin + right.info msg % val + right.send(method, val) + rescue AuthStoreError => detail + raise ConfigurationError, "%s at line %s of %s" % [detail.to_s, count, @file] + end + end + end + end end diff --git a/lib/puppet/network/rights.rb b/lib/puppet/network/rights.rb index a4133f22c..6b2082cdb 100755 --- a/lib/puppet/network/rights.rb +++ b/lib/puppet/network/rights.rb @@ -1,15 +1,16 @@ -require 'ipaddr' require 'puppet/network/authstore' # Define a set of rights and who has access to them. -class Puppet::Network::Rights < Hash +# There are two types of rights: +# * named rights (ie a common string) +# * path based rights (which are matched on a longest prefix basis) +class Puppet::Network::Rights + # We basically just proxy directly to our rights. Each Right stores # its own auth abilities. - [:allow, :allowed?, :deny].each do |method| + [:allow, :deny].each do |method| define_method(method) do |name, *args| - name = name.intern if name.is_a? String - - if obj = right(name) + if obj = self[name] obj.send(method, *args) else raise ArgumentError, "Unknown right '%s'" % name @@ -17,45 +18,115 @@ class Puppet::Network::Rights < Hash end end + # this method is used to add a new allowed +method+ to +name+ + # method applies only to path rights + def restrict_method(name, *args) + if right = self[name] + right.restrict_method(*args) + else + raise ArgumentError, "'%s' right is not allowing method specification" % name + end + end + + def allowed?(name, *args) + res = :nomatch + right = @rights.find do |acl| + # an acl can return :dunno, which means "I'm not qualified to answer your question, + # please ask someone else". This is used when for instance an acl matches, but not for the + # current rest method, where we might think some other acl might be more specific. + if match = acl.match?(name) + args << match + if (res = acl.allowed?(*args)) != :dunno + return res + end + end + false + end + + # if allowed or denied, tell it to the world + return res unless res == :nomatch + + # there were no rights allowing/denying name + # if name is not a path, let's throw + raise ArgumentError, "Unknown namespace right '%s'" % name unless name =~ /^\// + + # but if this was a path, we implement a deny all policy by default + # on unknown rights. + return false + end + + def initialize() + @rights = [] + end + def [](name) - name = name.intern if name.is_a? String - super(name) + @rights.find { |acl| acl == name } + end + + def include?(name) + @rights.include?(name) + end + + def each + @rights.each { |r| yield r.name,r } end # Define a new right to which access can be provided. - def newright(name) - name = name.intern if name.is_a? String - shortname = Right.shortname(name) - if self.include? name - raise ArgumentError, "Right '%s' is already defined" % name - else - self[name] = Right.new(name, shortname) - end + def newright(name, line=nil) + add_right( Right.new(name, line) ) end private + def add_right(right) + if right.acl_type == :name and include?(right.key) + raise ArgumentError, "Right '%s' already exists" + end + @rights << right + sort_rights + right + end + + def sort_rights + @rights.sort! + end + # Retrieve a right by name. def right(name) - name = name.intern if name.is_a? String self[name] end # A right. class Right < Puppet::Network::AuthStore - attr_accessor :name, :shortname + attr_accessor :name, :key, :acl_type, :line + attr_accessor :methods, :length - Puppet::Util.logmethods(self, true) + ALL = [:save, :destroy, :find, :search] - def self.shortname(name) - name.to_s[0..0] - end + Puppet::Util.logmethods(self, true) - def initialize(name, shortname = nil) + def initialize(name, line) + @methods = [] @name = name - @shortname = shortname - unless @shortname - @shortname = Right.shortname(name) + @line = line || 0 + case name + when Symbol + @acl_type = :name + @key = name + when /^\[(.+)\]$/ + @acl_type = :name + @key = $1.intern if name.is_a?(String) + when /^\// + @acl_type = :regex + @key = Regexp.new("^" + Regexp.escape(name)) + @methods = ALL + when /^~/ # this is a regex + @acl_type = :regex + @name = name.gsub(/^~\s+/,'') + @key = Regexp.new(@name) + @methods = ALL + else + raise ArgumentError, "Unknown right type '%s'" % name end super() end @@ -68,6 +139,85 @@ class Puppet::Network::Rights < Hash def valid? true end + + def regex? + acl_type == :regex + end + + # does this right is allowed for this triplet? + # if this right is too restrictive (ie we don't match this access method) + # then return :dunno so that upper layers have a chance to try another right + # tailored to the given method + def allowed?(name, ip, method = nil, match = nil) + return :dunno if acl_type == :regex and not @methods.include?(method) + + if acl_type == :regex and match # make sure any capture are replaced + interpolate(match) + end + + res = super(name,ip) + + if acl_type == :regex + reset_interpolation + end + res + end + + # restrict this right to some method only + def restrict_method(m) + m = m.intern if m.is_a?(String) + + unless ALL.include?(m) + raise ArgumentError, "'%s' is not an allowed value for method directive" % m + end + + # if we were allowing all methods, then starts from scratch + if @methods === ALL + @methods = [] + end + + if @methods.include?(m) + raise ArgumentError, "'%s' is already in the '%s' ACL" % [m, name] + end + + @methods << m + end + + def match?(key) + # if we are a namespace compare directly + return self.key == namespace_to_key(key) if acl_type == :name + + # otherwise match with the regex + return self.key.match(key) + end + + def namespace_to_key(key) + key = key.intern if key.is_a?(String) + key + end + + # this is where all the magic happens. + # we're sorting the rights array with this scheme: + # * namespace rights are all in front + # * regex path rights are then all queued in file order + def <=>(rhs) + # move namespace rights at front + if self.acl_type != rhs.acl_type + return self.acl_type == :name ? -1 : 1 + end + + # sort by creation order (ie first match appearing in the file will win) + # that is don't sort, in which case the sort algorithm will order in the + # natural array order (ie the creation order) + return 0 + end + + def ==(name) + return self.key == namespace_to_key(name) if acl_type == :name + return self.name == name + end + end + end diff --git a/spec/unit/network/authconfig.rb b/spec/unit/network/authconfig.rb index 9d5f6154d..d891fe45a 100755 --- a/spec/unit/network/authconfig.rb +++ b/spec/unit/network/authconfig.rb @@ -28,7 +28,7 @@ describe Puppet::Network::AuthConfig do Puppet::Network::AuthConfig.new end - it "should raise an error if no file is defined in fine" do + it "should raise an error if no file is defined finally" do Puppet.stubs(:[]).with(:authconfig).returns(nil) lambda { Puppet::Network::AuthConfig.new }.should raise_error(Puppet::DevError) @@ -111,6 +111,14 @@ describe Puppet::Network::AuthConfig do @authconfig.read end + it "should increment line number even on commented lines" do + @fd.stubs(:each).multiple_yields(' # comment','[puppetca]') + + @rights.expects(:newright).with('[puppetca]', 2) + + @authconfig.read + end + it "should skip blank lines" do @fd.stubs(:each).yields(' ') @@ -119,7 +127,15 @@ describe Puppet::Network::AuthConfig do @authconfig.read end - it "should throw an error if read rights already exist" do + it "should increment line number even on blank lines" do + @fd.stubs(:each).multiple_yields(' ','[puppetca]') + + @rights.expects(:newright).with('[puppetca]', 2) + + @authconfig.read + end + + it "should throw an error if the current namespace right already exist" do @fd.stubs(:each).yields('[puppetca]') @rights.stubs(:include?).with("puppetca").returns(true) @@ -127,10 +143,19 @@ describe Puppet::Network::AuthConfig do lambda { @authconfig.read }.should raise_error end + it "should not throw an error if the current path right already exist" do + @fd.stubs(:each).yields('path /hello') + + @rights.stubs(:newright).with("/hello",1) + @rights.stubs(:include?).with("/hello").returns(true) + + lambda { @authconfig.read }.should_not raise_error + end + it "should create a new right for found namespaces" do @fd.stubs(:each).yields('[puppetca]') - @rights.expects(:newright).with("puppetca") + @rights.expects(:newright).with("[puppetca]", 1) @authconfig.read end @@ -138,8 +163,24 @@ describe Puppet::Network::AuthConfig do it "should create a new right for each found namespace line" do @fd.stubs(:each).multiple_yields('[puppetca]', '[fileserver]') - @rights.expects(:newright).with("puppetca") - @rights.expects(:newright).with("fileserver") + @rights.expects(:newright).with("[puppetca]", 1) + @rights.expects(:newright).with("[fileserver]", 2) + + @authconfig.read + end + + it "should create a new right for each found path line" do + @fd.stubs(:each).multiple_yields('path /certificates') + + @rights.expects(:newright).with("/certificates", 1) + + @authconfig.read + end + + it "should create a new right for each found regex line" do + @fd.stubs(:each).multiple_yields('path ~ .rb$') + + @rights.expects(:newright).with("~ .rb$", 1) @authconfig.read end @@ -148,26 +189,47 @@ describe Puppet::Network::AuthConfig do acl = stub 'acl', :info @fd.stubs(:each).multiple_yields('[puppetca]', 'allow 127.0.0.1') - @rights.stubs(:newright).with("puppetca") - @rights.stubs(:[]).returns(acl) + @rights.stubs(:newright).with("[puppetca]", 1).returns(acl) acl.expects(:allow).with('127.0.0.1') @authconfig.read end - it "should create a deny ACE on each subsequent allow" do + it "should create a deny ACE on each subsequent deny" do acl = stub 'acl', :info @fd.stubs(:each).multiple_yields('[puppetca]', 'deny 127.0.0.1') - @rights.stubs(:newright).with("puppetca") - @rights.stubs(:[]).returns(acl) + @rights.stubs(:newright).with("[puppetca]", 1).returns(acl) acl.expects(:deny).with('127.0.0.1') @authconfig.read end + it "should inform the current ACL if we get the 'method' directive" do + acl = stub 'acl', :info + acl.stubs(:acl_type).returns(:regex) + + @fd.stubs(:each).multiple_yields('path /certificates', 'method search,find') + @rights.stubs(:newright).with("/certificates", 1).returns(acl) + + acl.expects(:restrict_method).with('search') + acl.expects(:restrict_method).with('find') + + @authconfig.read + end + + it "should raise an error if the 'method' directive is used in a right different than a path/regex one" do + acl = stub 'acl', :info + acl.stubs(:acl_type).returns(:regex) + + @fd.stubs(:each).multiple_yields('[puppetca]', 'method search,find') + @rights.stubs(:newright).with("puppetca", 1).returns(acl) + + lambda { @authconfig.read }.should raise_error + end + end end diff --git a/spec/unit/network/rights.rb b/spec/unit/network/rights.rb index 5fe8e51f4..6e918124f 100755 --- a/spec/unit/network/rights.rb +++ b/spec/unit/network/rights.rb @@ -9,7 +9,7 @@ describe Puppet::Network::Rights do @right = Puppet::Network::Rights.new end - [:allow, :allowed?, :deny].each do |m| + [:allow, :deny].each do |m| it "should have a #{m} method" do @right.should respond_to(m) end @@ -17,7 +17,7 @@ describe Puppet::Network::Rights do describe "when using #{m}" do it "should delegate to the correct acl" do acl = stub 'acl' - @right.stubs(:right).returns(acl) + @right.stubs(:[]).returns(acl) acl.expects(m).with("me") @@ -26,29 +26,399 @@ describe Puppet::Network::Rights do end end - describe "when creating new ACLs" do + it "should throw an error if type can't be determined" do + lambda { @right.newright("name") }.should raise_error + end + + describe "when creating new namespace ACLs" do + it "should throw an error if the ACL already exists" do - @right.newright("name") + @right.newright("[name]") - lambda { @right.newright("name")}.should raise_error + lambda { @right.newright("[name]") }.should raise_error end it "should create a new ACL with the correct name" do - @right.newright("name") + @right.newright("[name]") - @right["name"].name.should == :name + @right["name"].key.should == :name end it "should create an ACL of type Puppet::Network::AuthStore" do - @right.newright("name") + @right.newright("[name]") @right["name"].should be_a_kind_of(Puppet::Network::AuthStore) end + end + + describe "when creating new path ACLs" do + it "should not throw an error if the ACL already exists" do + @right.newright("/name") + + lambda { @right.newright("/name")}.should_not raise_error + end + + it "should throw an error if the acl uri path is not absolute" do + lambda { @right.newright("name")}.should raise_error + end + + it "should create a new ACL with the correct path" do + @right.newright("/name") + + @right["/name"].should_not be_nil + end + + it "should create an ACL of type Puppet::Network::AuthStore" do + @right.newright("/name") + + @right["/name"].should be_a_kind_of(Puppet::Network::AuthStore) + end + end + + describe "when creating new regex ACLs" do + it "should not throw an error if the ACL already exists" do + @right.newright("~ .rb$") + + lambda { @right.newright("~ .rb$")}.should_not raise_error + end + + it "should create a new ACL with the correct regex" do + @right.newright("~ .rb$") + + @right.include?(".rb$").should_not be_nil + end + + it "should be able to lookup the regex" do + @right.newright("~ .rb$") + + @right[".rb$"].should_not be_nil + end + + it "should create an ACL of type Puppet::Network::AuthStore" do + @right.newright("~ .rb$").should be_a_kind_of(Puppet::Network::AuthStore) + end + end + + describe "when checking ACLs existence" do + it "should return false if there are no matching rights" do + @right.include?("name").should be_false + end + + it "should return true if a namespace rights exist" do + @right.newright("[name]") + + @right.include?("name").should be_true + end + + it "should return false if no matching namespace rights exist" do + @right.newright("[name]") + + @right.include?("notname").should be_false + end + + it "should return true if a path right exists" do + @right.newright("/name") + + @right.include?("/name").should be_true + end + + it "should return false if no matching path rights exist" do + @right.newright("/name") + + @right.include?("/differentname").should be_false + end + + it "should return true if a regex right exists" do + @right.newright("~ .rb$") + + @right.include?(".rb$").should be_true + end + + it "should return false if no matching path rights exist" do + @right.newright("~ .rb$") + + @right.include?(".pp$").should be_false + end + end + + describe "when checking if right is allowed" do + before :each do + @right.stubs(:right).returns(nil) + + @pathacl = stub 'pathacl', :acl_type => :path + Puppet::Network::Rights::Right.stubs(:new).returns(@pathacl) + end + + it "should first check namespace rights" do + acl = stub 'acl', :acl_type => :name, :key => :namespace + Puppet::Network::Rights::Right.stubs(:new).returns(acl) + + @right.newright("[namespace]") + acl.expects(:match?).returns(true) + acl.expects(:allowed?).with(:args, true).returns(true) + + @right.allowed?("namespace", :args) + end + + it "should then check for path rights if no namespace match" do + acl = stub 'acl', :acl_type => :name, :match? => false + + acl.expects(:allowed?).with(:args).never + @right.newright("/path/to/there") + + @pathacl.stubs(:match?).returns(true) + @pathacl.expects(:allowed?) + + @right.allowed?("/path/to/there", :args) + end + + it "should pass the match? return to allowed?" do + @right.newright("/path/to/there") + + @pathacl.expects(:match?).returns(:match) + @pathacl.expects(:allowed?).with(:args, :match) + + @right.allowed?("/path/to/there", :args) + end + + describe "with namespace acls" do + it "should raise an error if this namespace right doesn't exist" do + lambda{ @right.allowed?("namespace") }.should raise_error + end + end + + describe "with path acls" do + before :each do + @long_acl = stub 'longpathacl', :name => "/path/to/there", :acl_type => :regex + Puppet::Network::Rights::Right.stubs(:new).with("/path/to/there", 0).returns(@long_acl) + + @short_acl = stub 'shortpathacl', :name => "/path/to", :acl_type => :regex + Puppet::Network::Rights::Right.stubs(:new).with("/path/to", 0).returns(@short_acl) + + @long_acl.stubs(:"<=>").with(@short_acl).returns(0) + @short_acl.stubs(:"<=>").with(@long_acl).returns(0) + end + + it "should select the first match" do + @right.newright("/path/to/there", 0) + @right.newright("/path/to", 0) + + @long_acl.stubs(:match?).returns(true) + @short_acl.stubs(:match?).returns(true) + + @long_acl.expects(:allowed?).returns(true) + @short_acl.expects(:allowed?).never + + @right.allowed?("/path/to/there/and/there", :args) + end + + it "should select the first match that doesn't return :dunno" do + @right.newright("/path/to/there", 0) + @right.newright("/path/to", 0) + + @long_acl.stubs(:match?).returns(true) + @short_acl.stubs(:match?).returns(true) + + @long_acl.expects(:allowed?).returns(:dunno) + @short_acl.expects(:allowed?) + + @right.allowed?("/path/to/there/and/there", :args) + end + + it "should not select an ACL that doesn't match" do + @right.newright("/path/to/there", 0) + @right.newright("/path/to", 0) + + @long_acl.stubs(:match?).returns(false) + @short_acl.stubs(:match?).returns(true) + + @long_acl.expects(:allowed?).never + @short_acl.expects(:allowed?) + + @right.allowed?("/path/to/there/and/there", :args) + end + + it "should return the result of the acl" do + @right.newright("/path/to/there", 0) + + @long_acl.stubs(:match?).returns(true) + @long_acl.stubs(:allowed?).returns(:returned) + + @right.allowed?("/path/to/there/and/there", :args).should == :returned + end + + it "should not raise an error if this path acl doesn't exist" do + lambda{ @right.allowed?("/path", :args) }.should_not raise_error + end + + it "should return false if no path match" do + @right.allowed?("/path", :args).should be_false + end + end + + describe "with regex acls" do + before :each do + @regex_acl1 = stub 'regex_acl1', :name => "/files/(.*)/myfile", :acl_type => :regex + Puppet::Network::Rights::Right.stubs(:new).with("~ /files/(.*)/myfile", 0).returns(@regex_acl1) - it "should create an ACL with a shortname" do - @right.newright("name") + @regex_acl2 = stub 'regex_acl2', :name => "/files/(.*)/myfile/", :acl_type => :regex + Puppet::Network::Rights::Right.stubs(:new).with("~ /files/(.*)/myfile/", 0).returns(@regex_acl2) + + @regex_acl1.stubs(:"<=>").with(@regex_acl2).returns(0) + @regex_acl2.stubs(:"<=>").with(@regex_acl1).returns(0) + end + + it "should select the first match" do + @right.newright("~ /files/(.*)/myfile", 0) + @right.newright("~ /files/(.*)/myfile/", 0) + + @regex_acl1.stubs(:match?).returns(true) + @regex_acl2.stubs(:match?).returns(true) + + @regex_acl1.expects(:allowed?).returns(true) + @regex_acl2.expects(:allowed?).never + + @right.allowed?("/files/repository/myfile/other", :args) + end + + it "should select the first match that doesn't return :dunno" do + @right.newright("~ /files/(.*)/myfile", 0) + @right.newright("~ /files/(.*)/myfile/", 0) + + @regex_acl1.stubs(:match?).returns(true) + @regex_acl2.stubs(:match?).returns(true) + + @regex_acl1.expects(:allowed?).returns(:dunno) + @regex_acl2.expects(:allowed?) + + @right.allowed?("/files/repository/myfile/other", :args) + end + + it "should not select an ACL that doesn't match" do + @right.newright("~ /files/(.*)/myfile", 0) + @right.newright("~ /files/(.*)/myfile/", 0) + + @regex_acl1.stubs(:match?).returns(false) + @regex_acl2.stubs(:match?).returns(true) + + @regex_acl1.expects(:allowed?).never + @regex_acl2.expects(:allowed?) + + @right.allowed?("/files/repository/myfile/other", :args) + end + + it "should return the result of the acl" do + @right.newright("~ /files/(.*)/myfile", 0) + + @regex_acl1.stubs(:match?).returns(true) + @regex_acl1.stubs(:allowed?).returns(:returned) + + @right.allowed?("/files/repository/myfile/other", :args).should == :returned + end + + it "should not raise an error if no regex acl match" do + lambda{ @right.allowed?("/path", :args) }.should_not raise_error + end + + it "should return false if no regex match" do + @right.allowed?("/path", :args).should be_false + end - @right["name"].shortname.should == "n" end end + + describe Puppet::Network::Rights::Right do + before :each do + @acl = Puppet::Network::Rights::Right.new("/path",0) + end + + describe "with path" do + it "should say it's a regex ACL" do + @acl.acl_type.should == :regex + end + + it "should match up to its path length" do + @acl.match?("/path/that/works").should_not be_nil + end + + it "should match up to its path length" do + @acl.match?("/paththatalsoworks").should_not be_nil + end + + it "should return nil if no match" do + @acl.match?("/notpath").should be_nil + end + end + + describe "with regex" do + before :each do + @acl = Puppet::Network::Rights::Right.new("~ .rb$",0) + end + + it "should say it's a regex ACL" do + @acl.acl_type.should == :regex + end + + it "should match as a regex" do + @acl.match?("this shoud work.rb").should_not be_nil + end + + it "should return nil if no match" do + @acl.match?("do not match").should be_nil + end + end + + it "should allow all rest methods by default" do + @acl.methods.should == Puppet::Network::Rights::Right::ALL + end + + it "should allow modification of the methods filters" do + @acl.restrict_method(:save) + + @acl.methods.should == [:save] + end + + it "should stack methods filters" do + @acl.restrict_method(:save) + @acl.restrict_method(:destroy) + + @acl.methods.should == [:save, :destroy] + end + + it "should raise an error if the method is already filtered" do + @acl.restrict_method(:save) + + lambda { @acl.restrict_method(:save) }.should raise_error + end + + describe "when checking right authorization" do + it "should return :dunno if this right doesn't apply" do + @acl.restrict_method(:destroy) + + @acl.allowed?("me","127.0.0.1", :save).should == :dunno + end + + it "should interpolate allow/deny patterns with the given match" do + @acl.expects(:interpolate).with(:match) + + @acl.allowed?("me","127.0.0.1", :save, :match) + end + + it "should reset interpolation after the match" do + @acl.expects(:reset_interpolation) + + @acl.allowed?("me","127.0.0.1", :save, :match) + end + + # mocha doesn't allow testing super... + # it "should delegate to the AuthStore for the result" do + # @acl.method(:save) + # + # @acl.expects(:allowed?).with("me","127.0.0.1") + # + # @acl.allowed?("me","127.0.0.1", :save) + # end + end + end + end |