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
115
116
117
118
119
120
121
122
123
124
125
126
|
""" the core of the CherryPy-powered server """
import sys
import time
import atexit
import cherrypy
import Bcfg2.Options
from Bcfg2.Compat import urlparse, xmlrpclib, b64decode
from Bcfg2.Server.Core import BaseCore
from cherrypy.lib import xmlrpcutil
from cherrypy._cptools import ErrorTool
if cherrypy.engine.state == 0:
cherrypy.engine.start(blocking=False)
atexit.register(cherrypy.engine.stop)
# define our own error handler that handles xmlrpclib.Fault objects
# and so allows for the possibility of returning proper error
# codes. this obviates the need to use the builtin CherryPy xmlrpc
# tool
def on_error(*args, **kwargs):
err = sys.exc_info()[1]
if not isinstance(err, xmlrpclib.Fault):
err = xmlrpclib.Fault(xmlrpclib.INTERNAL_ERROR, str(err))
xmlrpcutil._set_response(xmlrpclib.dumps(err))
cherrypy.tools.xmlrpc_error = ErrorTool(on_error)
class Core(BaseCore):
_cp_config = {'tools.xmlrpc_error.on': True,
'tools.bcfg2_authn.on': True}
def __init__(self, *args, **kwargs):
BaseCore.__init__(self, *args, **kwargs)
cherrypy.tools.bcfg2_authn = cherrypy.Tool('on_start_resource',
self.do_authn)
self.rmi = self._get_rmi()
cherrypy.engine.subscribe('stop', self.shutdown)
def do_authn(self):
try:
header = cherrypy.request.headers['Authorization']
except KeyError:
self.critical_error("No authentication data presented")
auth_type, auth_content = header.split()
auth_content = b64decode(auth_content)
try:
username, password = auth_content.split(":")
except ValueError:
username = auth_content
password = ""
# FIXME: Get client cert
cert = None
address = (cherrypy.request.remote.ip, cherrypy.request.remote.name)
return self.authenticate(cert, username, password, address)
@cherrypy.expose
def default(self, *vpath, **params):
# needed to make enough changes to the stock XMLRPCController
# to support plugin.__rmi__ and prepending client address that
# we just rewrote. it clearly wasn't written with inheritance
# in mind :(
rpcparams, rpcmethod = xmlrpcutil.process_body()
if "." not in rpcmethod:
address = (cherrypy.request.remote.ip, cherrypy.request.remote.name)
rpcparams = (address, ) + rpcparams
handler = getattr(self, rpcmethod)
if not handler or not getattr(handler, "exposed", False):
raise Exception('method "%s" is not supported' % attr)
else:
try:
handler = self.rmi[rpcmethod]
except:
raise Exception('method "%s" is not supported' % rpcmethod)
method_start = time.time()
try:
body = handler(*rpcparams, **params)
finally:
self.stats.add_value(rpcmethod, time.time() - method_start)
xmlrpcutil.respond(body, 'utf-8', True)
return cherrypy.serving.response.body
def run(self):
hostname, port = urlparse(self.setup['location'])[1].split(':')
if self.setup['listen_all']:
hostname = '0.0.0.0'
config = {'engine.autoreload.on': False,
'server.socket_port': int(port)}
if self.setup['cert'] and self.setup['key']:
config.update({'server.ssl_module': 'pyopenssl',
'server.ssl_certificate': self.setup['cert'],
'server.ssl_private_key': self.setup['key']})
if self.setup['debug']:
config['log.screen'] = True
cherrypy.config.update(config)
cherrypy.quickstart(self, config={'/': self.setup})
def parse_opts(argv=None):
if argv is None:
argv = sys.argv[1:]
optinfo = dict()
optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS)
optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS)
optinfo.update(Bcfg2.Options.DAEMON_COMMON_OPTIONS)
setup = Bcfg2.Options.OptionParser(optinfo, argv=argv)
setup.parse(argv)
return setup
def application(environ, start_response):
""" running behind Apache as a WSGI app is not currently
supported, but I'm keeping this code here because I hope for it to
be supported some day. we'll need to set up an AMQP task queue
and related magic for that to happen, though. """
cherrypy.config.update({'environment': 'embedded'})
setup = parse_opts(argv=['-C', environ['config']])
root = Core(setup, start_fam_thread=True)
cherrypy.tree.mount(root)
return cherrypy.tree(environ, start_response)
|