summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2008-05-27 10:36:16 -0600
committerLuke Kanies <luke@madstop.com>2008-06-09 16:39:26 -0500
commit1205881c8f022cd0dd26ed896976f9451549c571 (patch)
tree7a9bd74817886cf946ac48acffdb48f4acace00c /lib
parente8044f93efd29fab87d67f55461df371dec8bdff (diff)
downloadpuppet-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.rb56
-rw-r--r--lib/puppet/network/http/mongrel/rest.rb49
-rw-r--r--lib/puppet/network/http/webrick/rest.rb51
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