diff --git a/lib/puppet/network/rest_authconfig.rb b/lib/puppet/network/rest_authconfig.rb index dfe8f85c4..7dcc81ef4 100644 --- a/lib/puppet/network/rest_authconfig.rb +++ b/lib/puppet/network/rest_authconfig.rb @@ -1,89 +1,94 @@ require 'puppet/network/authconfig' module Puppet class Network::RestAuthConfig < Network::AuthConfig extend MonitorMixin attr_accessor :rights DEFAULT_ACL = [ { :acl => "~ ^\/catalog\/([^\/]+)$", :method => :find, :allow => '$1', :authenticated => true }, { :acl => "~ ^\/node\/([^\/]+)$", :method => :find, :allow => '$1', :authenticated => true }, # this one will allow all file access, and thus delegate # to fileserver.conf { :acl => "/file" }, { :acl => "/certificate_revocation_list/ca", :method => :find, :authenticated => true }, { :acl => "/report", :method => :save, :authenticated => true }, { :acl => "/certificate/ca", :method => :find, :authenticated => false }, { :acl => "/certificate/", :method => :find, :authenticated => false }, { :acl => "/certificate_request", :method => [:find, :save], :authenticated => false }, { :acl => "/status", :method => [:find], :authenticated => true }, ] def self.main synchronize do add_acl = @main.nil? super @main.insert_default_acl if add_acl and !@main.exists? end @main end + def allowed?(request) + Puppet.deprecation_warning "allowed? should not be called for REST authorization - use check_authorization instead" + check_authorization(request) + end + # check wether this request is allowed in our ACL # raise an Puppet::Network::AuthorizedError if the request # is denied. - def allowed?(indirection, method, key, params) + def check_authorization(indirection, method, key, params) read # we're splitting the request in part because # fail_on_deny could as well be called in the XMLRPC context # with a ClientRequest. if authorization_failure_exception = @rights.is_request_forbidden_and_why?(indirection, method, key, params) Puppet.warning("Denying access: #{authorization_failure_exception}") raise authorization_failure_exception end end def initialize(file = nil, parsenow = true) super(file || Puppet[:rest_authconfig], parsenow) # if we didn't read a file (ie it doesn't exist) # make sure we can create some default rights @rights ||= Puppet::Network::Rights.new end def parse super() insert_default_acl end # force regular ACLs to be present def insert_default_acl DEFAULT_ACL.each do |acl| unless rights[acl[:acl]] Puppet.info "Inserting default '#{acl[:acl]}'(#{acl[:authenticated] ? "auth" : "non-auth"}) ACL because #{( !exists? ? "#{Puppet[:rest_authconfig]} doesn't exist" : "none were found in '#{@file}'")}" mk_acl(acl) end end # queue an empty (ie deny all) right for every other path # actually this is not strictly necessary as the rights system # denies not explicitely allowed paths unless rights["/"] rights.newright("/") rights.restrict_authenticated("/", :any) end end def mk_acl(acl) @rights.newright(acl[:acl]) @rights.allow(acl[:acl], acl[:allow] || "*") if method = acl[:method] method = [method] unless method.is_a?(Array) method.each { |m| @rights.restrict_method(acl[:acl], m) } end @rights.restrict_authenticated(acl[:acl], acl[:authenticated]) unless acl[:authenticated].nil? end end end diff --git a/lib/puppet/network/rest_authorization.rb b/lib/puppet/network/rest_authorization.rb index 50f094e3e..d636d486a 100644 --- a/lib/puppet/network/rest_authorization.rb +++ b/lib/puppet/network/rest_authorization.rb @@ -1,23 +1,23 @@ require 'puppet/network/client_request' require 'puppet/network/rest_authconfig' module Puppet::Network module RestAuthorization # Create our config object if necessary. If there's no configuration file # we install our defaults def authconfig @authconfig ||= Puppet::Network::RestAuthConfig.main @authconfig end # Verify that our client has access. def check_authorization(indirection, method, key, params) - authconfig.allowed?(indirection, method, key, params) + authconfig.check_authorization(indirection, method, key, params) end end end diff --git a/spec/integration/network/rest_authconfig_spec.rb b/spec/integration/network/rest_authconfig_spec.rb index 0e5278860..d2f539cd4 100644 --- a/spec/integration/network/rest_authconfig_spec.rb +++ b/spec/integration/network/rest_authconfig_spec.rb @@ -1,145 +1,145 @@ require 'spec_helper' require 'puppet/network/rest_authconfig' RSpec::Matchers.define :allow do |params| match do |auth| begin - auth.allowed?(params[0], params[1], params[2], params[3]) + auth.check_authorization(params[0], params[1], params[2], params[3]) true rescue Puppet::Network::AuthorizationError false end end failure_message_for_should do |instance| "expected #{params[3][:node]}/#{params[3][:ip]} to be allowed" end failure_message_for_should_not do |instance| "expected #{params[3][:node]}/#{params[3][:ip]} to be forbidden" end end describe Puppet::Network::RestAuthConfig do include PuppetSpec::Files before(:each) do Puppet[:rest_authconfig] = tmpfile('auth.conf') end def add_rule(rule) File.open(Puppet[:rest_authconfig],"w+") do |f| f.print "path /test\n#{rule}\n" end @auth = Puppet::Network::RestAuthConfig.new(Puppet[:rest_authconfig], true) end def add_regex_rule(regex, rule) File.open(Puppet[:rest_authconfig],"w+") do |f| f.print "path ~ #{regex}\n#{rule}\n" end @auth = Puppet::Network::RestAuthConfig.new(Puppet[:rest_authconfig], true) end def request(args = {}) { :ip => '10.1.1.1', :node => 'host.domain.com', :key => 'key', :authenticated => true }.each do |k,v| args[k] ||= v end ['test', :find, args[:key], args] end it "should support IPv4 address" do add_rule("allow 10.1.1.1") @auth.should allow(request) end it "should support CIDR IPv4 address" do add_rule("allow 10.0.0.0/8") @auth.should allow(request) end it "should support wildcard IPv4 address" do add_rule("allow 10.1.1.*") @auth.should allow(request) end it "should support IPv6 address" do add_rule("allow 2001:DB8::8:800:200C:417A") @auth.should allow(request(:ip => '2001:DB8::8:800:200C:417A')) end it "should support hostname" do add_rule("allow host.domain.com") @auth.should allow(request) end it "should support wildcard host" do add_rule("allow *.domain.com") @auth.should allow(request) end it "should support hostname backreferences" do add_regex_rule('^/test/([^/]+)$', "allow $1.domain.com") @auth.should allow(request(:key => 'host')) end it "should support opaque strings" do add_rule("allow this-is-opaque@or-not") @auth.should allow(request(:node => 'this-is-opaque@or-not')) end it "should support opaque strings and backreferences" do add_regex_rule('^/test/([^/]+)$', "allow $1") @auth.should allow(request(:key => 'this-is-opaque@or-not', :node => 'this-is-opaque@or-not')) end it "should support hostname ending with '.'" do pending('bug #7589') add_rule("allow host.domain.com.") @auth.should allow(request(:node => 'host.domain.com.')) end it "should support hostname ending with '.' and backreferences" do pending('bug #7589') add_regex_rule('^/test/([^/]+)$',"allow $1") @auth.should allow(request(:node => 'host.domain.com.')) end it "should support trailing whitespace" do add_rule('allow host.domain.com ') @auth.should allow(request) end it "should support inlined comments" do add_rule('allow host.domain.com # will it work?') @auth.should allow(request) end it "should deny non-matching host" do add_rule("allow inexistant") @auth.should_not allow(request) end it "should deny denied hosts" do add_rule("deny host.domain.com") @auth.should_not allow(request) end end \ No newline at end of file diff --git a/spec/unit/network/rest_authconfig_spec.rb b/spec/unit/network/rest_authconfig_spec.rb index e1403997f..bebbb874f 100755 --- a/spec/unit/network/rest_authconfig_spec.rb +++ b/spec/unit/network/rest_authconfig_spec.rb @@ -1,127 +1,127 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/network/rest_authconfig' describe Puppet::Network::RestAuthConfig do DEFAULT_ACL = Puppet::Network::RestAuthConfig::DEFAULT_ACL before :each do FileTest.stubs(:exists?).returns(true) File.stubs(:stat).returns(stub('stat', :ctime => :now)) Time.stubs(:now).returns Time.now @authconfig = Puppet::Network::RestAuthConfig.new("dummy", false) @authconfig.stubs(:read) @acl = stub_everything 'rights' @authconfig.rights = @acl 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 ask for authorization to the ACL subsystem" do params = {:ip => "127.0.0.1", :node => "me", :environment => :env, :authenticated => true} @acl.expects(:is_request_forbidden_and_why?).with("path", :save, "to/resource", params).returns(nil) - @authconfig.allowed?("path", :save, "to/resource", params) + @authconfig.check_authorization("path", :save, "to/resource", params) 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(:acl => :path) end it "should allow everyone for each default right" do @acl.expects(:allow).with(:path, "*") @authconfig.mk_acl(:acl => :path) end it "should restrict the ACL to a method" do @acl.expects(:restrict_method).with(:path, :method) @authconfig.mk_acl(:acl => :path, :method => :method) end it "should restrict the ACL to a specific authentication state" do @acl.expects(:restrict_authenticated).with(:path, :authentication) @authconfig.mk_acl(:acl => :path, :authenticated => :authentication) 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 DEFAULT_ACL.each do |acl| it "should insert #{acl[:acl]} if not present" do @authconfig.rights.stubs(:[]).returns(true) @authconfig.rights.stubs(:[]).with(acl[:acl]).returns(nil) @authconfig.expects(:mk_acl).with { |h| h[:acl] == acl[:acl] } @authconfig.insert_default_acl end it "should not insert #{acl[: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 DEFAULT_ACL.each do |acl| it "should create a default right for #{acl[:acl]}" do @authconfig.stubs(:mk_acl) @authconfig.expects(:mk_acl).with(acl) @authconfig.insert_default_acl end end it "should log at info loglevel" do Puppet.expects(:info).at_least_once @authconfig.insert_default_acl 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 it "should create a last catch-all deny all rule for any authenticated request state" do @authconfig.stubs(:mk_acl) @acl.stubs(:newright).with("/") @acl.expects(:restrict_authenticated).with("/", :any) @authconfig.insert_default_acl end end end