diff options
author | Brice Figureau <brice-puppet@daysofwonder.com> | 2009-04-21 23:53:48 +0200 |
---|---|---|
committer | Brice Figureau <brice-puppet@daysofwonder.com> | 2009-04-23 20:52:03 +0200 |
commit | dc1cd6fb6b143b6525953e619a716f04e678727c (patch) | |
tree | ab38c7fac99ba1cddab062176142ba071caea831 /spec/unit | |
parent | 85233768f080b4cbc4e20eb0c354b6d859a2fb23 (diff) | |
download | puppet-dc1cd6fb6b143b6525953e619a716f04e678727c.tar.gz puppet-dc1cd6fb6b143b6525953e619a716f04e678727c.tar.xz puppet-dc1cd6fb6b143b6525953e619a716f04e678727c.zip |
Fix #1875 - Add a REST authorization system
This patch introduces a new configuration file (and configuration
setting to set it).
Each REST request is checked against this configuration file, and is
either allowed or denied.
The configuration file has the following format:
path /uripath
method <methods>
allow <ip> or <name>
deny <ip> or <name>
or
path ~ <regex>
method <methods>
allow <ip> or <name>
deny <ip> or <name>
where regex is a ruby regex.
This last syntax allows deny/allow interpolation from
the regex captures:
path ~ /files[^/]+/files/([^/]+)/([^/])/
method find
allow $2.$1
If you arrange your files/ directory to have files in
'domain.com/host/', then only the referenced host will
be able to access their files, other hosts will be denied.
For instance:
files/reductivelabs.com/dns/...
files/reductivelabs.com/www/...
then only files in dns can be accessible by dns.reductivelabs.com
and so on...
If the auth.conf file doesn't exist puppet uses sane defaults that allows
clients to check-in and ask for their configurations...
Signed-off-by: Brice Figureau <brice-puppet@daysofwonder.com>
Diffstat (limited to 'spec/unit')
-rwxr-xr-x | spec/unit/network/http/handler.rb | 33 | ||||
-rwxr-xr-x | spec/unit/network/rest_authconfig.rb | 118 | ||||
-rwxr-xr-x | spec/unit/network/rest_authorization.rb | 56 |
3 files changed, 207 insertions, 0 deletions
diff --git a/spec/unit/network/http/handler.rb b/spec/unit/network/http/handler.rb index 84b87025f..7b7ef4722 100755 --- a/spec/unit/network/http/handler.rb +++ b/spec/unit/network/http/handler.rb @@ -2,6 +2,7 @@ require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/network/http/handler' +require 'puppet/network/rest_authorization' class HttpHandled include Puppet::Network::HTTP::Handler @@ -16,6 +17,10 @@ describe Puppet::Network::HTTP::Handler do Puppet::Network::HTTP::Handler.ancestors.should be_include(Puppet::Network::HTTP::API::V1) end + it "should include the Rest Authorization system" do + Puppet::Network::HTTP::Handler.ancestors.should be_include(Puppet::Network::RestAuthorization) + end + it "should have a method for initializing" do @handler.should respond_to(:initialize_for_puppet) end @@ -44,6 +49,8 @@ describe Puppet::Network::HTTP::Handler do @result = stub 'result', :render => "mytext" + @handler.stubs(:authorized?).returns(true) + stub_server_interface end @@ -82,6 +89,32 @@ describe Puppet::Network::HTTP::Handler do @handler.process(@request, @response) end + it "should delegate authorization to the RestAuthorization layer" do + request = stub 'request' + @handler.expects(:uri2indirection).returns request + + request.expects(:method).returns "mymethod" + + @handler.expects(:do_mymethod).with(request, @request, @response) + + @handler.expects(:authorized?).with(request).returns(true) + + @handler.process(@request, @response) + end + + it "should return 403 if the request is not authorized" do + request = stub 'request' + @handler.expects(:uri2indirection).returns request + + @handler.expects(:do_mymethod).never + + @handler.expects(:authorized?).with(request).returns(false) + + @handler.expects(:set_response)#.with { |response, body, status| status == 403 } + + @handler.process(@request, @response) + end + it "should serialize a controller exception when an exception is thrown while finding the model instance" do @handler.expects(:uri2indirection).returns stub("request", :method => :find) diff --git a/spec/unit/network/rest_authconfig.rb b/spec/unit/network/rest_authconfig.rb new file mode 100755 index 000000000..1f98f4082 --- /dev/null +++ b/spec/unit/network/rest_authconfig.rb @@ -0,0 +1,118 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/network/rest_authconfig' + +describe Puppet::Network::RestAuthConfig do + before :each do + FileTest.stubs(:exists?).returns(true) + File.stubs(:stat).returns(stub 'stat', :ctime => :now) + Time.stubs(:now).returns :now + + @authconfig = Puppet::Network::RestAuthConfig.new("dummy", false) + @authconfig.stubs(:read) + + @acl = stub_everything 'rights' + @authconfig.rights = @acl + + @request = stub 'request', :indirection_name => "path", :key => "to/resource", :ip => "127.0.0.1", :node => "me", :method => :save + end + + it "should use the puppet default rest authorization file" do + Puppet.expects(:[]).with(:rest_authconfig).returns("dummy") + + Puppet::Network::RestAuthConfig.new(nil, false) + end + + it "should read the config file when needed" do + @authconfig.expects(:read) + + @authconfig.allowed?(@request) + end + + it "should ask for authorization to the ACL subsystem" do + @acl.expects(:allowed?).with("/path/to/resource", "me", "127.0.0.1", :save) + + @authconfig.allowed?(@request) + end + + describe "when defining an acl with mk_acl" do + it "should create a new right for each default acl" do + @acl.expects(:newright).with(:path) + @authconfig.mk_acl(:path) + end + + it "should allow everyone for each default right" do + @acl.expects(:allow).with(:path, "*") + @authconfig.mk_acl(:path) + end + + it "should restrict the ACL to a method" do + @acl.expects(:restrict_method).with(:path, :method) + @authconfig.mk_acl(:path, :method) + end + end + + describe "when parsing the configuration file" do + it "should check for missing ACL after reading the authconfig file" do + File.stubs(:open) + + @authconfig.expects(:insert_default_acl) + + @authconfig.parse() + end + end + + [ "/facts", "/report", "/catalog", "/file"].each do |acl| + it "should insert #{acl} if not present" do + @authconfig.rights.stubs(:[]).returns(true) + @authconfig.rights.stubs(:[]).with(acl).returns(nil) + + @authconfig.expects(:mk_acl).with { |a,m| a == acl } + + @authconfig.insert_default_acl + end + + it "should not insert #{acl} if present" do + @authconfig.rights.stubs(:[]).returns(true) + @authconfig.rights.stubs(:[]).with(acl).returns(true) + + @authconfig.expects(:mk_acl).never + + @authconfig.insert_default_acl + end + end + + it "should create default ACL entries if no file have been read" do + Puppet::Network::RestAuthConfig.any_instance.stubs(:exists?).returns(false) + + Puppet::Network::RestAuthConfig.any_instance.expects(:insert_default_acl) + + Puppet::Network::RestAuthConfig.main + end + + describe "when adding default ACLs" do + + [ + { :acl => "/facts", :method => [:save, :find] }, + { :acl => "/catalog", :method => :find }, + { :acl => "/report", :method => :save }, + { :acl => "/file" }, + ].each do |acl| + it "should create a default right for #{acl[:acl]}" do + @authconfig.stubs(:mk_acl) + @authconfig.expects(:mk_acl).with(acl[:acl], acl[:method]) + @authconfig.insert_default_acl + end + end + + it "should create a last catch-all deny all rule" do + @authconfig.stubs(:mk_acl) + @acl.expects(:newright).with("/") + @authconfig.insert_default_acl + end + + end + +end diff --git a/spec/unit/network/rest_authorization.rb b/spec/unit/network/rest_authorization.rb new file mode 100755 index 000000000..15351b172 --- /dev/null +++ b/spec/unit/network/rest_authorization.rb @@ -0,0 +1,56 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../spec_helper' + +require 'puppet/network/rest_authorization' + +class RestAuthorized + include Puppet::Network::RestAuthorization +end + + +describe Puppet::Network::RestAuthorization do + before :each do + @auth = RestAuthorized.new + @authconig = stub 'authconfig' + @auth.stubs(:authconfig).returns(@authconfig) + + @request = stub_everything 'request' + @request.stubs(:method).returns(:find) + @request.stubs(:node).returns("node") + end + + describe "when testing request authorization" do + describe "when the client is not authenticated" do + before :each do + @request.stubs(:authenticated?).returns(false) + end + + [ :certificate, :certificate_request].each do |indirection| + it "should allow #{indirection}" do + @request.stubs(:indirection_name).returns(indirection) + @auth.authorized?(@request).should be_true + end + end + + [ :facts, :file_metadata, :file_content, :catalog, :report, :checksum, :runner ].each do |indirection| + it "should not allow #{indirection}" do + @request.stubs(:indirection_name).returns(indirection) + @auth.authorized?(@request).should be_false + end + end + end + + describe "when the client is authenticated" do + before :each do + @request.stubs(:authenticated?).returns(true) + end + + it "should delegate to the current rest authconfig" do + @authconfig.expects(:allowed?).with(@request) + + @auth.authorized?(@request) + end + end + end +end
\ No newline at end of file |