diff options
author | Luke Kanies <luke@madstop.com> | 2008-05-27 10:36:16 -0600 |
---|---|---|
committer | Luke Kanies <luke@madstop.com> | 2008-06-09 16:39:26 -0500 |
commit | 1205881c8f022cd0dd26ed896976f9451549c571 (patch) | |
tree | 7a9bd74817886cf946ac48acffdb48f4acace00c /lib | |
parent | e8044f93efd29fab87d67f55461df371dec8bdff (diff) | |
download | puppet-1205881c8f022cd0dd26ed896976f9451549c571.tar.gz puppet-1205881c8f022cd0dd26ed896976f9451549c571.tar.xz puppet-1205881c8f022cd0dd26ed896976f9451549c571.zip |
The mongrel and webrick REST handlers now extract certificate information.
All requests should now have an ipaddress add to them, they
should always be marked authenticated or not, and they should
have the certificate name set as their 'node' if a certificate
is present.
They both use the same methods they use for xmlrpc,
although there's no common code, to facilitate deprecation
of xmlrpc.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/puppet/network/http/handler.rb | 56 | ||||
-rw-r--r-- | lib/puppet/network/http/mongrel/rest.rb | 49 | ||||
-rw-r--r-- | lib/puppet/network/http/webrick/rest.rb | 51 |
3 files changed, 96 insertions, 60 deletions
diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb index 7113c92d3..8c6abde8a 100644 --- a/lib/puppet/network/http/handler.rb +++ b/lib/puppet/network/http/handler.rb @@ -1,30 +1,30 @@ module Puppet::Network::HTTP::Handler - + def initialize_for_puppet(args = {}) raise ArgumentError unless @server = args[:server] raise ArgumentError unless @handler = args[:handler] @model = find_model_for_handler(@handler) end - + # handle an HTTP request def process(request, response) return do_find(request, response) if get?(request) and singular?(request) return do_search(request, response) if get?(request) and plural?(request) return do_destroy(request, response) if delete?(request) and singular?(request) - return do_save(request, response) if put?(request) and singular?(request) + return do_save(request, response) if put?(request) and singular?(request) raise ArgumentError, "Did not understand HTTP #{http_method(request)} request for '#{path(request)}'" rescue Exception => e return do_exception(request, response, e) end - + private def model - @model + @model end - + def do_find(request, response) - key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path}]") + key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path(request)}]") args = params(request) result = model.find(key, args).to_yaml encode_result(request, response, result) @@ -37,7 +37,7 @@ module Puppet::Network::HTTP::Handler end def do_destroy(request, response) - key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path}]") + key = request_key(request) || raise(ArgumentError, "Could not locate lookup key in request path [#{path(request)}]") args = params(request) result = model.destroy(key, args) encode_result(request, response, YAML.dump(result)) @@ -46,71 +46,67 @@ module Puppet::Network::HTTP::Handler def do_save(request, response) data = body(request).to_s raise ArgumentError, "No data to save" if !data or data.empty? - # args = params(request) + args = params(request) obj = model.from_yaml(data) - result = save_object(obj).to_yaml + result = obj.save(args).to_yaml encode_result(request, response, result) end - - def save_object(obj) - obj.save - end - + def do_exception(request, response, exception, status=404) encode_result(request, response, exception.to_yaml, status) end - + def find_model_for_handler(handler) Puppet::Indirector::Indirection.model(handler) || raise(ArgumentError, "Cannot locate indirection [#{handler}].") 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 - + def singular?(request) %r{/#{@handler.to_s}$}.match(path(request)) end - + def plural?(request) %r{/#{@handler.to_s}s$}.match(path(request)) end - - # methods to be overridden by the including web server class - + + # methods to be overridden by the including web server class + def register_handler raise NotImplementedError end - + 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 encode_result(request, response, result, status = 200) raise NotImplementedError end diff --git a/lib/puppet/network/http/mongrel/rest.rb b/lib/puppet/network/http/mongrel/rest.rb index 2a3d4f143..d1257d5cb 100644 --- a/lib/puppet/network/http/mongrel/rest.rb +++ b/lib/puppet/network/http/mongrel/rest.rb @@ -3,45 +3,64 @@ require 'puppet/network/http/handler' class Puppet::Network::HTTP::MongrelREST < Mongrel::HttpHandler include Puppet::Network::HTTP::Handler - - def initialize(args={}) - super() - initialize_for_puppet(args) - end + + def initialize(args={}) + super() + initialize_for_puppet(args) + end + + # Return the query params for this request. We had to expose this method for + # testing purposes. + def params(request) + Mongrel::HttpRequest.query_parse(request.params["QUERY_STRING"]).merge(client_info(request)) + end private - + # which HTTP verb was used in this request def http_method(request) request.params[Mongrel::Const::REQUEST_METHOD] end - + # what path was requested? def path(request) # LAK:NOTE See http://snurl.com/21zf8 [groups_google_com] x = '/' + request.params[Mongrel::Const::REQUEST_PATH].split('/')[1] end - + # return the key included in the request path def request_key(request) # LAK:NOTE See http://snurl.com/21zf8 [groups_google_com] x = request.params[Mongrel::Const::REQUEST_PATH].split('/')[2] end - + # return the request body def body(request) request.body end - - # return the query params for this request - def params(request) - Mongrel::HttpRequest.query_parse(request.params["QUERY_STRING"]) - end - + # produce the body of the response def encode_result(request, response, result, status = 200) response.start(status) do |head, body| body.write(result) end end + + def client_info(request) + result = {} + params = request.params + result[:ip] = params["REMOTE_ADDR"] + + # JJM #906 The following dn.match regular expression is forgiving + # enough to match the two Distinguished Name string contents + # coming from Apache, Pound or other reverse SSL proxies. + if dn = params[Puppet[:ssl_client_header]] and dn_matchdata = dn.match(/^.*?CN\s*=\s*(.*)/) + result[:node] = dn_matchdata[1].to_str + result[:authenticated] = (params[Puppet[:ssl_client_verify_header]] == 'SUCCESS') + else + result[:authenticated] = false + end + + return result + end end diff --git a/lib/puppet/network/http/webrick/rest.rb b/lib/puppet/network/http/webrick/rest.rb index b43912196..a235fb4f3 100644 --- a/lib/puppet/network/http/webrick/rest.rb +++ b/lib/puppet/network/http/webrick/rest.rb @@ -1,46 +1,67 @@ require 'puppet/network/http/handler' class Puppet::Network::HTTP::WEBrickREST < WEBrick::HTTPServlet::AbstractServlet - + include Puppet::Network::HTTP::Handler - + def initialize(server, handler) - raise ArgumentError, "server is required" unless server - super(server) - initialize_for_puppet(:server => server, :handler => handler) + raise ArgumentError, "server is required" unless server + super(server) + initialize_for_puppet(:server => server, :handler => handler) + end + + # We had to expose this method for testing purposes. + def params(request) + result = request.query + result.merge(client_information(request)) end # WEBrick uses a service() method to respond to requests. Simply delegate to the handler response() method. def service(request, response) process(request, response) end - + private - + def http_method(request) request.request_method end - + def path(request) # LAK:NOTE See http://snurl.com/21zf8 [groups_google_com] x = '/' + request.path.split('/')[1] end - + def request_key(request) # LAK:NOTE See http://snurl.com/21zf8 [groups_google_com] x = request.path.split('/')[2] end - + def body(request) request.body end - - def params(request) - request.query - end - + def encode_result(request, response, result, status = 200) response.status = status response.body = result end + + # Retrieve node/cert/ip information from the request object. + def client_information(request) + result = {} + if peer = request.peeraddr and ip = peer[3] + result[:ip] = ip + end + + # If they have a certificate (which will almost always be true) + # then we get the hostname from the cert, instead of via IP + # info + result[:authenticated] = false + if cert = request.client_cert and nameary = cert.subject.to_a.find { |ary| ary[0] == "CN" } + result[:node] = nameary[1] + result[:authenticated] = true + end + + result + end end |