source: TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/openid/provider/renderinginterface/genshi/__init__.py @ 7077

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/openid/provider/renderinginterface/genshi/__init__.py@7077
Revision 7077, 15.7 KB checked in by pjkersha, 9 years ago (diff)
  • Property svn:keywords set to Id
Line 
1"""NDG Security Genshi based Rendering Interface for
2OpenIDProviderMiddleware
3
4NERC Data Grid Project
5"""
6__author__ = "P J Kershaw"
7__date__ = "14/08/08"
8__copyright__ = "(C) 2009 Science and Technology Facilities Council"
9__contact__ = "Philip.Kershaw@stfc.ac.uk"
10__revision__ = "$Id$"
11__license__ = "BSD - see LICENSE file in top-level directory"
12import logging
13log = logging.getLogger(__name__)
14
15import httplib
16from os import path
17
18from genshi.template import TemplateLoader
19from openid.consumer import discover
20from openid.server.server import CheckIDRequest, OpenIDResponse
21from openid.extensions import ax
22
23# Rendering classes for OpenID Provider must derive from generic render
24# interface
25from ndg.security.server.wsgi.openid.provider import (RenderingInterface, 
26    RenderingInterfaceConfigError)
27   
28from ndg.security.server.wsgi.openid.provider import OpenIDProviderMiddleware
29
30
31class GenshiRendering(RenderingInterface):
32    """Provide Templating for OpenID Provider Middleware using Genshi templating
33    """
34    PROPERTY_NAMES = (
35        'templateRootDir',
36        'baseURL',
37        'leftLogo',
38        'leftAlt',
39        'leftLink',
40        'leftImage',
41        'rightLink',
42        'rightImage',
43        'rightAlt',
44        'footerText',
45        'helpIcon'
46    )
47    ATTR_NAMES = (
48        'title', 
49        'heading',
50        'xml', 
51        'headExtras', 
52        'loginStatus',
53        'loader',
54        'session',
55        'success_to',
56        'fail_to',
57        'trust_root',
58        'environ',
59        'identityURI',
60        'oidRequest',
61        'oidResponse'
62    )
63    __slots__ = tuple(["__%s" % name for name in ATTR_NAMES])
64    del name
65    __slots__ += PROPERTY_NAMES
66       
67    LOGIN_TMPL_NAME = 'login.html'
68    DECIDE_PAGE_TMPL_NAME = 'decide.html'
69    MAIN_PAGE_TMPL_NAME = 'main.html'
70    ERROR_PAGE_TMPL_NAME = 'error.html'
71   
72    # Approve and reject submit HTML input types for the Relying Party Approval
73    # page
74    APPROVE_RP_SUBMIT = OpenIDProviderMiddleware.APPROVE_RP_SUBMIT
75    REJECT_RP_SUBMIT = OpenIDProviderMiddleware.REJECT_RP_SUBMIT
76
77    DEFAULT_TEMPLATES_DIR = path.join(path.dirname(__file__), 'templates')
78   
79    def __init__(self, *arg, **opt):
80        '''Extend RenderingInterface to include config and set-up for Genshi
81        templating
82       
83        @type *arg: tuple
84        @param *arg: RenderingInterface parent class arguments
85        @type **opt: dict
86        @param **opt: additional keywords to set-up Genshi rendering'''
87        super(GenshiRendering, self).__init__(*arg, **opt)
88       
89        # Initialise attributes
90        for i in GenshiRendering.PROPERTY_NAMES:
91            setattr(self, i, '')
92         
93        # Update from keywords   
94        for i in opt:
95            setattr(self, i, opt[i])
96
97        if not self.templateRootDir:
98            self.templateRootDir = GenshiRendering.DEFAULT_TEMPLATES_DIR
99         
100        self.__loader = TemplateLoader(self.templateRootDir, auto_reload=True)
101       
102        self.title = ''
103        self.heading = ''
104        self.xml = ''
105        self.headExtras = ''
106        self.loginStatus = True
107        self.session = ''
108        self.success_to = ''
109        self.fail_to = ''
110       
111        self.__oidRequest = None
112        self.__oidResponse = None
113        self.__identityURI = None
114        self.__environ = None
115        self.__trust_root = None
116
117    def getEnviron(self):
118        return self.__environ
119
120    def getIdentityURI(self):
121        return self.__identityURI
122
123    def setEnviron(self, value):
124        self.__environ = value
125
126    def setIdentityURI(self, value):
127        self.__identityURI = value
128
129    def getTrust_root(self):
130        return self.__trust_root
131
132    def getOidRequest(self):
133        return self.__oidRequest
134
135    def getOidResponse(self):
136        return self.__oidResponse
137
138    def setTrust_root(self, value):
139        if not isinstance(value, basestring):
140            raise TypeError('Expecting string type for trust_root attribute; '
141                            'got %r' % type(value))
142        self.__trust_root = value
143
144    def setOidRequest(self, value):
145        if not isinstance(value, CheckIDRequest):
146            raise TypeError('Expecting %r type for oidRequest attribute; '
147                            'got %r' % (CheckIDRequest, type(value)))
148        self.__oidRequest = value
149
150    def setOidResponse(self, value):
151        if not isinstance(value, OpenIDResponse):
152            raise TypeError('Expecting %r type for oidResponse attribute; '
153                            'got %r' % (OpenIDResponse, type(value)))
154        self.__oidResponse = value
155
156    def getSuccess_to(self):
157        return self.__success_to
158
159    def getFail_to(self):
160        return self.__fail_to
161
162    def setSuccess_to(self, value):
163        if not isinstance(value, basestring):
164            raise TypeError('Expecting string type for success_to attribute; '
165                            'got %r' % type(value))
166        self.__success_to = value
167
168    def setFail_to(self, value):
169        if not isinstance(value, basestring):
170            raise TypeError('Expecting string type for fail_to attribute; '
171                            'got %r' % type(value))
172        self.__fail_to = value
173
174    def getTitle(self):
175        return self.__title
176
177    def getHeading(self):
178        return self.__heading
179
180    def getXml(self):
181        return self.__xml
182
183    def getHeadExtras(self):
184        return self.__headExtras
185
186    def getLoginStatus(self):
187        return self.__loginStatus
188
189    def getSession(self):
190        return self.__session
191   
192    def setTitle(self, value):
193        if not isinstance(value, basestring):
194            raise TypeError('Expecting string type for title attribute; '
195                            'got %r' % type(value))
196        self.__title = value
197   
198    def setHeading(self, value):
199        if not isinstance(value, basestring):
200            raise TypeError('Expecting string type for heading attribute; '
201                            'got %r' % type(value))
202        self.__heading = value
203
204    def setXml(self, value):
205        if not isinstance(value, basestring):
206            raise TypeError('Expecting string type for xml attribute; '
207                            'got %r' % type(value))
208        self.__xml = value
209
210    def setHeadExtras(self, value):
211        if not isinstance(value, basestring):
212            raise TypeError('Expecting string type for headExtras attribute; '
213                            'got %r' % type(value))
214        self.__headExtras = value
215
216    def setLoginStatus(self, value):
217        if not isinstance(value, bool):
218            raise TypeError('Expecting bool type for loginStatus attribute; '
219                            'got %r' % type(value))
220        self.__loginStatus = value
221
222    def setSession(self, value):
223        self.__session = value
224
225    title = property(getTitle, setTitle, None, "Template title")
226
227    heading = property(getHeading, setHeading, None, "Template heading")
228
229    xml = property(getXml, setXml, None, "Additional XML for template")
230
231    headExtras = property(getHeadExtras, setHeadExtras, None, 
232                          "additional head info for template")
233
234    loginStatus = property(getLoginStatus, setLoginStatus, None, 
235                           "Login Status boolean")
236
237    session = property(getSession, setSession, None, 
238                       "Beaker session")
239
240    success_to = property(getSuccess_to, setSuccess_to, None, 
241                          "URL following successful login")
242
243    fail_to = property(getFail_to, setFail_to, None, 
244                       "URL following an error with login")
245
246    def __setattr__(self, name, value):
247        """Apply some generic type checking"""
248        if name in GenshiRendering.PROPERTY_NAMES:
249            if not isinstance(value, basestring):
250                raise TypeError('Expecting string type for %r attribute; got '
251                                '%r' % (name, type(value)))
252           
253        super(GenshiRendering, self).__setattr__(name, value)
254       
255    def _getLoader(self):
256        return self.__loader
257
258    def _setLoader(self, value):
259        if not isinstance(value, TemplateLoader):
260            raise TypeError('Expecting %r type for "loader"; got %r' % 
261                            (TemplateLoader, type(value)))
262        self.__loader = value
263
264    loader = property(_getLoader, _setLoader, 
265                      doc="Genshi TemplateLoader instance") 
266         
267    def _render(self, templateName, c=None, **kw):
268        '''Wrapper for Genshi template rendering
269        @type templateName: basestring
270        @param templateName: name of template file to load
271        @type c: None/object
272        @param c: reference to object to pass into template - defaults to self
273        @type kw: dict
274        @param kw: keywords to pass to template
275        @rtype: string
276        @return: rendered template
277        '''
278        if c is None:
279            c = self
280           
281        kw['c'] = c
282       
283        tmpl = self.loader.load(templateName)
284        rendering = tmpl.generate(**kw).render('html', doctype='html')
285       
286        return rendering
287
288    def yadis(self, environ, start_response):
289        """Render Yadis document containing user URL - override base
290        implementation to specify Yadis based discovery for user URL
291       
292        @type environ: dict
293        @param environ: dictionary of environment variables
294        @type start_response: callable
295        @param start_response: WSGI start response function.  Should be called
296        from this method to set the response code and HTTP header content
297        @rtype: basestring
298        @return: WSGI response
299        """
300        userIdentifier = OpenIDProviderMiddleware.parseIdentityURI(
301                                                    environ['PATH_INFO'])[-1]
302       
303        # This is where this implementation differs from the base class one
304        user_url = OpenIDProviderMiddleware.createIdentityURI(
305                                                        self.urls['url_yadis'],
306                                                        userIdentifier)
307       
308        yadisDict = dict(openid20type=discover.OPENID_2_0_TYPE, 
309                         openid10type=discover.OPENID_1_0_TYPE,
310                         endpoint_url=self.urls['url_openidserver'], 
311                         user_url=user_url)
312       
313        response = RenderingInterface.tmplYadis % yadisDict
314     
315        start_response('200 OK',
316                       [('Content-type', 'application/xrds+xml'+self.charset),
317                        ('Content-length', str(len(response)))])
318        return response
319 
320    def login(self, environ, start_response, success_to=None, fail_to=None, 
321              msg=''):
322        """Set-up template for OpenID Provider Login"""
323        self.title = "OpenID Login"
324        self.heading = "Login"
325        self.success_to = success_to or self.urls['url_mainpage']
326        self.fail_to = fail_to or self.urls['url_mainpage'] 
327        self.xml = msg
328       
329        response = self._render(GenshiRendering.LOGIN_TMPL_NAME)
330        start_response('200 OK', 
331                       [('Content-type', 'text/html'+self.charset),
332                        ('Content-length', str(len(response)))])
333        self.xml = ''
334        return response
335               
336    def mainPage(self, environ, start_response):
337        """Set-up template for OpenID Provider Login"""
338        self.title = "OpenID Provider"
339        self.heading = "OpenID Provider"
340        self.headExtras = '<meta http-equiv="x-xrds-location" content="%s"/>'%\
341                        self.urls['url_serveryadis']
342   
343        response = self._render(GenshiRendering.MAIN_PAGE_TMPL_NAME)
344        start_response('200 OK', 
345                       [('Content-type', 'text/html'+self.charset),
346                        ('Content-length', str(len(response)))])
347        return response
348
349    def identityPage(self, environ, start_response):
350        """This page would normally render the user's Identity page but it's
351        not needed for Yadis only based discovery"""
352        self.title = 'OpenID Provider - Error'
353        self.heading = 'OpenID Provider - Invalid Page Requested'
354        self.xml = 'Invalid page requested for OpenID Provider'
355        response = self._render(GenshiRendering.ERROR_PAGE_TMPL_NAME) 
356        self.xml = ''   
357        start_response("404 Not Found", 
358                       [('Content-type', 'text/html'+self.charset),
359                        ('Content-length', str(len(response)))])
360        return response
361 
362    def decidePage(self, environ, start_response, oidRequest, oidResponse):
363        """Handle user interaction required before final submit back to Relying
364        Party"""
365        self.title = 'Approve OpenID Request?'
366        self.heading = 'Approve OpenID Request?'
367        self.trust_root = oidRequest.trust_root
368        self.oidRequest = oidRequest
369       
370        # Get all the content namespaced as AX type
371        axArgs = oidResponse.fields.getArgs(ax.AXMessage.ns_uri)
372       
373        # Add to access object for convenient access based on type URI
374        axFetchResponse = ax.FetchResponse()
375        axFetchResponse.parseExtensionArgs(axArgs) 
376       
377        ax_req = ax.FetchRequest.fromOpenIDRequest(oidRequest)
378        axRequestedAttr = ax_req.requested_attributes
379        self.environ = environ
380       
381        if oidRequest.idSelect():
382            if 'username' not in self.session:
383                log.error("No 'username' key set in session object for "
384                          "idselect mode do decide page")
385                msg = ('An internal error has occurred.  Please contact '
386                       'your system administrator')
387                response = self.errorPage(environ, start_response, msg)
388                return response
389               
390            userIdentifier = self._authN.username2UserIdentifiers(
391                                            environ,
392                                            self.session['username'])[0]
393                                           
394            # Use the Yadis path because we want to use Yadis only
395            # based discovery
396            self.identityURI = OpenIDProviderMiddleware.createIdentityURI(
397                                                        self.urls['url_yadis'],
398                                                        userIdentifier)
399        else:
400            self.identityURI = oidRequest.identity
401       
402        response = self._render(GenshiRendering.DECIDE_PAGE_TMPL_NAME,
403                                axRequestedAttr=axRequestedAttr,
404                                axFetchResponse=axFetchResponse)
405        self.identityURI = ''
406       
407        start_response("200 OK", 
408                       [('Content-type', 'text/html'+self.charset),
409                        ('Content-length', str(len(response)))])
410        return response
411       
412    def errorPage(self, environ, start_response, msg, code=500):
413        '''Display error information'''
414        self.title = 'Error with OpenID Provider'
415        self.heading = 'Error'
416        self.xml = msg
417        response = self._render(GenshiRendering.ERROR_PAGE_TMPL_NAME)
418        start_response('%d %s' % (code, httplib.responses[code]), 
419                       [('Content-type', 'text/html'+self.charset),
420                        ('Content-length', str(len(response)))])
421        self.xml = ''
422        return response
423
424    trust_root = property(getTrust_root, setTrust_root, 
425                          doc="trust_root - dict of user trusted RPs")
426
427    oidRequest = property(getOidRequest, setOidRequest, 
428                          doc="oidRequest - OpenID Request object")
429
430    oidResponse = property(getOidResponse, setOidResponse, 
431                           doc="oidRequest - OpenID Response object")
432   
433    environ = property(getEnviron, setEnviron, None, 
434                       "WSGI environ dict")
435
436    identityURI = property(getIdentityURI, setIdentityURI, 
437                           doc="User OpenID URI")
Note: See TracBrowser for help on using the repository browser.