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

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg_security_server/ndg/security/server/wsgi/openid/provider/renderinginterface/genshi/__init__.py@6202
Revision 6202, 14.8 KB checked in by pjkersha, 11 years ago (diff)

Adding information about requested attributes for the decide page interface of the OpenID Provider.

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
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    )
62    __slots__ = tuple(["__%s" % name for name in ATTR_NAMES])
63    del name
64    __slots__ += PROPERTY_NAMES
65       
66    LOGIN_TMPL_NAME = 'login.html'
67    DECIDE_PAGE_TMPL_NAME = 'decide.html'
68    MAIN_PAGE_TMPL_NAME = 'main.html'
69    ERROR_PAGE_TMPL_NAME = 'error.html'
70   
71    # Approve and reject submit HTML input types for the Relying Party Approval
72    # page
73    APPROVE_RP_SUBMIT = OpenIDProviderMiddleware.APPROVE_RP_SUBMIT
74    REJECT_RP_SUBMIT = OpenIDProviderMiddleware.REJECT_RP_SUBMIT
75
76    DEFAULT_TEMPLATES_DIR = path.join(path.dirname(__file__), 'templates')
77   
78    def __init__(self, *arg, **opt):
79        '''Extend RenderingInterface to include config and set-up for Genshi
80        templating
81       
82        @type *arg: tuple
83        @param *arg: RenderingInterface parent class arguments
84        @type **opt: dict
85        @param **opt: additional keywords to set-up Buffet rendering'''
86        super(GenshiRendering, self).__init__(*arg, **opt)
87       
88        # Initialise attributes
89        for i in GenshiRendering.PROPERTY_NAMES:
90            setattr(self, i, '')
91         
92        # Update from keywords   
93        for i in opt:
94            setattr(self, i, opt[i])
95
96        if not self.templateRootDir:
97            self.templateRootDir = GenshiRendering.DEFAULT_TEMPLATES_DIR
98         
99        self.__loader = TemplateLoader(self.templateRootDir, auto_reload=True)
100       
101        self.title = ''
102        self.heading = ''
103        self.xml = ''
104        self.headExtras = ''
105        self.loginStatus = True
106        self.session = ''
107        self.success_to = ''
108        self.fail_to = ''
109       
110        self.__oidRequest = None
111        self.__identityURI = None
112        self.__environ = None
113        self.__trust_root = None
114
115    def getEnviron(self):
116        return self.__environ
117
118    def getIdentityURI(self):
119        return self.__identityURI
120
121    def setEnviron(self, value):
122        self.__environ = value
123
124    def setIdentityURI(self, value):
125        self.__identityURI = value
126
127    def getTrust_root(self):
128        return self.__trust_root
129
130    def getOidRequest(self):
131        return self.__oidRequest
132
133    def setTrust_root(self, value):
134        if not isinstance(value, basestring):
135            raise TypeError('Expecting string type for trust_root attribute; '
136                            'got %r' % type(value))
137        self.__trust_root = value
138
139    def setOidRequest(self, value):
140        if not isinstance(value, CheckIDRequest):
141            raise TypeError('Expecting %r type for oidRequest attribute; '
142                            'got %r' % (CheckIDRequest, type(value)))
143        self.__oidRequest = value
144
145    def getSuccess_to(self):
146        return self.__success_to
147
148    def getFail_to(self):
149        return self.__fail_to
150
151    def setSuccess_to(self, value):
152        if not isinstance(value, basestring):
153            raise TypeError('Expecting string type for success_to attribute; '
154                            'got %r' % type(value))
155        self.__success_to = value
156
157    def setFail_to(self, value):
158        if not isinstance(value, basestring):
159            raise TypeError('Expecting string type for fail_to attribute; '
160                            'got %r' % type(value))
161        self.__fail_to = value
162
163    def getTitle(self):
164        return self.__title
165
166    def getHeading(self):
167        return self.__heading
168
169    def getXml(self):
170        return self.__xml
171
172    def getHeadExtras(self):
173        return self.__headExtras
174
175    def getLoginStatus(self):
176        return self.__loginStatus
177
178    def getSession(self):
179        return self.__session
180   
181    def setTitle(self, value):
182        if not isinstance(value, basestring):
183            raise TypeError('Expecting string type for title attribute; '
184                            'got %r' % type(value))
185        self.__title = value
186   
187    def setHeading(self, value):
188        if not isinstance(value, basestring):
189            raise TypeError('Expecting string type for heading attribute; '
190                            'got %r' % type(value))
191        self.__heading = value
192
193    def setXml(self, value):
194        if not isinstance(value, basestring):
195            raise TypeError('Expecting string type for xml attribute; '
196                            'got %r' % type(value))
197        self.__xml = value
198
199    def setHeadExtras(self, value):
200        if not isinstance(value, basestring):
201            raise TypeError('Expecting string type for headExtras attribute; '
202                            'got %r' % type(value))
203        self.__headExtras = value
204
205    def setLoginStatus(self, value):
206        if not isinstance(value, bool):
207            raise TypeError('Expecting bool type for loginStatus attribute; '
208                            'got %r' % type(value))
209        self.__loginStatus = value
210
211    def setSession(self, value):
212        self.__session = value
213
214    title = property(getTitle, setTitle, None, "Template title")
215
216    heading = property(getHeading, setHeading, None, "Template heading")
217
218    xml = property(getXml, setXml, None, "Additional XML for template")
219
220    headExtras = property(getHeadExtras, setHeadExtras, None, 
221                          "additional head info for template")
222
223    loginStatus = property(getLoginStatus, setLoginStatus, None, 
224                           "Login Status boolean")
225
226    session = property(getSession, setSession, None, 
227                       "Beaker session")
228
229    success_to = property(getSuccess_to, setSuccess_to, None, 
230                          "URL following successful login")
231
232    fail_to = property(getFail_to, setFail_to, None, 
233                       "URL following an error with login")
234
235    def __setattr__(self, name, value):
236        """Apply some generic type checking"""
237        if name in GenshiRendering.PROPERTY_NAMES:
238            if not isinstance(value, basestring):
239                raise TypeError('Expecting string type for %r attribute; got '
240                                '%r' % (name, type(value)))
241           
242        super(GenshiRendering, self).__setattr__(name, value)
243       
244    def _getLoader(self):
245        return self.__loader
246
247    def _setLoader(self, value):
248        if not isinstance(value, TemplateLoader):
249            raise TypeError('Expecting %r type for "loader"; got %r' % 
250                            (TemplateLoader, type(value)))
251        self.__loader = value
252
253    loader = property(_getLoader, _setLoader, 
254                      doc="Genshi TemplateLoader instance") 
255         
256    def _render(self, templateName, c=None, **kw):
257        '''Wrapper for Genshi template rendering
258        @type templateName: basestring
259        @param templateName: name of template file to load
260        @type c: None/object
261        @param c: reference to object to pass into template - defaults to self
262        @type kw: dict
263        @param kw: keywords to pass to template
264        @rtype: string
265        @return: rendered template
266        '''
267        if c is None:
268            c = self
269           
270        kw['c'] = c
271       
272        tmpl = self.loader.load(templateName)
273        rendering = tmpl.generate(**kw).render('html', doctype='html')
274       
275        return rendering
276
277    def yadis(self, environ, start_response):
278        """Render Yadis document containing user URL - override base
279        implementation to specify Yadis based discovery for user URL
280       
281        @type environ: dict
282        @param environ: dictionary of environment variables
283        @type start_response: callable
284        @param start_response: WSGI start response function.  Should be called
285        from this method to set the response code and HTTP header content
286        @rtype: basestring
287        @return: WSGI response
288        """
289        userIdentifier = OpenIDProviderMiddleware.parseIdentityURI(
290                                                    environ['PATH_INFO'])[-1]
291       
292        # This is where this implementation differs from the base class one
293        user_url = OpenIDProviderMiddleware.createIdentityURI(
294                                                        self.urls['url_yadis'],
295                                                        userIdentifier)
296       
297        yadisDict = dict(openid20type=discover.OPENID_2_0_TYPE, 
298                         openid10type=discover.OPENID_1_0_TYPE,
299                         endpoint_url=self.urls['url_openidserver'], 
300                         user_url=user_url)
301       
302        response = RenderingInterface.tmplYadis % yadisDict
303     
304        start_response('200 OK',
305                       [('Content-type', 'application/xrds+xml'+self.charset),
306                        ('Content-length', str(len(response)))])
307        return response
308 
309    def login(self, environ, start_response, success_to=None, fail_to=None, 
310              msg=''):
311        """Set-up template for OpenID Provider Login"""
312        self.title = "OpenID Login"
313        self.heading = "Login"
314        self.success_to = success_to or self.urls['url_mainpage']
315        self.fail_to = fail_to or self.urls['url_mainpage'] 
316        self.xml = msg
317       
318        response = self._render(GenshiRendering.LOGIN_TMPL_NAME)
319        start_response('200 OK', 
320                       [('Content-type', 'text/html'+self.charset),
321                        ('Content-length', str(len(response)))])
322        self.xml = ''
323        return response
324               
325    def mainPage(self, environ, start_response):
326        """Set-up template for OpenID Provider Login"""
327        self.title = "OpenID Provider"
328        self.heading = "OpenID Provider"
329        self.headExtras = '<meta http-equiv="x-xrds-location" content="%s"/>'%\
330                        self.urls['url_serveryadis']
331   
332        response = self._render(GenshiRendering.MAIN_PAGE_TMPL_NAME)
333        start_response('200 OK', 
334                       [('Content-type', 'text/html'+self.charset),
335                        ('Content-length', str(len(response)))])
336        return response
337
338    def identityPage(self, environ, start_response):
339        """This page would normally render the user's Identity page but it's
340        not needed for Yadis only based discovery"""
341        self.title = 'OpenID Provider - Error'
342        self.heading = 'OpenID Provider - Invalid Page Requested'
343        self.xml = 'Invalid page requested for OpenID Provider'
344        response = self._render(GenshiRendering.ERROR_PAGE_TMPL_NAME) 
345        self.xml = ''   
346        start_response("404 Not Found", 
347                       [('Content-type', 'text/html'+self.charset),
348                        ('Content-length', str(len(response)))])
349        return response
350 
351    def decidePage(self, environ, start_response, oidRequest):
352        """Handle user interaction required before final submit back to Relying
353        Party"""
354        self.title = 'Approve OpenID Request?'
355        self.heading = 'Approve OpenID Request?'
356        self.trust_root = oidRequest.trust_root
357        self.oidRequest = oidRequest
358        ax_req = ax.FetchRequest.fromOpenIDRequest(oidRequest)
359        axRequestedAttr = ax_req.requested_attributes
360        self.environ = environ
361       
362        if oidRequest.idSelect():
363            if 'username' not in self.session:
364                log.error("No 'username' key set in session object for "
365                          "idselect mode do decide page")
366                msg = ('An internal error has occurred.  Please contact '
367                       'your system administrator')
368                response = self.errorPage(environ, start_response, msg)
369                return response
370               
371            userIdentifier = self._authN.username2UserIdentifiers(
372                                            environ,
373                                            self.session['username'])[0]
374                                           
375            # Use the Yadis path because we want to use Yadis only
376            # based discovery
377            self.identityURI = OpenIDProviderMiddleware.createIdentityURI(
378                                                        self.urls['url_yadis'],
379                                                        userIdentifier)
380        else:
381            self.identityURI = oidRequest.identity
382       
383        response = self._render(GenshiRendering.DECIDE_PAGE_TMPL_NAME,
384                                axRequestedAttr=axRequestedAttr)
385        self.identityURI = ''
386       
387        start_response("200 OK", 
388                       [('Content-type', 'text/html'+self.charset),
389                        ('Content-length', str(len(response)))])
390        return response
391       
392    def errorPage(self, environ, start_response, msg, code=500):
393        '''Display error information'''
394        self.title = 'Error with OpenID Provider'
395        self.heading = 'Error'
396        self.xml = msg
397        response = self._render(GenshiRendering.ERROR_PAGE_TMPL_NAME)
398        start_response('%d %s' % (code, httplib.responses[code]), 
399                       [('Content-type', 'text/html'+self.charset),
400                        ('Content-length', str(len(response)))])
401        self.xml = ''
402        return response
403
404    trust_root = property(getTrust_root, setTrust_root, 
405                          doc="trust_root - dict of user trusted RPs")
406
407    oidRequest = property(getOidRequest, setOidRequest, 
408                          doc="oidRequest - OpenID Request object")
409
410    environ = property(getEnviron, setEnviron, None, 
411                       "WSGI environ dict")
412
413    identityURI = property(getIdentityURI, setIdentityURI, 
414                           doc="User OpenID URI")
Note: See TracBrowser for help on using the repository browser.