diff options
| author | Paul Berry <paul@puppetlabs.com> | 2011-01-10 17:05:38 -0800 |
|---|---|---|
| committer | Paul Berry <paul@puppetlabs.com> | 2011-01-12 16:29:06 -0800 |
| commit | c514c641d0c0090be29252dcc773385248d3fe93 (patch) | |
| tree | b73294a3764f4dc0743971aee1fcc574d5d85f02 /lib | |
| parent | 2b9b7a5f7fe4b673f0d1fba9fb523cc0e2e34fa5 (diff) | |
| download | puppet-c514c641d0c0090be29252dcc773385248d3fe93.tar.gz puppet-c514c641d0c0090be29252dcc773385248d3fe93.tar.xz puppet-c514c641d0c0090be29252dcc773385248d3fe93.zip | |
(#5838) Added support for HEAD requests to the indirector.
Added the ability for the indirector to handle REST HEAD requests.
These are done using a new indirector method, head(), which should
return true if find() would return a result and false if find() would
return nil.
Access control for the head method is the union of that for the find
and save methods. That is, if either find or save is allowed, then
head is allowed. This is necessary so that users will not have to
change their authconfig to take advantage of the new feature.
Paired-with: Jesse Wolfe <jesse@puppetlabs.com>
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/puppet/indirector.rb | 4 | ||||
| -rw-r--r-- | lib/puppet/indirector/indirection.rb | 11 | ||||
| -rw-r--r-- | lib/puppet/indirector/rest.rb | 21 | ||||
| -rw-r--r-- | lib/puppet/network/http/api/v1.rb | 3 | ||||
| -rw-r--r-- | lib/puppet/network/http/handler.rb | 11 | ||||
| -rw-r--r-- | lib/puppet/network/rest_authconfig.rb | 12 | ||||
| -rwxr-xr-x | lib/puppet/network/rights.rb | 24 |
7 files changed, 73 insertions, 13 deletions
diff --git a/lib/puppet/indirector.rb b/lib/puppet/indirector.rb index 5b737578b..e6472f4d9 100644 --- a/lib/puppet/indirector.rb +++ b/lib/puppet/indirector.rb @@ -50,6 +50,10 @@ module Puppet::Indirector indirection.find(*args) end + def head(*args) + indirection.head(*args) + end + def destroy(*args) indirection.destroy(*args) end diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index 4341f7cb2..ec147ec69 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -198,6 +198,17 @@ class Puppet::Indirector::Indirection nil end + # Search for an instance in the appropriate terminus, and return a + # boolean indicating whether the instance was found. + def head(key, *args) + request = request(:head, key, *args) + terminus = prepare(request) + + # Look in the cache first, then in the terminus. Force the result + # to be a boolean. + !!(find_in_cache(request) || terminus.head(request)) + end + def find_in_cache(request) # See if our instance is in the cache and up to date. return nil unless cache? and ! request.ignore_cache? and cached = cache.find(request) diff --git a/lib/puppet/indirector/rest.rb b/lib/puppet/indirector/rest.rb index eb41ff3b1..e50dc68ae 100644 --- a/lib/puppet/indirector/rest.rb +++ b/lib/puppet/indirector/rest.rb @@ -53,11 +53,15 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus end else # Raise the http error if we didn't get a 'success' of some kind. - message = "Error #{response.code} on SERVER: #{(response.body||'').empty? ? response.message : uncompress_body(response)}" - raise Net::HTTPError.new(message, response) + raise convert_to_http_error(response) end end + def convert_to_http_error(response) + message = "Error #{response.code} on SERVER: #{(response.body||'').empty? ? response.message : uncompress_body(response)}" + Net::HTTPError.new(message, response) + end + # Provide appropriate headers. def headers add_accept_encoding({"Accept" => model.supported_formats.join(", ")}) @@ -73,6 +77,19 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus result end + def head(request) + response = network(request).head(indirection2uri(request), headers) + case response.code + when "404" + return false + when /^2/ + return true + else + # Raise the http error if we didn't get a 'success' of some kind. + raise convert_to_http_error(response) + end + end + def search(request) unless result = deserialize(network(request).get(indirection2uri(request), headers), true) return [] diff --git a/lib/puppet/network/http/api/v1.rb b/lib/puppet/network/http/api/v1.rb index dd4612a14..8aa1f0ee1 100644 --- a/lib/puppet/network/http/api/v1.rb +++ b/lib/puppet/network/http/api/v1.rb @@ -13,6 +13,9 @@ module Puppet::Network::HTTP::API::V1 }, "DELETE" => { :singular => :destroy + }, + "HEAD" => { + :singular => :head } } diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index f22498b70..9e9356b2f 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -116,6 +116,17 @@ module Puppet::Network::HTTP::Handler end end + # Execute our head. + def do_head(indirection_request, request, response) + unless indirection_request.model.head(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) + end + + # No need to set a response because no response is expected from a + # HEAD request. All we need to do is not die. + end + # Execute our search. def do_search(indirection_request, request, response) result = indirection_request.model.search(indirection_request.key, indirection_request.to_hash) diff --git a/lib/puppet/network/rest_authconfig.rb b/lib/puppet/network/rest_authconfig.rb index 1704ea0c1..7a6147a82 100644 --- a/lib/puppet/network/rest_authconfig.rb +++ b/lib/puppet/network/rest_authconfig.rb @@ -38,13 +38,7 @@ module Puppet # fail_on_deny could as well be called in the XMLRPC context # with a ClientRequest. - if authorization_failure_exception = @rights.is_forbidden_and_why?( - build_uri(request), - :node => request.node, - :ip => request.ip, - :method => request.method, - :environment => request.environment, - :authenticated => request.authenticated) + if authorization_failure_exception = @rights.is_request_forbidden_and_why?(request) Puppet.warning("Denying access: #{authorization_failure_exception}") raise authorization_failure_exception end @@ -90,9 +84,5 @@ module Puppet end @rights.restrict_authenticated(acl[:acl], acl[:authenticated]) unless acl[:authenticated].nil? end - - def build_uri(request) - "/#{request.indirection_name}/#{request.key}" - end end end diff --git a/lib/puppet/network/rights.rb b/lib/puppet/network/rights.rb index b2146494c..00ee04f8d 100755 --- a/lib/puppet/network/rights.rb +++ b/lib/puppet/network/rights.rb @@ -29,6 +29,30 @@ class Rights !is_forbidden_and_why?(name, :node => args[0], :ip => args[1]) end + def is_request_forbidden_and_why?(request) + methods_to_check = if request.method == :head + # :head is ok if either :find or :save is ok. + [:find, :save] + else + [request.method] + end + authorization_failure_exceptions = methods_to_check.map do |method| + is_forbidden_and_why?("/#{request.indirection_name}/#{request.key}", + :node => request.node, + :ip => request.ip, + :method => method, + :environment => request.environment, + :authenticated => request.authenticated) + end + if authorization_failure_exceptions.include? nil + # One of the methods we checked is ok, therefore this request is ok. + nil + else + # Just need to return any of the failure exceptions. + authorization_failure_exceptions.first + end + end + def is_forbidden_and_why?(name, args = {}) res = :nomatch right = @rights.find do |acl| |
