source: TI05-delivery/ows_framework/branches/ows_framework-refactor/ows_server/ows_server/lib/base.py @ 3631

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/ows_framework/branches/ows_framework-refactor/ows_server/ows_server/lib/base.py
Revision 3631, 8.0 KB checked in by spascoe, 12 years ago (diff)

Merging Calum's ows_server work into the refactor branch

Line 
1import urllib
2from urlparse import urlsplit, urlunsplit
3from base64 import urlsafe_b64encode
4from pylons import c, g, cache, request, session, response
5from pylons.controllers import WSGIController
6from pylons.decorators import jsonify, validate
7from pylons.templating import render
8from pylons.controllers.util import abort, redirect_to, etag_cache
9from pylons.i18n import N_, _, ungettext
10from paste.request import construct_url
11import ows_server.models as model
12import ows_server.lib.helpers as h
13from ows_server.lib.security_util import setSecuritySession, LoginServiceQuery
14from ows_common import exceptions as OWS_E
15from ows_common.operations_metadata import OperationsMetadata, Operation, RequestMethod
16from ows_common.get_capabilities import ServiceMetadata
17import ows_common.xml
18
19
20try:
21    from xml.etree import ElementTree as ET
22except ImportError:
23    from elementtree import ElementTree as ET
24
25import logging
26logger = logging.getLogger(__name__)
27
28# Configure
29EXCEPTION_TYPE = request.environ['ndgConfig'].get('OWS_SERVER', 'exception_type', 'ogc').lower()
30
31class BaseController(WSGIController):
32   
33    def __call__(self, environ, start_response):       
34        # Insert any code to be run per request here. The Routes match
35        # is under environ['pylons.routes_dict'] should you want to check
36        # the action or route vars here
37
38        logger.debug("BaseController.__call__ ...")
39       
40        # construct URL picking up setting of server name from config to
41        # avoid exposing absolute URL hidden behind mod_proxy see #857
42        # Also, avoid returning to getCredentials and potentially exposing
43        # username/pass-phrase on URL.
44        # TODO: rework getCredentials get-out for more modular solution
45        pathInfo = urllib.quote(environ.get('PATH_INFO', '')) 
46        if 'getCredentials' in pathInfo:
47            logger.debug(\
48                "Reverting request URL from getCredentials to discovery...")
49            c.requestURL = g.server + '/discovery'       
50        else:
51            c.requestURL = g.server + pathInfo
52            query='&'.join(["%s=%s"%item for item in request.params.items()])
53            if query:
54                c.requestURL += '?' + query
55       
56        # Base 64 encode to enable passing around in 'r' argument of query
57        # string for use with login/logout
58        c.b64encRequestURL = urlsafe_b64encode(c.requestURL)
59
60        if 'h' in request.params:
61            # 'h' corresponds to the setting of a session manager host i.e.
62            # the request has come from a completed login from the login
63            # service
64            logger.debug("Setting security session from URL query args ...")
65           
66            # Copy the query arguments into security session keys
67            setSecuritySession()
68           
69            session.save()
70           
71            # Re-construct the URL removing the security related arguments
72            qs = LoginServiceQuery.stripFromURI()
73
74            logger.debug('Switching from https to http...')
75            cc = g.server + urllib.quote(environ.get('PATH_INFO',''))
76            if qs:
77                cc += "?" + qs
78               
79            logger.debug('URL transport switched to http: "%s"' % cc)
80            h.redirect_to(cc)
81
82               
83        #organise the information needed by pagetabs ...
84        # TODO avoid this for the server controllers ...
85       
86        c.pageTabs=[('Search',g.discoveryURL)]
87        if 'results' in session: 
88            c.pageTabs.append(('Results',session['results']))
89            # make selections tab available once results are shown - to simplify associated business logic
90            c.pageTabs.append(('Selections',h.url_for(controller='selectedItems',action='index')))
91           
92        if 'lastViewed' in session: c.pageTabs.append(('Details',session['lastViewed']))
93       
94        if 'viewItems' in session: c.pageTabs.append(('View', h.url_for(controller='viewItems',action='index')))
95
96#            c.pageTabs.append(('Visualise',h.url_for(controller='visualise', action='index')))
97#            c.pageTabs.append(('Download','Blah'))
98       
99        return WSGIController.__call__(self, environ, start_response)
100   
101class OwsController(BaseController):
102    def __call__(self, environ, start_response):
103
104        # All OWS parameter names are case insensitive.
105        req = request._current_obj()
106        self.ows_params = {}
107        for k in req.params:
108            self.ows_params[k.lower()] = req.params[k]       
109
110        # If the EXCEPTION_TYPE is 'pylons' let Pylons catch any exceptions.
111        # Otherwise send an OGC exception report for any OWS_E.OwsError
112        if 'pylons' in EXCEPTION_TYPE:
113            self._fixOwsAction(environ)
114            return super(OwsController, self).__call__(environ, start_response)
115        else:
116            try:
117                self._fixOwsAction(environ)
118                return super(OwsController, self).__call__(environ, start_response)
119            except OWS_E.OwsError, e:
120                logger.exception(e)
121
122                response.headers['content-type'] = 'text/xml'
123                return render('exception_report', report=e.report, format='xml')
124
125
126    def _fixOwsAction(self, environ):
127        # Override the Routes action from the request query parameter
128        try:
129            action = self.ows_params['request']
130        except KeyError:
131            raise OWS_E.MissingParameterValue('REQUEST parameter not specified', 'REQUEST')
132
133        # Check action is a method in self
134        if not getattr(self, action):
135            raise OWS_E.InvalidParameterValue('request=%s not supported' % action, 'REQUEST')
136
137        # override routes action with request
138        environ['pylons.routes_dict']['action'] = action
139        del self.ows_params['request']
140
141    def _loadCapabilities(self):
142        """
143        creates an ows_common.get_capabilities.ServiceMetadata object
144        by consulting the paste configuration and annotations in the
145        controller definition.
146
147        """
148        # Deduce ows_endpoint from routes
149        ows_endpoint = h.url_for(controller=request.environ['pylons.routes_dict']['controller'])
150       
151        #Deduce base_url from config
152        base_url =request.environ['ndgConfig'].get('DEFAULT','server')
153
154       
155        # Get the server-level configuration data from an XML file
156        config = request.environ['paste.config']
157        sm_tree = ET.parse(config['ows_common_config'])
158        sm = ows_common.xml.service_metadata(sm_tree.getroot())
159       
160        # Extract service-level parameters and constraint
161        parameters = getattr(self, '_ows_parameters', {})
162        constraints = getattr(self, '_ows_constraints', {})
163        versions = getattr(self, '_ows_versions', [])
164       
165        # Extract operation-level parameters and constraints
166        od = {}
167        for attr in dir(self):
168            op = getattr(self, attr)
169            if hasattr(op, '_ows_name'):
170                p = getattr(op, '_ows_parameters', {})
171                c = getattr(op, '_ows_constraints', {})
172                od[op._ows_name] = Operation(get=RequestMethod(href=base_url+ows_endpoint),
173                                             post=None,
174                                             parameters=p,
175                                             constraints=c,
176                                             name=op._ows_name)
177       
178        sm.operationsMetadata = OperationsMetadata(od, constraints, parameters)
179        sm.serviceIdentification.serviceTypeVersions = versions
180        return sm
181
182    def _renderCapabilities(self, template='ows/get_capabilities'):
183        """
184        The standard way of returning a Capabilities document.
185
186        Each subclass should implement self._load_capabilities() and call
187        this method to return a response object.
188
189        """
190        c.service_metadata = self._loadCapabilities()       
191        response.headers['content-type'] = 'text/xml'
192        return render(template, format='xml')
193
194
195# Include the '_' function in the public names
196__all__ = [__name for __name in locals().keys() if not __name.startswith('_') \
197           or __name == '_']
Note: See TracBrowser for help on using the repository browser.