summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrice Figureau <brice-puppet@daysofwonder.com>2009-04-11 18:58:56 +0200
committerBrice Figureau <brice-puppet@daysofwonder.com>2009-04-23 20:52:02 +0200
commit22b82abcd27834e43426f2758fba5728c146be61 (patch)
tree0ff8d542a0a1baf4bcfbecbc92a43455680d3671
parent15abe1709aa52bb45fe228139f4c0352dc8905df (diff)
downloadpuppet-22b82abcd27834e43426f2758fba5728c146be61.tar.gz
puppet-22b82abcd27834e43426f2758fba5728c146be61.tar.xz
puppet-22b82abcd27834e43426f2758fba5728c146be61.zip
Add dynamic authorization to authstore
The idea is to have allow/deny authorization directives that are dynamic: their evaluation is deferred until we perform the authorization checking in allowed?. This is done to allow replacing backreferences in allow/deny directives by parameters of the match that selected this right. For instance, it is possible to: allow $1.$2 And using Right::interpolate() with the result of a regex match using 2 captures, will evaluate $1.$2 to those captures. For instance, if we captured [host, reductivelabs.com], then the allow directive is replaced by: allow host.reductivelabs.com It is then safe to call allowed?, after which we can reset the interpolation. This interpolation is thread-safe. Signed-off-by: Brice Figureau <brice-puppet@daysofwonder.com> authconfig regex support
-rwxr-xr-xlib/puppet/network/authstore.rb41
-rwxr-xr-xtest/network/authstore.rb49
2 files changed, 88 insertions, 2 deletions
diff --git a/lib/puppet/network/authstore.rb b/lib/puppet/network/authstore.rb
index 7341f8a1e..6f7a7df25 100755
--- a/lib/puppet/network/authstore.rb
+++ b/lib/puppet/network/authstore.rb
@@ -45,7 +45,7 @@ module Puppet
return true
end
- if decl = @declarations.find { |d| d.match?(name, ip) }
+ if decl = declarations.find { |d| d.match?(name, ip) }
return decl.result
end
@@ -72,8 +72,29 @@ module Puppet
"authstore"
end
+ def interpolate(match)
+ declarations = @declarations.collect do |ace|
+ ace.interpolate(match)
+ end
+ declarations.sort!
+ Thread.current[:declarations] = declarations
+ end
+
+ def reset_interpolation
+ Thread.current[:declarations] = nil
+ end
+
private
+ # returns our ACEs list, but if we have a modification of it
+ # in our current thread, let's return it
+ # this is used if we want to override the this purely immutable list
+ # by a modified version in a multithread safe way.
+ def declarations
+ return Thread.current[:declarations] if Thread.current[:declarations]
+ @declarations
+ end
+
# Store the results of a pattern into our hash. Basically just
# converts the pattern and sticks it into the hash.
def store(type, pattern)
@@ -193,6 +214,21 @@ module Puppet
@type = type
end
+ # interpolate a pattern to replace any
+ # backreferences by the given match
+ # for instance if our pattern is $1.reductivelabs.com
+ # and we're called with a MatchData whose capture 1 is puppet
+ # we'll return a pattern of puppet.reductivelabs.com
+ def interpolate(match)
+ return self if @name == :ip
+
+ clone = dup
+ clone.pattern = clone.pattern.reverse.collect do |p|
+ p.gsub(/\$(\d)/) { |m| match[$1.to_i] }
+ end.join(".")
+ clone
+ end
+
private
# Returns nil if both values are true or both are false, returns
@@ -277,6 +313,9 @@ module Puppet
@pattern = munge_name(value)
@pattern.pop # take off the '*'
@length = @pattern.length
+ when /\$\d+/ # a backreference pattern ala $1.reductivelabs.com or 192.168.0.$1 or $1.$2
+ @name = :dynamic
+ @pattern = munge_name(value)
else
# Else, use the IPAddr class to determine if we've got a
# valid IP address.
diff --git a/test/network/authstore.rb b/test/network/authstore.rb
index ad4a4f1c2..587f39627 100755
--- a/test/network/authstore.rb
+++ b/test/network/authstore.rb
@@ -266,6 +266,51 @@ class TestAuthStore < Test::Unit::TestCase
assert(@store.allowed?("host.madstop.com", "192.168.0.1"),
"More specific allowal by ip failed")
end
+
+ def test_dynamic_backreferences
+ @store.allow("$1.madstop.com")
+
+ assert_nothing_raised { @store.interpolate([nil, "host"]) }
+ assert(@store.allowed?("host.madstop.com", "192.168.0.1"), "interpolation failed")
+ assert_nothing_raised { @store.reset_interpolation }
+ end
+
+ def test_dynamic_ip
+ @store.allow("192.168.0.$1")
+
+ assert_nothing_raised { @store.interpolate([nil, "12"]) }
+ assert(@store.allowed?("host.madstop.com", "192.168.0.12"), "interpolation failed")
+ assert_nothing_raised { @store.reset_interpolation }
+ end
+
+ def test_multiple_dynamic_backreferences
+ @store.allow("$1.$2")
+
+ assert_nothing_raised { @store.interpolate([nil, "host", "madstop.com"]) }
+ assert(@store.allowed?("host.madstop.com", "192.168.0.1"), "interpolation failed")
+ assert_nothing_raised { @store.reset_interpolation }
+ end
+
+ def test_multithreaded_allow_with_dynamic_backreferences
+ @store.allow("$1.madstop.com")
+
+ threads = []
+ 9.times { |a|
+ threads << Thread.new {
+ 9.times { |b|
+ Thread.pass
+ @store.interpolate([nil, "a#{b}", "madstop.com"])
+ Thread.pass
+ assert( @store.allowed?("a#{b}.madstop.com", "192.168.0.1") )
+ Thread.pass
+ @store.reset_interpolation
+ Thread.pass
+ }
+ }
+ }
+ threads.each { |th| th.join }
+ end
+
end
class TestAuthStoreDeclaration < PuppetTest::TestCase
@@ -292,7 +337,9 @@ class TestAuthStoreDeclaration < PuppetTest::TestCase
"billy.Hostname.COM" => [:domain, %w{com hostname billy}, nil],
"billy-jean.Hostname.COM" => [:domain, %w{com hostname billy-jean}, nil],
"*.hostname.COM" => [:domain, %w{com hostname}, 2],
- "*.hostname.COM" => [:domain, %w{com hostname}, 2]
+ "*.hostname.COM" => [:domain, %w{com hostname}, 2],
+ "$1.hostname.COM" => [:dynamic, %w{com hostname $1}, nil],
+ "192.168.$1.$2" => [:dynamic, %w{$2 $1 168 192}, nil]
}.each do |input, output|
# Create a new decl each time, so values aren't cached.