diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index 2a4736365..abbb2dfa9 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -1,69 +1,69 @@ require 'puppet/network/http/api' module Puppet::Network::HTTP::API::V1 # How we map http methods and the indirection name in the URI # to an indirection method. METHOD_MAP = { "GET" => { :plural => :search, :singular => :find }, "PUT" => { :singular => :save }, "DELETE" => { :singular => :destroy } } def uri2indirection(http_method, uri, params) environment, indirection, key = uri.split("/", 4)[1..-1] # the first field is always nil because of the leading slash raise ArgumentError, "The environment must be purely alphanumeric, not '#{environment}'" unless environment =~ /^\w+$/ raise ArgumentError, "The indirection name must be purely alphanumeric, not '#{indirection}'" unless indirection =~ /^\w+$/ method = indirection_method(http_method, indirection) params[:environment] = environment raise ArgumentError, "No request key specified in #{uri}" if key == "" or key.nil? key = URI.unescape(key) - Puppet::Indirector::Request.new(indirection, method, key, params) + [indirection, method, key, params] end def indirection2uri(request) indirection = request.method == :search ? pluralize(request.indirection_name.to_s) : request.indirection_name.to_s "/#{request.environment.to_s}/#{indirection}/#{request.escaped_key}#{request.query_string}" end def indirection_method(http_method, indirection) raise ArgumentError, "No support for http method #{http_method}" unless METHOD_MAP[http_method] unless method = METHOD_MAP[http_method][plurality(indirection)] raise ArgumentError, "No support for plural #{http_method} operations" end method end def pluralize(indirection) return(indirection == "status" ? "statuses" : indirection + "s") end def plurality(indirection) # NOTE This specific hook for facts is ridiculous, but it's a *many*-line # fix to not need this, and our goal is to move away from the complication # that leads to the fix being too long. return :singular if indirection == "facts" return :singular if indirection == "status" return :plural if indirection == "inventory" result = (indirection =~ /s$/) ? :plural : :singular indirection.sub!(/s$/, '') if result result end end diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index e5aa1c56f..82238aa0a 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -1,229 +1,229 @@ module Puppet::Network::HTTP end require 'puppet/network/http/api/v1' require 'puppet/network/rest_authorization' require 'puppet/network/rights' require 'resolv' module Puppet::Network::HTTP::Handler include Puppet::Network::HTTP::API::V1 include Puppet::Network::RestAuthorization attr_reader :server, :handler # Retrieve the accept header from the http request. def accept_header(request) raise NotImplementedError end # Retrieve the Content-Type header from the http request. def content_type_header(request) raise NotImplementedError end # Which format to use when serializing our response or interpreting the request. # IF the client provided a Content-Type use this, otherwise use the Accept header # and just pick the first value. def format_to_use(request) unless header = accept_header(request) raise ArgumentError, "An Accept header must be provided to pick the right format" end format = nil header.split(/,\s*/).each do |name| next unless format = Puppet::Network::FormatHandler.format(name) next unless format.suitable? return format end raise "No specified acceptable formats (#{header}) are functional on this machine" end def request_format(request) if header = content_type_header(request) header.gsub!(/\s*;.*$/,'') # strip any charset format = Puppet::Network::FormatHandler.mime(header) raise "Client sent a mime-type (#{header}) that doesn't correspond to a format we support" if format.nil? return format.name.to_s if format.suitable? end raise "No Content-Type header was received, it isn't possible to unserialize the request" end def format_to_mime(format) format.is_a?(Puppet::Network::Format) ? format.mime : format end def initialize_for_puppet(server) @server = server end # handle an HTTP request def process(request, response) - indirection_request = uri2indirection(http_method(request), path(request), params(request)) + indirection, method, key, params = uri2indirection(http_method(request), path(request), params(request)) - check_authorization(indirection_request) + check_authorization(indirection, method, key, params) - send("do_#{indirection_request.method}", indirection_request, request, response) + send("do_#{method}", indirection, key, params, request, response) rescue SystemExit,NoMemoryError raise rescue Exception => e return do_exception(response, e) end # Set the response up, with the body and status. def set_response(response, body, status = 200) raise NotImplementedError end # Set the specified format as the content type of the response. def set_content_type(response, format) raise NotImplementedError end def do_exception(response, exception, status=400) if exception.is_a?(Puppet::Network::AuthorizationError) # make sure we return the correct status code # for authorization issues status = 403 if status == 400 end if exception.is_a?(Exception) puts exception.backtrace if Puppet[:trace] Puppet.err(exception) end set_content_type(response, "text/plain") set_response(response, exception.to_s, status) end + def model(indirection_name) + raise ArgumentError, "Could not find indirection '#{indirection_name}'" unless indirection = Puppet::Indirector::Indirection.instance(indirection_name.to_sym) + indirection.model + end + # Execute our find. - def do_find(indirection_request, request, response) - unless result = indirection_request.model.find(indirection_request.key, indirection_request.to_hash) - Puppet.info("Could not find #{indirection_request.indirection_name} for '#{indirection_request.key}'") - return do_exception(response, "Could not find #{indirection_request.indirection_name} #{indirection_request.key}", 404) + def do_find(indirection_name, key, params, request, response) + unless result = model(indirection_name).find(key, params) + Puppet.info("Could not find #{indirection_name} for '#{key}'") + return do_exception(response, "Could not find #{indirection_name} #{key}", 404) end # The encoding of the result must include the format to use, # and it needs to be used for both the rendering and as # the content type. format = format_to_use(request) set_content_type(response, format) set_response(response, result.render(format)) end # Execute our search. - def do_search(indirection_request, request, response) - result = indirection_request.model.search(indirection_request.key, indirection_request.to_hash) + def do_search(indirection_name, key, params, request, response) + model = self.model(indirection_name) + result = model.search(key, params) if result.nil? - return do_exception(response, "Could not find instances in #{indirection_request.indirection_name} with '#{indirection_request.key}'", 404) + return do_exception(response, "Could not find instances in #{indirection_name} with '#{key}'", 404) end format = format_to_use(request) set_content_type(response, format) - set_response(response, indirection_request.model.render_multiple(format, result)) + set_response(response, model.render_multiple(format, result)) end # Execute our destroy. - def do_destroy(indirection_request, request, response) - result = indirection_request.model.destroy(indirection_request.key, indirection_request.to_hash) + def do_destroy(indirection_name, key, params, request, response) + result = model(indirection_name).destroy(key, params) return_yaml_response(response, result) end # Execute our save. - def do_save(indirection_request, request, response) + def do_save(indirection_name, key, params, request, response) data = body(request).to_s raise ArgumentError, "No data to save" if !data or data.empty? format = request_format(request) - obj = indirection_request.model.convert_from(format, data) - result = save_object(indirection_request, obj) + obj = model(indirection_name).convert_from(format, data) + result = obj.save(key) return_yaml_response(response, result) end # resolve node name from peer's ip address # this is used when the request is unauthenticated def resolve_node(result) begin return Resolv.getname(result[:ip]) rescue => detail Puppet.err "Could not resolve #{result[:ip]}: #{detail}" end result[:ip] end private def return_yaml_response(response, body) set_content_type(response, Puppet::Network::FormatHandler.format("yaml")) set_response(response, body.to_yaml) end - # LAK:NOTE This has to be here for testing; it's a stub-point so - # we keep infinite recursion from happening. - def save_object(ind_request, object) - object.save(ind_request.key) - end - def get?(request) http_method(request) == 'GET' end def put?(request) http_method(request) == 'PUT' end def delete?(request) http_method(request) == 'DELETE' end # methods to be overridden by the including web server class def http_method(request) raise NotImplementedError end def path(request) raise NotImplementedError end def request_key(request) raise NotImplementedError end def body(request) raise NotImplementedError end def params(request) raise NotImplementedError end def decode_params(params) params.inject({}) do |result, ary| param, value = ary next result if param.nil? || param.empty? param = param.to_sym # These shouldn't be allowed to be set by clients # in the query string, for security reasons. next result if param == :node next result if param == :ip value = CGI.unescape(value) if value =~ /^---/ value = YAML.load(value) else value = true if value == "true" value = false if value == "false" value = Integer(value) if value =~ /^\d+$/ value = value.to_f if value =~ /^\d+\.\d+$/ end result[param] = value result end end end diff --git a/lib/puppet/network/rest_authconfig.rb b/lib/puppet/network/rest_authconfig.rb index 82d5a9de2..b6a163316 100644 --- a/lib/puppet/network/rest_authconfig.rb +++ b/lib/puppet/network/rest_authconfig.rb @@ -1,97 +1,97 @@ 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 }, # 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 }, { :acl => "/resource", :method => [:find, :save, :search], :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 # check wether this request is allowed in our ACL # raise an Puppet::Network::AuthorizedError if the request # is denied. - def allowed?(request) + def allowed?(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. - @rights.fail_on_deny( - build_uri(request), - - :node => request.node, - :ip => request.ip, - :method => request.method, - :environment => request.environment, - :authenticated => request.authenticated) + @rights.fail_on_deny( + build_uri(indirection, key), + :node => params[:node], + :ip => params[:ip], + :method => method, + :environment => params[:environment], + :authenticated => params[:authenticated] + ) 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 where 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 - def build_uri(request) - "/#{request.indirection_name}/#{request.key}" + def build_uri(indirection_name, key) + "/#{indirection_name}/#{key}" end end end diff --git a/lib/puppet/network/rest_authorization.rb b/lib/puppet/network/rest_authorization.rb index e052245eb..50f094e3e 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(request) - authconfig.allowed?(request) + def check_authorization(indirection, method, key, params) + authconfig.allowed?(indirection, method, key, params) end end end diff --git a/spec/integration/indirector/bucket_file/rest_spec.rb b/spec/integration/indirector/bucket_file/rest_spec.rb deleted file mode 100644 index dc10faa67..000000000 --- a/spec/integration/indirector/bucket_file/rest_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/file_bucket/file' -require 'puppet/network/server' -require 'puppet/network/http/webrick/rest' - -describe "Filebucket REST Terminus" do - before do - Puppet[:masterport] = 34343 - Puppet[:server] = "localhost" - - # Get a safe temporary file - @tmpfile = Tempfile.new("webrick_integration_testing") - @dir = @tmpfile.path + "_dir" - - Puppet.settings[:confdir] = @dir - Puppet.settings[:vardir] = @dir - Puppet.settings[:group] = Process.gid - Puppet.settings[:server] = "127.0.0.1" - Puppet.settings[:masterport] = "34343" - - Puppet::Util::Cacher.expire - - Puppet[:servertype] = 'webrick' - Puppet[:server] = '127.0.0.1' - Puppet[:certname] = '127.0.0.1' - - # Generate the certificate with a local CA - Puppet::SSL::Host.ca_location = :local - ca = Puppet::SSL::CertificateAuthority.new - ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) - ca.generate("foo.madstop.com") unless Puppet::SSL::Certificate.find(Puppet[:certname]) - - @host = Puppet::SSL::Host.new(Puppet[:certname]) - - @params = { :port => 34343, :handlers => [ :file_bucket_file ] } - @server = Puppet::Network::Server.new(@params) - @server.listen - - @old_terminus = Puppet::FileBucket::File.indirection.terminus_class - Puppet::FileBucket::File.terminus_class = :rest - - # LAK:NOTE We need to have a fake model here so that our indirected methods get - # passed through REST; otherwise we'd be stubbing 'find', which would cause an immediate - # return. - @file_bucket_file = stub_everything 'file_bucket_file' - @mock_model = stub('faked model', :name => "file_bucket_file", :convert_from => @file_bucket_file) - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:check_authorization) - end - - after do - Puppet::Network::HttpPool.expire - Puppet::SSL::Host.ca_location = :none - Puppet.settings.clear - @server.unlisten - Puppet::FileBucket::File.terminus_class = @old_terminus - end - - it "should be able save a file to the remote filebucket" do - @file_bucket_file.expects(:save) - - file_bucket_file = Puppet::FileBucket::File.new("pouet") - file_bucket_file.save - end -end diff --git a/spec/integration/indirector/certificate/rest_spec.rb b/spec/integration/indirector/certificate/rest_spec.rb deleted file mode 100755 index 58aa96c48..000000000 --- a/spec/integration/indirector/certificate/rest_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/ssl/certificate' -require 'puppet/network/server' -require 'puppet/network/http/webrick/rest' - -describe "Certificate REST Terminus" do - before do - Puppet[:masterport] = 34343 - Puppet[:server] = "localhost" - - # Get a safe temporary file - @tmpfile = Tempfile.new("webrick_integration_testing") - @dir = @tmpfile.path + "_dir" - - Puppet.settings[:confdir] = @dir - Puppet.settings[:vardir] = @dir - Puppet.settings[:group] = Process.gid - Puppet.settings[:server] = "127.0.0.1" - Puppet.settings[:masterport] = "34343" - - Puppet::Util::Cacher.expire - - Puppet[:servertype] = 'webrick' - Puppet[:server] = '127.0.0.1' - Puppet[:certname] = '127.0.0.1' - - # Generate the certificate with a local CA - Puppet::SSL::Host.ca_location = :local - ca = Puppet::SSL::CertificateAuthority.new - ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) - ca.generate("foo.madstop.com") unless Puppet::SSL::Certificate.find(Puppet[:certname]) - - @host = Puppet::SSL::Host.new(Puppet[:certname]) - - @params = { :port => 34343, :handlers => [ :certificate ] } - @server = Puppet::Network::Server.new(@params) - @server.listen - - # Then switch to a remote CA, so that we go through REST. - Puppet::SSL::Host.ca_location = :remote - - # LAK:NOTE We need to have a fake model here so that our indirected methods get - # passed through REST; otherwise we'd be stubbing 'find', which would cause an immediate - # return. - @mock_model = stub('faked model', :name => "certificate") - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:check_authorization).returns(true) - end - - after do - Puppet::Network::HttpPool.expire - Puppet::SSL::Host.ca_location = :none - Puppet.settings.clear - @server.unlisten - end - - it "should be able to retrieve a remote certificate" do - @mock_model.expects(:find).returns @host.certificate - result = Puppet::SSL::Certificate.find('bar') - - # There's no good '==' method on certs. - result.content.to_s.should == @host.certificate.content.to_s - result.name.should == "bar" - end -end diff --git a/spec/integration/indirector/certificate_request/rest_spec.rb b/spec/integration/indirector/certificate_request/rest_spec.rb deleted file mode 100755 index c718b78ab..000000000 --- a/spec/integration/indirector/certificate_request/rest_spec.rb +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/ssl/certificate_request' -require 'puppet/network/server' -require 'puppet/network/http/webrick/rest' - -describe "Certificate Request REST Terminus" do - before do - Puppet::Util::Cacher.expire - - Puppet[:masterport] = 34343 - Puppet[:server] = "localhost" - - # Get a safe temporary file - @tmpfile = Tempfile.new("webrick_integration_testing") - @dir = @tmpfile.path + "_dir" - - Puppet.settings[:confdir] = @dir - Puppet.settings[:vardir] = @dir - Puppet.settings[:group] = Process.gid - Puppet.settings[:server] = "127.0.0.1" - Puppet.settings[:masterport] = "34343" - - Puppet[:servertype] = 'webrick' - Puppet[:server] = '127.0.0.1' - Puppet[:certname] = '127.0.0.1' - - # Generate the certificate with a local CA - Puppet::SSL::Host.ca_location = :local - ca = Puppet::SSL::CertificateAuthority.new - ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) - - # Create the CSR and write it to disk - @host = Puppet::SSL::Host.new("foo.madstop.com") - @host.generate_certificate_request - - # Now remove the cached csr - Puppet::SSL::Host.ca_location = :none - Puppet::SSL::Host.destroy("foo.madstop.com") - - @params = { :port => 34343, :handlers => [ :certificate_request ] } - @server = Puppet::Network::Server.new(@params) - @server.listen - - # Then switch to a remote CA, so that we go through REST. - Puppet::SSL::Host.ca_location = :remote - - # LAK:NOTE We need to have a fake model here so that our indirected methods get - # passed through REST; otherwise we'd be stubbing 'find', which would cause an immediate - # return. - @mock_model = stub('faked model', :name => "certificate request") - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:check_authorization).returns(true) - end - - after do - Puppet::Network::HttpPool.expire - Puppet::SSL::Host.ca_location = :none - Puppet.settings.clear - @server.unlisten - end - - it "should be able to save a certificate request to the CA" do - key = Puppet::SSL::Key.new("bar.madstop.com") - key.generate - - csr = Puppet::SSL::CertificateRequest.new("bar.madstop.com") - csr.generate(key.content) - - server_csr = mock 'csr' - server_csr.expects(:save) - @mock_model.expects(:convert_from).with("s", csr.content.to_s).returns server_csr - - csr.save - end - - it "should be able to retrieve a remote certificate request" do - # We're finding the cached value :/ - @mock_model.expects(:find).returns @host.certificate_request - result = Puppet::SSL::CertificateRequest.find('foo.madstop.com') - - # There's no good '==' method on certs. - result.content.to_s.should == @host.certificate_request.content.to_s - result.name.should == @host.certificate_request.name - end -end diff --git a/spec/integration/indirector/certificate_revocation_list/rest_spec.rb b/spec/integration/indirector/certificate_revocation_list/rest_spec.rb deleted file mode 100755 index d831805c8..000000000 --- a/spec/integration/indirector/certificate_revocation_list/rest_spec.rb +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/ssl/certificate' -require 'puppet/network/server' -require 'puppet/network/http/webrick/rest' - -describe "Certificate REST Terminus" do - before do -# This port randomization is NOT a good pattern for testing, don't copy -# but it's a quick fix to deal with sporadic test failures in Hudson #4894 -# Ticket #5098 created to fix these tests in the future - masterport = rand(100) + 34500 - Puppet[:masterport] = masterport - Puppet[:server] = "localhost" - - # Get a safe temporary file - @tmpfile = Tempfile.new("webrick_integration_testing") - @dir = @tmpfile.path + "_dir" - - Puppet.settings[:confdir] = @dir - Puppet.settings[:vardir] = @dir - Puppet.settings[:group] = Process.gid - Puppet.settings[:server] = "127.0.0.1" - Puppet.settings[:masterport] = masterport - - Puppet::Util::Cacher.expire - - Puppet[:servertype] = 'webrick' - Puppet[:server] = '127.0.0.1' - Puppet[:certname] = '127.0.0.1' - - # Generate the certificate with a local CA - Puppet::SSL::Host.ca_location = :local - ca = Puppet::SSL::CertificateAuthority.new - ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) - - @params = { :port => masterport, :handlers => [ :certificate_revocation_list ] } - @server = Puppet::Network::Server.new(@params) - @server.listen - - # And make sure we've generated the CRL - @crl = ca.crl - - # Now remove the cached crl - Puppet::SSL::Host.ca_location = :none - Puppet::SSL::CertificateRevocationList.destroy(Puppet::SSL::CA_NAME) - - # This is necessary so that we create the SSL store before we start - # using REST. This is necessary to prevent an infinite loop, - # which only occurs during testing. - Puppet::Network::HttpPool.ssl_host.ssl_store - - # Then switch to a remote CA, so that we go through REST. - Puppet::SSL::Host.ca_location = :remote - - # LAK:NOTE We need to have a fake model here so that our indirected methods get - # passed through REST; otherwise we'd be stubbing 'find', which would cause an immediate - # return. - @mock_model = stub('faked model', :name => "certificate") - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:check_authorization).returns(true) - end - - after do - Puppet::Network::HttpPool.expire - Puppet::SSL::Host.ca_location = :none - Puppet.settings.clear - @server.unlisten - end - - it "should be able to retrieve a remote CRL" do - @mock_model.expects(:find).returns @crl - result = Puppet::SSL::CertificateRevocationList.find('bar') - - # There's no good '==' method on certs. - result.content.to_s.should == @crl.content.to_s - end -end diff --git a/spec/integration/indirector/report/rest_spec.rb b/spec/integration/indirector/report/rest_spec.rb deleted file mode 100644 index fdc218975..000000000 --- a/spec/integration/indirector/report/rest_spec.rb +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env ruby - -Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } - -require 'puppet/transaction/report' -require 'puppet/network/server' -require 'puppet/network/http/webrick/rest' - -describe "Report REST Terminus" do - before do - Puppet[:masterport] = 34343 - Puppet[:server] = "localhost" - - # Get a safe temporary file - @tmpfile = Tempfile.new("webrick_integration_testing") - @dir = @tmpfile.path + "_dir" - - Puppet.settings[:confdir] = @dir - Puppet.settings[:vardir] = @dir - Puppet.settings[:group] = Process.gid - Puppet.settings[:server] = "127.0.0.1" - Puppet.settings[:masterport] = "34343" - - Puppet::Util::Cacher.expire - - Puppet[:servertype] = 'webrick' - Puppet[:server] = '127.0.0.1' - Puppet[:certname] = '127.0.0.1' - - # Generate the certificate with a local CA - Puppet::SSL::Host.ca_location = :local - ca = Puppet::SSL::CertificateAuthority.new - ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) - ca.generate("foo.madstop.com") unless Puppet::SSL::Certificate.find(Puppet[:certname]) - - @host = Puppet::SSL::Host.new(Puppet[:certname]) - - @params = { :port => 34343, :handlers => [ :report ] } - @server = Puppet::Network::Server.new(@params) - @server.listen - - # Let's use REST for our reports :-) - @old_terminus = Puppet::Transaction::Report.indirection.terminus_class - Puppet::Transaction::Report.terminus_class = :rest - - # LAK:NOTE We need to have a fake model here so that our indirected methods get - # passed through REST; otherwise we'd be stubbing 'save', which would cause an immediate - # return. - @report = stub_everything 'report' - @mock_model = stub_everything 'faked model', :name => "report", :convert_from => @report - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:check_authorization) - end - - after do - Puppet::Network::HttpPool.expire - Puppet::SSL::Host.ca_location = :none - Puppet.settings.clear - @server.unlisten - Puppet::Transaction::Report.terminus_class = @old_terminus - end - - it "should be able to send a report to the server" do - @report.expects(:save) - - report = Puppet::Transaction::Report.new - - resourcemetrics = { - :total => 12, - :out_of_sync => 20, - :applied => 45, - :skipped => 1, - :restarted => 23, - :failed_restarts => 1, - :scheduled => 10 - } - report.add_metric(:resources, resourcemetrics) - - timemetrics = { - :resource1 => 10, - :resource2 => 50, - :resource3 => 40, - :resource4 => 20, - } - report.add_metric(:times, timemetrics) - - - report.add_metric( - :changes, - - :total => 20 - ) - - report.save - end -end diff --git a/spec/integration/indirector/rest_spec.rb b/spec/integration/indirector/rest_spec.rb deleted file mode 100755 index e36d793ea..000000000 --- a/spec/integration/indirector/rest_spec.rb +++ /dev/null @@ -1,529 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet/network/server' -require 'puppet/indirector' -require 'puppet/indirector/rest' - -# a fake class that will be indirected via REST -class Puppet::TestIndirectedFoo - extend Puppet::Indirector - indirects :test_indirected_foo, :terminus_setting => :test_indirected_foo_terminus - - attr_reader :value - attr_accessor :name - - def initialize(value = 0) - @value = value - end - - def self.from_yaml(yaml) - YAML.load(yaml) - end - - def name - "bob" - end -end - -# empty Terminus class -- this would normally have to be in a directory findable by the autoloader, but we short-circuit that below -class Puppet::TestIndirectedFoo::Rest < Puppet::Indirector::REST -end - -# This port randomization is NOT a good pattern for testing, don't copy -# but it's a quick fix to deal with sporadic test failures in Hudson #4894 -# Ticket #5098 created to fix these tests in the future -masterport = rand(100) + 34300 - -describe Puppet::Indirector::REST do - before do - # Get a safe temporary file - @tmpfile = Tempfile.new("webrick_integration_testing") - @dir = @tmpfile.path + "_dir" - - Puppet.settings[:confdir] = @dir - Puppet.settings[:vardir] = @dir - Puppet.settings[:group] = Process.gid - Puppet.settings[:server] = "127.0.0.1" - Puppet.settings[:masterport] = masterport - - Puppet::SSL::Host.ca_location = :local - - Puppet::TestIndirectedFoo.terminus_class = :rest - end - - after do - Puppet::Network::HttpPool.expire - Puppet::SSL::Host.ca_location = :none - Puppet.settings.clear - end - - describe "when using webrick" do - before :each do - Puppet::Util::Cacher.expire - - Puppet[:servertype] = 'webrick' - Puppet[:server] = '127.0.0.1' - Puppet[:certname] = '127.0.0.1' - - ca = Puppet::SSL::CertificateAuthority.new - ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.find(Puppet[:certname]) - - @params = { :port => masterport, :handlers => [ :test_indirected_foo ], :xmlrpc_handlers => [ :status ] } - @server = Puppet::Network::Server.new(@params) - @server.listen - - # LAK:NOTE We need to have a fake model here so that our indirected methods get - # passed through REST; otherwise we'd be stubbing 'find', which would cause an immediate - # return. - @mock_model = stub('faked model', :name => "foo") - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - - # do not trigger the authorization layer - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:check_authorization).returns(true) - end - - describe "when finding a model instance over REST" do - describe "when a matching model instance can be found" do - before :each do - @model_instance = Puppet::TestIndirectedFoo.new(23) - @mock_model.stubs(:find).returns @model_instance - end - - it "should not fail" do - lambda { Puppet::TestIndirectedFoo.find('bar') }.should_not raise_error - end - - it 'should return an instance of the model class' do - Puppet::TestIndirectedFoo.find('bar').class.should == Puppet::TestIndirectedFoo - end - - it "should pass options all the way through" do - @mock_model.expects(:find).with { |key, args| args[:one] == "two" and args[:three] == "four" }.returns @model_instance - Puppet::TestIndirectedFoo.find('bar', :one => "two", :three => "four") - end - - it 'should return the instance of the model class associated with the provided lookup key' do - Puppet::TestIndirectedFoo.find('bar').value.should == @model_instance.value - end - - it 'should set an expiration on model instance' do - Puppet::TestIndirectedFoo.find('bar').expiration.should_not be_nil - end - - it "should use a supported format" do - Puppet::TestIndirectedFoo.expects(:supported_formats).returns ["marshal"] - text = Marshal.dump(@model_instance) - @model_instance.expects(:render).with(Puppet::Network::FormatHandler.format("marshal")).returns text - Puppet::TestIndirectedFoo.find('bar') - end - end - - describe "when no matching model instance can be found" do - before :each do - @mock_model = stub('faked model', :name => "foo", :find => nil) - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - end - - it "should return nil" do - Puppet::TestIndirectedFoo.find('bar').should be_nil - end - end - - describe "when an exception is encountered in looking up a model instance" do - before :each do - @mock_model = stub('faked model', :name => "foo") - @mock_model.stubs(:find).raises(RuntimeError) - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - end - - it "should raise an exception" do - lambda { Puppet::TestIndirectedFoo.find('bar') }.should raise_error(Net::HTTPError) - end - end - end - - describe "when searching for model instances over REST" do - describe "when matching model instances can be found" do - before :each do - @model_instances = [ Puppet::TestIndirectedFoo.new(23), Puppet::TestIndirectedFoo.new(24) ] - @mock_model.stubs(:search).returns @model_instances - - # Force yaml, because otherwise our mocks can't work correctly - Puppet::TestIndirectedFoo.stubs(:supported_formats).returns %w{yaml} - - @mock_model.stubs(:render_multiple).returns @model_instances.to_yaml - end - - it "should not fail" do - lambda { Puppet::TestIndirectedFoo.search('bar') }.should_not raise_error - end - - it 'should return all matching results' do - Puppet::TestIndirectedFoo.search('bar').length.should == @model_instances.length - end - - it "should pass options all the way through" do - @mock_model.expects(:search).with { |key, args| args[:one] == "two" and args[:three] == "four" }.returns @model_instances - Puppet::TestIndirectedFoo.search("foo", :one => "two", :three => "four") - end - - it 'should return model instances' do - Puppet::TestIndirectedFoo.search('bar').each do |result| - result.class.should == Puppet::TestIndirectedFoo - end - end - - it 'should return the instance of the model class associated with the provided lookup key' do - Puppet::TestIndirectedFoo.search('bar').collect { |i| i.value }.should == @model_instances.collect { |i| i.value } - end - end - - describe "when no matching model instance can be found" do - before :each do - @mock_model = stub('faked model', :name => "foo", :find => nil) - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - end - - it "should return nil" do - Puppet::TestIndirectedFoo.find('bar').should be_nil - end - end - - describe "when an exception is encountered in looking up a model instance" do - before :each do - @mock_model = stub('faked model') - @mock_model.stubs(:find).raises(RuntimeError) - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - end - - it "should raise an exception" do - lambda { Puppet::TestIndirectedFoo.find('bar') }.should raise_error(Net::HTTPError) - end - end - end - - describe "when destroying a model instance over REST" do - describe "when a matching model instance can be found" do - before :each do - @mock_model.stubs(:destroy).returns true - end - - it "should not fail" do - lambda { Puppet::TestIndirectedFoo.destroy('bar') }.should_not raise_error - end - - it 'should return success' do - Puppet::TestIndirectedFoo.destroy('bar').should == true - end - end - - describe "when no matching model instance can be found" do - before :each do - @mock_model.stubs(:destroy).returns false - end - - it "should return failure" do - Puppet::TestIndirectedFoo.destroy('bar').should == false - end - end - - describe "when an exception is encountered in destroying a model instance" do - before :each do - @mock_model.stubs(:destroy).raises(RuntimeError) - end - - it "should raise an exception" do - lambda { Puppet::TestIndirectedFoo.destroy('bar') }.should raise_error(Net::HTTPError) - end - end - end - - describe "when saving a model instance over REST" do - before :each do - @instance = Puppet::TestIndirectedFoo.new(42) - @mock_model.stubs(:save_object).returns @instance - @mock_model.stubs(:convert_from).returns @instance - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:save_object).returns(@instance) - end - - describe "when a successful save can be performed" do - before :each do - end - - it "should not fail" do - lambda { @instance.save }.should_not raise_error - end - - it 'should return an instance of the model class' do - @instance.save.class.should == Puppet::TestIndirectedFoo - end - - it 'should return a matching instance of the model class' do - @instance.save.value.should == @instance.value - end - end - - describe "when a save cannot be completed" do - before :each do - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:save_object).returns(false) - end - - it "should return failure" do - @instance.save.should == false - end - end - - describe "when an exception is encountered in performing a save" do - before :each do - Puppet::Network::HTTP::WEBrickREST.any_instance.stubs(:save_object).raises(RuntimeError) - end - - it "should raise an exception" do - lambda { @instance.save }.should raise_error(Net::HTTPError) - end - end - end - - after :each do - @server.unlisten - end - end - - describe "when using mongrel" do - confine "Mongrel is not available" => Puppet.features.mongrel? - - before :each do - Puppet[:servertype] = 'mongrel' - @params = { :port => masterport, :handlers => [ :test_indirected_foo ] } - - # Make sure we never get a cert, since mongrel can't speak ssl - Puppet::SSL::Certificate.stubs(:find).returns nil - - # We stub ssl to be off, since mongrel can't speak ssl - Net::HTTP.any_instance.stubs(:use_ssl?).returns false - - @server = Puppet::Network::Server.new(@params) - @server.listen - - # LAK:NOTE We need to have a fake model here so that our indirected methods get - # passed through REST; otherwise we'd be stubbing 'find', which would cause an immediate - # return. - @mock_model = stub('faked model', :name => "foo") - Puppet::Indirector::Request.any_instance.stubs(:model).returns(@mock_model) - - # do not trigger the authorization layer - Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:check_authorization).returns(true) - end - - after :each do - @server.unlisten - end - - describe "when finding a model instance over REST" do - describe "when a matching model instance can be found" do - before :each do - @model_instance = Puppet::TestIndirectedFoo.new(23) - @mock_model.stubs(:find).returns @model_instance - end - - it "should not fail" do - lambda { Puppet::TestIndirectedFoo.find('bar') }.should_not raise_error - end - - it 'should return an instance of the model class' do - Puppet::TestIndirectedFoo.find('bar').class.should == Puppet::TestIndirectedFoo - end - - it "should pass options all the way through" do - @mock_model.expects(:find).with { |key, args| args[:one] == "two" and args[:three] == "four" }.returns @model_instance - Puppet::TestIndirectedFoo.find('bar', :one => "two", :three => "four") - end - - it 'should return the instance of the model class associated with the provided lookup key' do - Puppet::TestIndirectedFoo.find('bar').value.should == @model_instance.value - end - - it 'should set an expiration on model instance' do - Puppet::TestIndirectedFoo.find('bar').expiration.should_not be_nil - end - - it "should use a supported format" do - Puppet::TestIndirectedFoo.expects(:supported_formats).returns ["marshal"] - format = stub 'format' - text = Marshal.dump(@model_instance) - @model_instance.expects(:render).with(Puppet::Network::FormatHandler.format("marshal")).returns text - Puppet::TestIndirectedFoo.find('bar') - end - end - - describe "when no matching model instance can be found" do - before :each do - @mock_model.stubs(:find).returns nil - end - - it "should return nil" do - Puppet::TestIndirectedFoo.find('bar').should be_nil - end - end - - describe "when an exception is encountered in looking up a model instance" do - before :each do - @mock_model.stubs(:find).raises(RuntimeError) - end - - it "should raise an exception" do - lambda { Puppet::TestIndirectedFoo.find('bar') }.should raise_error(Net::HTTPError) - end - end - end - - describe "when searching for model instances over REST" do - describe "when matching model instances can be found" do - before :each do - @model_instances = [ Puppet::TestIndirectedFoo.new(23), Puppet::TestIndirectedFoo.new(24) ] - - # Force yaml, because otherwise our mocks can't work correctly - Puppet::TestIndirectedFoo.stubs(:supported_formats).returns %w{yaml} - - @mock_model.stubs(:search).returns @model_instances - @mock_model.stubs(:render_multiple).returns @model_instances.to_yaml - end - - it "should not fail" do - lambda { Puppet::TestIndirectedFoo.search('bar') }.should_not raise_error - end - - it 'should return all matching results' do - Puppet::TestIndirectedFoo.search('bar').length.should == @model_instances.length - end - - it "should pass options all the way through" do - @mock_model.expects(:search).with { |key, args| args[:one] == "two" and args[:three] == "four" }.returns @model_instances - Puppet::TestIndirectedFoo.search('bar', :one => "two", :three => "four") - end - - it 'should return model instances' do - Puppet::TestIndirectedFoo.search('bar').each do |result| - result.class.should == Puppet::TestIndirectedFoo - end - end - - it 'should return the instance of the model class associated with the provided lookup key' do - Puppet::TestIndirectedFoo.search('bar').collect { |i| i.value }.should == @model_instances.collect { |i| i.value } - end - - it 'should set an expiration on model instances' do - Puppet::TestIndirectedFoo.search('bar').each do |result| - result.expiration.should_not be_nil - end - end - end - - describe "when no matching model instance can be found" do - before :each do - @mock_model.stubs(:search).returns nil - @mock_model.stubs(:render_multiple).returns nil.to_yaml - end - - it "should return nil" do - Puppet::TestIndirectedFoo.search('bar').should == [] - end - end - - describe "when an exception is encountered in looking up a model instance" do - before :each do - @mock_model.stubs(:find).raises(RuntimeError) - end - - it "should raise an exception" do - lambda { Puppet::TestIndirectedFoo.find('bar') }.should raise_error(Net::HTTPError) - end - end - end - - describe "when destroying a model instance over REST" do - describe "when a matching model instance can be found" do - before :each do - @mock_model.stubs(:destroy).returns true - end - - it "should not fail" do - lambda { Puppet::TestIndirectedFoo.destroy('bar') }.should_not raise_error - end - - it 'should return success' do - Puppet::TestIndirectedFoo.destroy('bar').should == true - end - end - - describe "when no matching model instance can be found" do - before :each do - @mock_model.stubs(:destroy).returns false - end - - it "should return failure" do - Puppet::TestIndirectedFoo.destroy('bar').should == false - end - end - - describe "when an exception is encountered in destroying a model instance" do - before :each do - @mock_model.stubs(:destroy).raises(RuntimeError) - end - - it "should raise an exception" do - lambda { Puppet::TestIndirectedFoo.destroy('bar') }.should raise_error(Net::HTTPError) - end - end - end - - describe "when saving a model instance over REST" do - before :each do - @instance = Puppet::TestIndirectedFoo.new(42) - @mock_model.stubs(:convert_from).returns @instance - - # LAK:NOTE This stub is necessary to prevent the REST call from calling - # REST.save again, thus producing painful infinite recursion. - Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:save_object).returns(@instance) - end - - describe "when a successful save can be performed" do - before :each do - end - - it "should not fail" do - lambda { @instance.save }.should_not raise_error - end - - it 'should return an instance of the model class' do - @instance.save.class.should == Puppet::TestIndirectedFoo - end - - it 'should return a matching instance of the model class' do - @instance.save.value.should == @instance.value - end - end - - describe "when a save cannot be completed" do - before :each do - Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:save_object).returns(false) - end - - it "should return failure" do - @instance.save.should == false - end - end - - describe "when an exception is encountered in performing a save" do - before :each do - Puppet::Network::HTTP::MongrelREST.any_instance.stubs(:save_object).raises(RuntimeError) - end - - it "should raise an exception" do - lambda { @instance.save }.should raise_error(Net::HTTPError) - end - end - end - end -end diff --git a/spec/unit/network/http/api/v1_spec.rb b/spec/unit/network/http/api/v1_spec.rb index 8d507046f..84b98ddaf 100644 --- a/spec/unit/network/http/api/v1_spec.rb +++ b/spec/unit/network/http/api/v1_spec.rb @@ -1,134 +1,135 @@ #!/usr/bin/env ruby Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") } require 'puppet/network/http/api/v1' class V1RestApiTester include Puppet::Network::HTTP::API::V1 end describe Puppet::Network::HTTP::API::V1 do before do @tester = V1RestApiTester.new end it "should be able to convert a URI into a request" do @tester.should respond_to(:uri2indirection) end it "should be able to convert a request into a URI" do @tester.should respond_to(:indirection2uri) end describe "when converting a URI into a request" do before do @tester.stubs(:handler).returns "foo" end it "should require the http method, the URI, and the query parameters" do # Not a terribly useful test, but an important statement for the spec lambda { @tester.uri2indirection("/foo") }.should raise_error(ArgumentError) end it "should use the first field of the URI as the environment" do - @tester.uri2indirection("GET", "/env/foo/bar", {}).environment.should == Puppet::Node::Environment.new("env") + @tester.uri2indirection("GET", "/env/foo/bar", {})[3][:environment].should == "env" end it "should fail if the environment is not alphanumeric" do lambda { @tester.uri2indirection("GET", "/env ness/foo/bar", {}) }.should raise_error(ArgumentError) end it "should use the environment from the URI even if one is specified in the parameters" do - @tester.uri2indirection("GET", "/env/foo/bar", {:environment => "otherenv"}).environment.should == Puppet::Node::Environment.new("env") + @tester.uri2indirection("GET", "/env/foo/bar", {:environment => "otherenv"})[3][:environment].should == "env" end it "should use the second field of the URI as the indirection name" do - @tester.uri2indirection("GET", "/env/foo/bar", {}).indirection_name.should == :foo + @tester.uri2indirection("GET", "/env/foo/bar", {})[0].should == "foo" end it "should fail if the indirection name is not alphanumeric" do lambda { @tester.uri2indirection("GET", "/env/foo ness/bar", {}) }.should raise_error(ArgumentError) end it "should use the remainder of the URI as the indirection key" do - @tester.uri2indirection("GET", "/env/foo/bar", {}).key.should == "bar" + @tester.uri2indirection("GET", "/env/foo/bar", {})[2].should == "bar" end it "should support the indirection key being a /-separated file path" do - @tester.uri2indirection("GET", "/env/foo/bee/baz/bomb", {}).key.should == "bee/baz/bomb" + @tester.uri2indirection("GET", "/env/foo/bee/baz/bomb", {})[2].should == "bee/baz/bomb" end it "should fail if no indirection key is specified" do lambda { @tester.uri2indirection("GET", "/env/foo/", {}) }.should raise_error(ArgumentError) lambda { @tester.uri2indirection("GET", "/env/foo", {}) }.should raise_error(ArgumentError) end it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is singular" do - @tester.uri2indirection("GET", "/env/foo/bar", {}).method.should == :find + @tester.uri2indirection("GET", "/env/foo/bar", {})[1].should == :find end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is plural" do - @tester.uri2indirection("GET", "/env/foos/bar", {}).method.should == :search + @tester.uri2indirection("GET", "/env/foos/bar", {})[1].should == :search end it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is facts" do - @tester.uri2indirection("GET", "/env/facts/bar", {}).method.should == :find + @tester.uri2indirection("GET", "/env/facts/bar", {})[1].should == :find end it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is facts" do - @tester.uri2indirection("PUT", "/env/facts/bar", {}).method.should == :save + @tester.uri2indirection("PUT", "/env/facts/bar", {})[1].should == :save end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is inventory" do - @tester.uri2indirection("GET", "/env/inventory/search", {}).method.should == :search + @tester.uri2indirection("GET", "/env/inventory/search", {})[1].should == :search end it "should choose 'delete' as the indirection method if the http method is a DELETE and the indirection name is singular" do - @tester.uri2indirection("DELETE", "/env/foo/bar", {}).method.should == :destroy + @tester.uri2indirection("DELETE", "/env/foo/bar", {})[1].should == :destroy end it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is singular" do - @tester.uri2indirection("PUT", "/env/foo/bar", {}).method.should == :save + @tester.uri2indirection("PUT", "/env/foo/bar", {})[1].should == :save end it "should fail if an indirection method cannot be picked" do lambda { @tester.uri2indirection("UPDATE", "/env/foo/bar", {}) }.should raise_error(ArgumentError) end it "should URI unescape the indirection key" do escaped = URI.escape("foo bar") - @tester.uri2indirection("GET", "/env/foo/#{escaped}", {}).key.should == "foo bar" + indirection_name, method, key, params = @tester.uri2indirection("GET", "/env/foo/#{escaped}", {}) + key.should == "foo bar" end end describe "when converting a request into a URI" do before do @request = Puppet::Indirector::Request.new(:foo, :find, "with spaces", :foo => :bar, :environment => "myenv") end it "should use the environment as the first field of the URI" do @tester.indirection2uri(@request).split("/")[1].should == "myenv" end it "should use the indirection as the second field of the URI" do @tester.indirection2uri(@request).split("/")[2].should == "foo" end it "should pluralize the indirection name if the method is 'search'" do @request.stubs(:method).returns :search @tester.indirection2uri(@request).split("/")[2].should == "foos" end it "should use the escaped key as the remainder of the URI" do escaped = URI.escape("with spaces") @tester.indirection2uri(@request).split("/")[3].sub(/\?.+/, '').should == escaped end it "should add the query string to the URI" do @request.expects(:query_string).returns "?query" @tester.indirection2uri(@request).should =~ /\?query$/ end end end diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb index 355f500e6..16f1b5349 100755 --- a/spec/unit/network/http/handler_spec.rb +++ b/spec/unit/network/http/handler_spec.rb @@ -1,458 +1,430 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/network/http/handler' require 'puppet/network/rest_authorization' class HttpHandled include Puppet::Network::HTTP::Handler end describe Puppet::Network::HTTP::Handler do before do @handler = HttpHandled.new end it "should include the v1 REST API" 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 describe "when initializing" do it "should fail when no server type has been provided" do lambda { @handler.initialize_for_puppet }.should raise_error(ArgumentError) end it "should set server type" do @handler.initialize_for_puppet("foo") @handler.server.should == "foo" end end it "should be able to process requests" do @handler.should respond_to(:process) end describe "when processing a request" do before do @request = stub('http request') @request.stubs(:[]).returns "foo" @response = stub('http response') @model_class = stub('indirected model class') @result = stub 'result', :render => "mytext" @handler.stubs(:check_authorization) stub_server_interface end # Stub out the interface we require our including classes to # implement. def stub_server_interface @handler.stubs(:accept_header ).returns "format_one,format_two" @handler.stubs(:content_type_header).returns "text/yaml" @handler.stubs(:set_content_type ).returns "my_result" @handler.stubs(:set_response ).returns "my_result" @handler.stubs(:path ).returns "/my_handler/my_result" @handler.stubs(:http_method ).returns("GET") @handler.stubs(:params ).returns({}) @handler.stubs(:content_type ).returns("text/plain") end it "should create an indirection request from the path, parameters, and http method" do @handler.expects(:path).with(@request).returns "mypath" @handler.expects(:http_method).with(@request).returns "mymethod" @handler.expects(:params).with(@request).returns "myparams" @handler.expects(:uri2indirection).with("mymethod", "mypath", "myparams").returns stub("request", :method => :find) @handler.stubs(:do_find) @handler.process(@request, @response) end - it "should call the 'do' method associated with the indirection method" do - request = stub 'request' - @handler.expects(:uri2indirection).returns request + it "should call the 'do' method and delegate authorization to the RestAuthorization layer" do + @handler.expects(:uri2indirection).returns(["facts", :mymethod, "key", {:node => "name"}]) - request.expects(:method).returns "mymethod" + @handler.expects(:do_mymethod).with("facts", "key", {:node => "name"}, @request, @response) - @handler.expects(:do_mymethod).with(request, @request, @response) - - @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(:check_authorization).with(request) + @handler.expects(:check_authorization).with("facts", :mymethod, "key", {:node => "name"}) @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(:uri2indirection).returns(["facts", :mymethod, "key", {:node => "name"}]) @handler.expects(:do_mymethod).never - @handler.expects(:check_authorization).with(request).raises(Puppet::Network::AuthorizationError.new("forbindden")) + @handler.expects(:check_authorization).with("facts", :mymethod, "key", {:node => "name"}).raises(Puppet::Network::AuthorizationError.new("forbidden")) @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) + @handler.expects(:uri2indirection).returns(["facts", :find, "key", {:node => "name"}]) @handler.expects(:do_find).raises(ArgumentError, "The exception") @handler.expects(:set_response).with { |response, body, status| body == "The exception" and status == 400 } @handler.process(@request, @response) end it "should set the format to text/plain when serializing an exception" do @handler.expects(:set_content_type).with(@response, "text/plain") @handler.do_exception(@response, "A test", 404) end it "should raise an error if the request is formatted in an unknown format" do @handler.stubs(:content_type_header).returns "unknown format" lambda { @handler.request_format(@request) }.should raise_error end it "should still find the correct format if content type contains charset information" do @handler.stubs(:content_type_header).returns "text/plain; charset=UTF-8" @handler.request_format(@request).should == "s" end describe "when finding a model instance" do before do - @irequest = stub 'indirection_request', :method => :find, :indirection_name => "my_handler", :to_hash => {}, :key => "my_result", :model => @model_class - @model_class.stubs(:find).returns @result + Puppet::Indirector::Indirection.expects(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @format = stub 'format', :suitable? => true, :mime => "text/format", :name => "format" Puppet::Network::FormatHandler.stubs(:format).returns @format @oneformat = stub 'one', :suitable? => true, :mime => "text/one", :name => "one" Puppet::Network::FormatHandler.stubs(:format).with("one").returns @oneformat end it "should use the indirection request to find the model class" do - @irequest.expects(:model).returns @model_class - - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should use the escaped request key" do @model_class.expects(:find).with do |key, args| key == "my_result" end.returns @result - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should use a common method for determining the request parameters" do - @irequest.stubs(:to_hash).returns(:foo => :baz, :bar => :xyzzy) @model_class.expects(:find).with do |key, args| args[:foo] == :baz and args[:bar] == :xyzzy end.returns @result - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {:foo => :baz, :bar => :xyzzy}, @request, @response) end it "should set the content type to the first format specified in the accept header" do @handler.expects(:accept_header).with(@request).returns "one,two" @handler.expects(:set_content_type).with(@response, @oneformat) - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should fail if no accept header is provided" do @handler.expects(:accept_header).with(@request).returns nil - lambda { @handler.do_find(@irequest, @request, @response) }.should raise_error(ArgumentError) + lambda { @handler.do_find("my_handler", "my_result", {}, @request, @response) }.should raise_error(ArgumentError) end it "should fail if the accept header does not contain a valid format" do @handler.expects(:accept_header).with(@request).returns "" - lambda { @handler.do_find(@irequest, @request, @response) }.should raise_error(RuntimeError) + lambda { @handler.do_find("my_handler", "my_result", {}, @request, @response) }.should raise_error(RuntimeError) end it "should not use an unsuitable format" do @handler.expects(:accept_header).with(@request).returns "foo,bar" foo = mock 'foo', :suitable? => false bar = mock 'bar', :suitable? => true Puppet::Network::FormatHandler.expects(:format).with("foo").returns foo Puppet::Network::FormatHandler.expects(:format).with("bar").returns bar @handler.expects(:set_content_type).with(@response, bar) # the suitable one - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should render the result using the first format specified in the accept header" do @handler.expects(:accept_header).with(@request).returns "one,two" @result.expects(:render).with(@oneformat) - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should use the default status when a model find call succeeds" do @handler.expects(:set_response).with { |response, body, status| status.nil? } - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should return a serialized object when a model find call succeeds" do @model_instance = stub('model instance') @model_instance.expects(:render).returns "my_rendered_object" @handler.expects(:set_response).with { |response, body, status| body == "my_rendered_object" } @model_class.stubs(:find).returns(@model_instance) - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should return a 404 when no model instance can be found" do @model_class.stubs(:name).returns "my name" @handler.expects(:set_response).with { |response, body, status| status == 404 } @model_class.stubs(:find).returns(nil) - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should write a log message when no model instance can be found" do @model_class.stubs(:name).returns "my name" @model_class.stubs(:find).returns(nil) Puppet.expects(:info).with("Could not find my_handler for 'my_result'") - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should serialize the result in with the appropriate format" do @model_instance = stub('model instance') @handler.expects(:format_to_use).returns(@oneformat) @model_instance.expects(:render).with(@oneformat).returns "my_rendered_object" @model_class.stubs(:find).returns(@model_instance) - @handler.do_find(@irequest, @request, @response) + @handler.do_find("my_handler", "my_result", {}, @request, @response) end end describe "when searching for model instances" do before do - @irequest = stub 'indirection_request', :method => :find, :indirection_name => "my_handler", :to_hash => {}, :key => "key", :model => @model_class + Puppet::Indirector::Indirection.expects(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @result1 = mock 'result1' @result2 = mock 'results' @result = [@result1, @result2] @model_class.stubs(:render_multiple).returns "my rendered instances" @model_class.stubs(:search).returns(@result) @format = stub 'format', :suitable? => true, :mime => "text/format", :name => "format" Puppet::Network::FormatHandler.stubs(:format).returns @format @oneformat = stub 'one', :suitable? => true, :mime => "text/one", :name => "one" Puppet::Network::FormatHandler.stubs(:format).with("one").returns @oneformat end it "should use the indirection request to find the model" do - @irequest.expects(:model).returns @model_class - - @handler.do_search(@irequest, @request, @response) + @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should use a common method for determining the request parameters" do - @irequest.stubs(:to_hash).returns(:foo => :baz, :bar => :xyzzy) @model_class.expects(:search).with do |key, args| args[:foo] == :baz and args[:bar] == :xyzzy end.returns @result - @handler.do_search(@irequest, @request, @response) + @handler.do_search("my_handler", "my_result", {:foo => :baz, :bar => :xyzzy}, @request, @response) end it "should use the default status when a model search call succeeds" do @model_class.stubs(:search).returns(@result) - @handler.do_search(@irequest, @request, @response) + @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should set the content type to the first format returned by the accept header" do @handler.expects(:accept_header).with(@request).returns "one,two" @handler.expects(:set_content_type).with(@response, @oneformat) - @handler.do_search(@irequest, @request, @response) + @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should return a list of serialized objects when a model search call succeeds" do @handler.expects(:accept_header).with(@request).returns "one,two" @model_class.stubs(:search).returns(@result) @model_class.expects(:render_multiple).with(@oneformat, @result).returns "my rendered instances" @handler.expects(:set_response).with { |response, data| data == "my rendered instances" } - @handler.do_search(@irequest, @request, @response) + @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should return [] when searching returns an empty array" do @handler.expects(:accept_header).with(@request).returns "one,two" @model_class.stubs(:search).returns([]) @model_class.expects(:render_multiple).with(@oneformat, []).returns "[]" @handler.expects(:set_response).with { |response, data| data == "[]" } - @handler.do_search(@irequest, @request, @response) + @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should return a 404 when searching returns nil" do @model_class.stubs(:name).returns "my name" @handler.expects(:set_response).with { |response, body, status| status == 404 } @model_class.stubs(:search).returns(nil) - @handler.do_search(@irequest, @request, @response) + @handler.do_search("my_handler", "my_result", {}, @request, @response) end end describe "when destroying a model instance" do before do - @irequest = stub 'indirection_request', :method => :destroy, :indirection_name => "my_handler", :to_hash => {}, :key => "key", :model => @model_class + Puppet::Indirector::Indirection.expects(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @result = stub 'result', :render => "the result" @model_class.stubs(:destroy).returns @result end it "should use the indirection request to find the model" do - @irequest.expects(:model).returns @model_class - - @handler.do_destroy(@irequest, @request, @response) + @handler.do_destroy("my_handler", "my_result", {}, @request, @response) end it "should use the escaped request key to destroy the instance in the model" do - @irequest.expects(:key).returns "foo bar" @model_class.expects(:destroy).with do |key, args| key == "foo bar" end - @handler.do_destroy(@irequest, @request, @response) + @handler.do_destroy("my_handler", "foo bar", {}, @request, @response) end it "should use a common method for determining the request parameters" do - @irequest.stubs(:to_hash).returns(:foo => :baz, :bar => :xyzzy) @model_class.expects(:destroy).with do |key, args| args[:foo] == :baz and args[:bar] == :xyzzy end - @handler.do_destroy(@irequest, @request, @response) + @handler.do_destroy("my_handler", "my_result", {:foo => :baz, :bar => :xyzzy}, @request, @response) end it "should use the default status code a model destroy call succeeds" do @handler.expects(:set_response).with { |response, body, status| status.nil? } - @handler.do_destroy(@irequest, @request, @response) + @handler.do_destroy("my_handler", "my_result", {}, @request, @response) end it "should return a yaml-encoded result when a model destroy call succeeds" do @result = stub 'result', :to_yaml => "the result" @model_class.expects(:destroy).returns(@result) @handler.expects(:set_response).with { |response, body, status| body == "the result" } - @handler.do_destroy(@irequest, @request, @response) + @handler.do_destroy("my_handler", "my_result", {}, @request, @response) end end describe "when saving a model instance" do before do - @irequest = stub 'indirection_request', :method => :save, :indirection_name => "my_handler", :to_hash => {}, :key => "key", :model => @model_class + Puppet::Indirector::Indirection.stubs(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @handler.stubs(:body).returns('my stuff') @handler.stubs(:content_type_header).returns("text/yaml") @result = stub 'result', :render => "the result" @model_instance = stub('indirected model instance', :save => true) @model_class.stubs(:convert_from).returns(@model_instance) @format = stub 'format', :suitable? => true, :name => "format", :mime => "text/format" Puppet::Network::FormatHandler.stubs(:format).returns @format @yamlformat = stub 'yaml', :suitable? => true, :name => "yaml", :mime => "text/yaml" Puppet::Network::FormatHandler.stubs(:format).with("yaml").returns @yamlformat end it "should use the indirection request to find the model" do - @irequest.expects(:model).returns @model_class - - @handler.do_save(@irequest, @request, @response) + @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should use the 'body' hook to retrieve the body of the request" do @handler.expects(:body).returns "my body" @model_class.expects(:convert_from).with { |format, body| body == "my body" }.returns @model_instance - @handler.do_save(@irequest, @request, @response) + @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should fail to save model if data is not specified" do @handler.stubs(:body).returns('') - lambda { @handler.do_save(@irequest, @request, @response) }.should raise_error(ArgumentError) + lambda { @handler.do_save("my_handler", "my_result", {}, @request, @response) }.should raise_error(ArgumentError) end it "should use a common method for determining the request parameters" do @model_instance.expects(:save).with('key').once - @handler.do_save(@irequest, @request, @response) + @handler.do_save("my_handler", "key", {}, @request, @response) end it "should use the default status when a model save call succeeds" do @handler.expects(:set_response).with { |response, body, status| status.nil? } - @handler.do_save(@irequest, @request, @response) + @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should return the yaml-serialized result when a model save call succeeds" do @model_instance.stubs(:save).returns(@model_instance) @model_instance.expects(:to_yaml).returns('foo') - @handler.do_save(@irequest, @request, @response) + @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should set the content to yaml" do @handler.expects(:set_content_type).with(@response, @yamlformat) - @handler.do_save(@irequest, @request, @response) + @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should use the content-type header to know the body format" do @handler.expects(:content_type_header).returns("text/format") Puppet::Network::FormatHandler.stubs(:mime).with("text/format").returns @format @model_class.expects(:convert_from).with { |format, body| format == "format" }.returns @model_instance - @handler.do_save(@irequest, @request, @response) + @handler.do_save("my_handler", "my_result", {}, @request, @response) end end end describe "when resolving node" do it "should use a look-up from the ip address" do Resolv.expects(:getname).with("1.2.3.4").returns("host.domain.com") @handler.resolve_node(:ip => "1.2.3.4") end it "should return the look-up result" do Resolv.stubs(:getname).with("1.2.3.4").returns("host.domain.com") @handler.resolve_node(:ip => "1.2.3.4").should == "host.domain.com" end it "should return the ip address if resolving fails" do Resolv.stubs(:getname).with("1.2.3.4").raises(RuntimeError, "no such host") @handler.resolve_node(:ip => "1.2.3.4").should == "1.2.3.4" end end end diff --git a/spec/unit/network/rest_authconfig_spec.rb b/spec/unit/network/rest_authconfig_spec.rb index 351f3f040..e81eb41ed 100755 --- a/spec/unit/network/rest_authconfig_spec.rb +++ b/spec/unit/network/rest_authconfig_spec.rb @@ -1,148 +1,139 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/network/rest_authconfig' describe Puppet::Network::RestAuthConfig do DEFAULT_ACL = [ { :acl => "~ ^\/catalog\/([^\/]+)$", :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 }, { :acl => "/resource", :method => [:find, :save, :search], :authenticated => true }, ] 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, :environment => :env, :authenticated => true 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(:fail_on_deny).with("/path/to/resource", :node => "me", :ip => "127.0.0.1", :method => :save, :environment => :env, :authenticated => true) - @authconfig.allowed?(@request) + @authconfig.allowed?("path", :save, "to/resource", :ip => "127.0.0.1", :node => "me", :environment => :env, :authenticated => true) 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 diff --git a/spec/unit/network/rest_authorization_spec.rb b/spec/unit/network/rest_authorization_spec.rb deleted file mode 100755 index 0cb0bcee9..000000000 --- a/spec/unit/network/rest_authorization_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -#!/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") - @request.stubs(:ip).returns("ip") - end - - describe "when testing request authorization" do - it "should delegate to the current rest authconfig" do - @authconfig.expects(:allowed?).with(@request).returns(true) - - @auth.check_authorization(@request) - end - - it "should raise an AuthorizationError if authconfig raises an AuthorizationError" do - @authconfig.expects(:allowed?).with(@request).raises(Puppet::Network::AuthorizationError.new("forbidden")) - - lambda { @auth.check_authorization(@request) }.should raise_error(Puppet::Network::AuthorizationError) - end - - it "should not raise an AuthorizationError if request is allowed" do - @authconfig.expects(:allowed?).with(@request).returns(true) - - lambda { @auth.check_authorization(@request) }.should_not raise_error(Puppet::Network::AuthorizationError) - end - end -end