summaryrefslogtreecommitdiffstats
path: root/lib/puppet/network/xmlrpc/webrick_servlet.rb
blob: 890f2990eebec76d23f4f949abc24964a0dff91e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
require 'xmlrpc/server'
require 'puppet/network/authorization'
require 'puppet/network/xmlrpc/processor'

module Puppet::Network::XMLRPC
    class ServletError < RuntimeError; end
    class WEBrickServlet < ::XMLRPC::WEBrickServlet
        include Puppet::Network::XMLRPCProcessor

        # This is a hackish way to avoid an auth message every time we have a
        # normal operation
        def self.log(msg)
            @logs ||= {}
            if @logs.include?(msg)
                @logs[msg] += 1
            else
                Puppet.info msg
                @logs[msg] = 1
            end
        end

        # Accept a list of handlers and register them all.
        def initialize(handlers)
            # the servlet base class does not consume any arguments
            # and its BasicServer base class only accepts a 'class_delim'
            # option which won't change in Puppet at all
            # thus, we don't need to pass any args to our base class,
            # and we can consume them all ourselves
            super()

            setup_processor()

            # Set up each of the passed handlers.
            handlers.each do |handler|
                add_handler(handler.class.interface, handler)
            end
        end

        # Handle the actual request.  We can't use the super() method, because
        # we need to pass a ClientRequest object to process() so we can do
        # authorization.  It's the only way to stay thread-safe.
        def service(request, response)
            if @valid_ip
                raise WEBrick::HTTPStatus::Forbidden unless @valid_ip.any? { |ip| request.peeraddr[3] =~ ip }
            end

            if request.request_method != "POST"
                raise WEBrick::HTTPStatus::MethodNotAllowed,
                    "unsupported method `#{request.request_method}'."
            end

            raise WEBrick::HTTPStatus::BadRequest if parse_content_type(request['Content-type']).first != "text/xml"

            length = (request['Content-length'] || 0).to_i

            raise WEBrick::HTTPStatus::LengthRequired unless length > 0

            data = request.body

            raise WEBrick::HTTPStatus::BadRequest if data.nil? or data.size != length

            resp = process(data, client_request(request))
            raise WEBrick::HTTPStatus::InternalServerError if resp.nil? or resp.size <= 0

            response.status = 200
            response['Content-Length'] = resp.size
            response['Content-Type']   = "text/xml; charset=utf-8"
            response.body = resp
        end

        private

        # Generate a ClientRequest object for later validation.
        def client_request(request)
            if peer = request.peeraddr
                client = peer[2]
                clientip = peer[3]
            else

                            raise ::XMLRPC::FaultException.new(
                
                    ERR_UNCAUGHT_EXCEPTION,
        
                    "Could not retrieve client information"
                )
            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
            valid = false
            if cert = request.client_cert
                nameary = cert.subject.to_a.find { |ary|
                    ary[0] == "CN"
                }

                if nameary.nil?
                    Puppet.warning "Could not retrieve server name from cert"
                else
                    unless client == nameary[1]
                        Puppet.debug "Overriding #{client} with cert name #{nameary[1]}"
                        client = nameary[1]
                    end
                    valid = true
                end
            end

            info = Puppet::Network::ClientRequest.new(client, clientip, valid)

            info
        end
    end
end