diff options
author | Jason Gerard DeRose <jderose@redhat.com> | 2010-02-23 10:53:47 -0700 |
---|---|---|
committer | Jason Gerard DeRose <jderose@redhat.com> | 2010-03-01 20:21:38 -0700 |
commit | 942919bef77030b10a96cab66ab878a8a3d7ef10 (patch) | |
tree | e4f7f3262d913729c80ddf057b4b06db4aec252c /ipaserver/rpcserver.py | |
parent | 5220c949a446fcdc870708ea09ec1b444a715edd (diff) | |
download | freeipa-942919bef77030b10a96cab66ab878a8a3d7ef10.tar.gz freeipa-942919bef77030b10a96cab66ab878a8a3d7ef10.tar.xz freeipa-942919bef77030b10a96cab66ab878a8a3d7ef10.zip |
Consolidate to single WSGI entry point
Diffstat (limited to 'ipaserver/rpcserver.py')
-rw-r--r-- | ipaserver/rpcserver.py | 149 |
1 files changed, 117 insertions, 32 deletions
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py index 4a5040e2f..ad402cdf8 100644 --- a/ipaserver/rpcserver.py +++ b/ipaserver/rpcserver.py @@ -24,6 +24,7 @@ Also see the `ipalib.rpc` module. """ from cgi import parse_qs +from xml.sax.saxutils import escape from xmlrpclib import Fault from ipalib.backend import Executioner from ipalib.errors import PublicError, InternalError, CommandError, JSONError @@ -31,6 +32,33 @@ from ipalib.request import context, Connection, destroy_context from ipalib.rpc import xml_dumps, xml_loads from ipalib.util import make_repr from ipalib.compat import json +from wsgiref.util import shift_path_info + + +_not_found_template = """<html> +<head> +<title>404 Not Found</title> +</head> +<body> +<h1>Not Found</h1> +<p> +The requested URL <strong>%(url)s</strong> was not found on this server. +</p> +</body> +</html>""" + + +def not_found(environ, start_response): + """ + Return a 404 Not Found error. + """ + status = '404 Not Found' + response_headers = [('Content-Type', 'text/html')] + start_response(status, response_headers) + output = _not_found_template % dict( + url=escape(environ['SCRIPT_NAME'] + environ['PATH_INFO']) + ) + return [output] def read_input(environ): @@ -85,17 +113,81 @@ def extract_query(environ): return query +class session(Executioner): + """ + WSGI routing middleware and entry point into IPA server. + + The `session` plugin is the entry point into the IPA server. It will create + an LDAP connection (from a session cookie or the KRB5CCNAME header) and then + dispatch the request to the appropriate application. In WSGI parlance, + `session` is *middleware*. + """ + + def __init__(self): + super(session, self).__init__() + self.__apps = {} + + def __iter__(self): + for key in sorted(self.__apps): + yield key + + def __getitem__(self, key): + return self.__apps[key] + + def __contains__(self, key): + return key in self.__apps + + def __call__(self, environ, start_response): + try: + self.create_context(ccache=environ.get('KRB5CCNAME')) + return self.route(environ, start_response) + finally: + destroy_context() + + def finalize(self): + self.url = self.env['mount_ipa'] + super(session, self).finalize() + + def route(self, environ, start_response): + key = shift_path_info(environ) + if key in self.__apps: + app = self.__apps[key] + return app(environ, start_response) + return not_found(environ, start_response) + + def mount(self, app, key): + """ + Mount the WSGI application *app* at *key*. + """ +# if self.__islocked__(): +# raise StandardError('%s.mount(): locked, cannot mount %r at %r' % ( +# self.name, app, key) +# ) + if key in self.__apps: + raise StandardError('%s.mount(): cannot replace %r with %r at %r' % ( + self.name, self.__apps[key], app, key) + ) + self.info('Mounting %r at %r', app, key) + self.__apps[key] = app + + + + + class WSGIExecutioner(Executioner): """ Base class for execution backends with a WSGI application interface. """ + key = '' + + def set_api(self, api): + super(WSGIExecutioner, self).set_api(api) + if 'session' in self.api.Backend: + self.api.Backend.session.mount(self, self.key) + def finalize(self): - url = self.env['mount_' + self.name] - if url.startswith('/'): - self.url = url - else: - self.url = self.env.mount_ipa + url + self.url = self.env.mount_ipa + self.key super(WSGIExecutioner, self).finalize() def wsgi_execute(self, environ): @@ -103,28 +195,24 @@ class WSGIExecutioner(Executioner): error = None _id = None try: - try: - self.create_context(ccache=environ.get('KRB5CCNAME')) - if ( - environ.get('CONTENT_TYPE', '').startswith(self.content_type) - and environ['REQUEST_METHOD'] == 'POST' - ): - data = read_input(environ) - (name, args, options, _id) = self.unmarshal(data) - else: - (name, args, options, _id) = self.simple_unmarshal(environ) - if name not in self.Command: - raise CommandError(name=name) - result = self.Command[name](*args, **options) - except PublicError, e: - error = e - except StandardError, e: - self.exception( - 'non-public: %s: %s', e.__class__.__name__, str(e) - ) - error = InternalError() - finally: - destroy_context() + if ( + environ.get('CONTENT_TYPE', '').startswith(self.content_type) + and environ['REQUEST_METHOD'] == 'POST' + ): + data = read_input(environ) + (name, args, options, _id) = self.unmarshal(data) + else: + (name, args, options, _id) = self.simple_unmarshal(environ) + if name not in self.Command: + raise CommandError(name=name) + result = self.Command[name](*args, **options) + except PublicError, e: + error = e + except StandardError, e: + self.exception( + 'non-public: %s: %s', e.__class__.__name__, str(e) + ) + error = InternalError() return self.marshal(result, error, _id) def simple_unmarshal(self, environ): @@ -155,11 +243,6 @@ class WSGIExecutioner(Executioner): raise NotImplementedError('%s.marshal()' % self.fullname) - -class session(Executioner): - pass - - class xmlserver(WSGIExecutioner): """ Execution backend plugin for XML-RPC server. @@ -168,6 +251,7 @@ class xmlserver(WSGIExecutioner): """ content_type = 'text/xml' + key = 'xml' def finalize(self): self.__system = { @@ -226,6 +310,7 @@ class jsonserver(WSGIExecutioner): """ content_type = 'application/json' + key = 'json' def marshal(self, result, error, _id=None): if error: |