source: TI12-security/trunk/python/ndg.security.server/ndg/security/server/wsgi/openid_provider.py @ 4100

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg.security.server/ndg/security/server/wsgi/openid_provider.py@4100
Revision 4100, 27.9 KB checked in by pjkersha, 12 years ago (diff)

ndg.security.server.wsgi.openid_provider: fix to enable identifier_select support. - Feature in OpenID 2.0 that allows the user to enter their home site URL rather than their OpenID at the Relying Party.

Line 
1"""NDG Security OpenID Provider Middleware
2
3Compliments AuthKit OpenID Middleware used for OpenID *Relying Party*
4
5NERC Data Grid Project
6
7This software may be distributed under the terms of the Q Public License,
8version 1.0 or later.
9"""
10__author__ = "P J Kershaw"
11__date__ = "27/05/08"
12__copyright__ = "(C) 2008 STFC & NERC"
13__contact__ = "P.J.Kershaw@rl.ac.uk"
14__revision__ = "$Id$"
15
16import logging
17log = logging.getLogger(__name__)
18_debugLevel = log.getEffectiveLevel() <= logging.DEBUG
19
20import paste.request
21from authkit.authenticate import AuthKitConfigError
22
23from openid.extensions import sreg
24from openid.server import server
25from openid.store.filestore import FileOpenIDStore
26from openid.consumer import discover
27
28import httplib
29import sys
30import cgi
31quoteattr = lambda s: '"%s"' % cgi.escape(s, 1)
32
33
34class OpenIDProviderMiddlewareError(Exception):
35    """OpenID Provider WSGI Middleware Error"""
36   
37class OpenIDProviderMiddleware(object):
38    """WSGI Middleware to implement an OpenID Provider"""
39
40    defPaths = dict(path_openidserver='/openidserver',
41                   path_login='/login',
42                   path_loginsubmit='/loginsubmit',
43                   path_id='/id',
44                   path_yadis='/yadis',
45                   path_serveryadis='/serveryadis',
46                   path_allow='/allow',
47                   path_mainpage='/') 
48       
49    def __init__(self, app, 
50                 session_middleware='beaker.session', 
51                 base_url=None,
52                 data_path='./',
53                 charset=None,
54                 trace=True,#False,
55                 **kw):
56        invalidKw=[k for k in kw if k not in OpenIDProviderMiddleware.defPaths]
57        if len(invalidKw) > 0:
58            raise TypeError("Unexpected keywords: %s" % ", ".join(invalidKw))
59       
60        self.paths = OpenIDProviderMiddleware.defPaths.copy()
61        self.paths.update(kw)
62       
63       
64        self.method = dict([(v, k.replace('path_', 'do_')) \
65                         for k,v in OpenIDProviderMiddleware.defPaths.items()])
66       
67        self.session_middleware = session_middleware
68        self.base_url = base_url
69
70        if charset is None:
71            self.charset = ''
72        else:
73            self.charset = '; charset='+charset
74       
75        # If True and debug log level is set display content of response
76        self._trace = trace
77       
78        self.user = None
79        self.app = app
80       
81        # Instantiate OpenID consumer store and OpenID consumer.  If you
82        # were connecting to a database, you would create the database
83        # connection and instantiate an appropriate store here.
84        store = FileOpenIDStore(data_path)
85        self.oidserver=server.Server(store, 
86                                     base_url+self.paths['path_openidserver'])
87       
88        self.approved = {}
89        self.lastCheckIDRequest = {}
90
91   
92    def __call__(self, environ, start_response):
93        if not environ.has_key(self.session_middleware):
94            raise AuthKitConfigError(
95                'The session middleware %r is not present. '
96                'Have you set up the session middleware?'%(
97                    self.session_middleware
98                )
99            )
100
101        self.path = environ.get('PATH_INFO')
102        self.environ = environ
103        self.start_response = start_response
104       
105        # Match against the first level in the path only to allow for the 'id'
106        # and 'yadis' cases where a sub-level could contain a user ID
107        if self.path.startswith(self.paths['path_id']) or \
108           self.path.startswith(self.paths['path_yadis']):
109           
110            pathMatch = '/' + self.path[1:].split('/')[0]
111        else:
112            pathMatch = self.path
113           
114        if pathMatch in self.method:
115            self.query = dict(paste.request.parse_formvars(environ)) 
116            log.debug("Calling method %s ..." % self.method[pathMatch]) 
117           
118            action = getattr(self, self.method[pathMatch])
119            response = action(environ, start_response) 
120            if self._trace and _debugLevel:
121                if isinstance(response, list):
122                    log.debug('Output for %s:\n%s', self.method[pathMatch],
123                                                    ''.join(response))
124                else:
125                    log.debug('Output for %s:\n%s', self.method[pathMatch],
126                                                    response)
127                   
128            return response
129        else:
130            log.debug("No match for path %s" % self.path)
131            return self.app(environ, start_response)
132
133
134    def do_id(self, environ, start_response):
135        '''Handle ID request'''
136       
137        link_tag = '<link rel="openid.server" href="%s%s">' % \
138              (self.base_url, self.paths['path_openidserver'])
139        yadis_loc_tag = '<meta http-equiv="x-xrds-location" content="%s">'%\
140            (self.base_url+self.paths['path_yadis']+'/'+self.path[4:])
141        disco_tags = link_tag + yadis_loc_tag
142        ident = self.base_url + self.path
143
144        approved_trust_roots = []
145        for (aident, trust_root) in self.approved.keys():
146            if aident == ident:
147                trs = '<li><tt>%s</tt></li>\n' % cgi.escape(trust_root)
148                approved_trust_roots.append(trs)
149
150        if approved_trust_roots:
151            prepend = '<p>Approved trust roots:</p>\n<ul>\n'
152            approved_trust_roots.insert(0, prepend)
153            approved_trust_roots.append('</ul>\n')
154            msg = ''.join(approved_trust_roots)
155        else:
156            msg = ''
157
158        return self._showPage(200, 'An Identity Page', 
159                             head_extras=disco_tags, 
160                             msg='''\
161                            <p>This is an identity page for %s.</p>
162                            %s
163                            ''' % (ident, msg))
164
165    tmplYadis = """\
166<?xml version="1.0" encoding="UTF-8"?>
167<xrds:XRDS
168    xmlns:xrds="xri://$xrds"
169    xmlns="xri://$xrd*($v*2.0)">
170  <XRD>
171
172    <Service priority="0">
173      <Type>%(openid20type)s</Type>
174      <Type>%(openid10type)s</Type>
175      <URI>%(endpoint_url)s</URI>
176      <LocalID>%(user_url)s</LocalID>
177    </Service>
178
179  </XRD>
180</xrds:XRDS>"""
181
182    def do_yadis(self, environ, start_response):
183        """Generate Yadis document"""
184
185        self.user = self.path.split('/')[-1]
186       
187        endpoint_url = self.base_url + self.paths['path_openidserver']
188        user_url = self.base_url + self.paths['path_id'] + '/' + self.user
189       
190        yadisDict = dict(openid20type=discover.OPENID_2_0_TYPE, 
191                         openid10type=discover.OPENID_1_0_TYPE,
192                         endpoint_url=endpoint_url, 
193                         user_url=user_url)
194       
195        response = OpenIDProviderMiddleware.tmplYadis % yadisDict
196     
197        start_response('200 OK',
198                    [('Content-type', 'application/xrds+xml' + self.charset),
199                     ('Content-length', str(len(response)))])
200        return response
201
202
203    def do_openidserver(self, environ, start_response):
204        """Handle OpenID Server Request"""
205
206        try:
207            request = self.oidserver.decodeRequest(self.query)
208        except server.ProtocolError, why:
209            return self._displayResponse(why)
210
211        if request is None:
212            # Display text indicating that this is an endpoint.
213            return self._showAboutPage()
214
215        # Check mode is one of "checkid_immediate", "checkid_setup"
216        if request.mode in server.BROWSER_REQUEST_MODES:
217            return self._handleCheckIDRequest(request)
218        else:
219            response = self.oidserver.handleRequest(request)
220            return self._displayResponse(response)
221           
222
223    def do_allow(self, environ, start_response):
224        """Handle allow request - user allow credentials to be passed back to
225        the Relying Party?"""
226       
227        # pretend this next bit is keying off the user's session or something,
228        # right?
229        request = self.lastCheckIDRequest.get(self.user)
230
231        if 'yes' in self.query:
232            if 'login_as' in self.query:
233                self.user = self.query['login_as']
234
235            if request.idSelect():
236                identity = self.base_url+self.paths['path_id']+'/'+\
237                            self.query['identifier']
238            else:
239                identity = request.identity
240               
241            if self.user is None:
242                self.user = identity.split('/')[-1]
243
244            trust_root = request.trust_root
245            if self.query.get('remember', 'no') == 'yes':
246                self.approved[(identity, trust_root)] = 'always'
247
248            response = self._identityApproved(request, identity)
249        elif 'no' in self.query:
250            # TODO: Check 'no' response is OK - no causes AuthKit's Relying
251            # Party implementation to crash with 'openid.return_to' KeyError
252            # in uthkit.authenticate.open_id.process
253            response = request.answer(False)
254
255        else:
256            assert False, 'Expecting yes/no in allow post.  %r' % (self.query,)
257
258        return self._displayResponse(response)
259   
260    tmplServerYadis = """\
261<?xml version="1.0" encoding="UTF-8"?>
262<xrds:XRDS
263    xmlns:xrds="xri://$xrds"
264    xmlns="xri://$xrd*($v*2.0)">
265  <XRD>
266
267    <Service priority="0">
268      <Type>%(openid20type)s</Type>
269      <URI>%(endpoint_url)s</URI>
270    </Service>
271
272  </XRD>
273</xrds:XRDS>
274"""
275
276    def do_serveryadis(self, environ, start_response):
277        """Handle Server Yadis call"""
278        start_response("200 OK", [('Content-type', 'application/xrds+xml')])
279       
280        endpoint_url = self.base_url + self.paths['path_openidserver']
281        return [OpenIDProviderMiddleware.tmplServerYadis % \
282                {'openid20type': discover.OPENID_IDP_2_0_TYPE, 
283                 'endpoint_url': endpoint_url}]
284
285
286    def do_login(self, environ, start_response):
287       
288        success_to, fail_to = (self.paths['path_mainpage'],)*2
289       
290        return self._showPage(200, 'Login Page', form='''\
291        <h2>Login</h2>
292        <p>You may log in with any name. This server does not use
293        passwords because it is just a sample of how to use the OpenID
294        library.</p>
295        <form method="GET" action="%s">
296          <input type="hidden" name="success_to" value="%s" />
297          <input type="hidden" name="fail_to" value="%s" />
298          <input type="text" name="user" value="" />
299          <input type="submit" name="submit" value="Log In" />
300          <input type="submit" name="cancel" value="Cancel" />
301        </form>
302        ''' % (self.paths['path_loginsubmit'], success_to, fail_to))
303
304
305    def do_loginsubmit(self, environ, start_response):
306        if 'submit' in self.query:
307            if 'user' in self.query:
308                self.user = self.query['user']
309            else:
310                self.user = None
311            return self._redirect(start_response, self.query['success_to'])
312        elif 'cancel' in self.query:
313            return self._redirect(start_response, self.query['fail_to'])
314        else:
315            assert 0, 'strange login %r' % (self.query,)   
316           
317
318    def do_mainpage(self, environ, start_response):
319
320        yadis_tag = '<meta http-equiv="x-xrds-location" content="%s">'%\
321            (self.base_url + self.paths['path_serveryadis'])
322        if self.user:
323            openid_url = self.base_url + self.paths['path_id'] + '/' + \
324                        self.user
325            user_message = """\
326            <p>You are logged in as %s. Your OpenID identity URL is
327            <tt><a href=%s>%s</a></tt>. Enter that URL at an OpenID
328            consumer to test this server.</p>
329            """ % (self.user, quoteattr(openid_url), openid_url)
330        else:
331            user_message = """\
332            <p>This server uses a cookie to remember who you are in
333            order to simulate a standard Web user experience. You are
334            not <a href='%s'>logged in</a>.</p>""" % self.paths['path_login']
335
336        return self._showPage(200, 'Main Page', head_extras=yadis_tag, msg='''\
337        <p>This is a simple OpenID server implemented using the <a
338        href="http://openid.schtuff.com/">Python OpenID
339        library</a>.</p>
340
341        %s
342
343        <p>To use this server with a consumer, the consumer must be
344        able to fetch HTTP pages from this web server. If this
345        computer is behind a firewall, you will not be able to use
346        OpenID consumers outside of the firewall with it.</p>
347
348        <p>The URL for this server is <a href=%s><tt>%s</tt></a>.</p>
349        ''' % (user_message, quoteattr(self.base_url), self.base_url))
350           
351                       
352    def _setUser(self):
353        session = environ[self.session_middleware]
354        self.user = session.get('openid_provider_username')
355       
356    def _isAuthorized(self, identity_url, trust_root):
357        if self.user is None:
358            return False
359
360        if identity_url != self.base_url+self.paths['path_id']+'/'+self.user:
361            return False
362
363        key = (identity_url, trust_root)
364        return self.approved.get(key) is not None
365
366    def _addSRegResponse(self, request, response):
367        sreg_req = sreg.SRegRequest.fromOpenIDRequest(request)
368
369        # In a real application, this data would be user-specific,
370        # and the user should be asked for permission to release
371        # it.
372        # TODO: role/user attribute look-up in database vs. username?
373        sreg_data = {
374            'nickname':self.user
375            }
376
377        sreg_resp = sreg.SRegResponse.extractResponse(sreg_req, sreg_data)
378        response.addExtension(sreg_resp)
379
380    def _identityApproved(self, request, identifier=None):
381        response = request.answer(True, identity=identifier)
382        self._addSRegResponse(request, response)
383        return response
384
385    def _handleCheckIDRequest(self, request):
386        is_authorized = self._isAuthorized(request.identity,request.trust_root)
387        if is_authorized:
388            response = self._identityApproved(request)
389            return self._displayResponse(response)
390        elif request.immediate:
391            response = request.answer(False)
392            return self._displayResponse(response)
393        else:
394            self.lastCheckIDRequest[self.user] = request
395            return self._showDecidePage(request)
396
397    def _displayResponse(self, response):
398        try:
399            webresponse = self.oidserver.encodeResponse(response)
400        except server.EncodingError, why:
401            text = why.response.encodeToKVForm()
402            return self._showErrorPage('<pre>%s</pre>' % cgi.escape(text))
403
404        hdr = webresponse.headers.items() + self._writeUserHeader()
405       
406        lenWebResponseBody = len(webresponse.body)
407        if lenWebResponseBody:
408            response = webresponse.body
409        else:
410            response = []
411           
412        hdr += [('Content-length', str(lenWebResponseBody))]
413           
414        self.start_response('%d %s' % (webresponse.code, 
415                                       httplib.responses[webresponse.code]), 
416                            hdr)
417        return response
418
419
420    def _redirect(self, start_response, url):
421        hdr = [('Content-type', 'text/html'+self.charset),
422               ('Location', url)]
423        hdr += self._writeUserHeader()
424
425        start_response('302 %s' % httplib.responses[302], hdr)
426        return []
427
428
429    def _writeUserHeader(self):
430        if self.user is None:
431            # TODO: Refactor this out when replaced with beaker.session /
432            # AuthKit
433            import time
434            t1970 = time.gmtime(0)
435            expires = time.strftime(
436                'Expires=%a, %d-%b-%y %H:%M:%S GMT', t1970)
437            return [('Set-Cookie', 'user=;%s' % expires)]
438        else:
439            return [('Set-Cookie', 'user=%s' % self.user)]
440
441    def _showAboutPage(self):
442        endpoint_url = self.base_url + self.paths['path_openidserver']
443
444        def link(url):
445            url_attr = quoteattr(url)
446            url_text = cgi.escape(url)
447            return '<a href=%s><code>%s</code></a>' % (url_attr, url_text)
448
449        def term(url, text):
450            return '<dt>%s</dt><dd>%s</dd>' % (link(url), text)
451
452        resources = [
453            (self.base_url, "This example server's home page"),
454            ('http://www.openidenabled.com/',
455             'An OpenID community Web site, home of this library'),
456            ('http://www.openid.net/', 'the official OpenID Web site'),
457            ]
458
459        resource_markup = ''.join([term(url, text) for url, text in resources])
460
461        return self._showPage(200, 'This is an OpenID server', msg="""\
462        <p>%s is an OpenID server endpoint.<p>
463        <p>For more information about OpenID, see:</p>
464        <dl>
465        %s
466        </dl>
467        """ % (link(endpoint_url), resource_markup,))
468
469    def _showErrorPage(self, error_message):
470        return self._showPage(400, 'Error Processing Request', err='''\
471        <p>%s</p>
472        <!--
473
474        This is a large comment.  It exists to make this page larger.
475        That is unfortunately necessary because of the "smart"
476        handling of pages returned with an error code in IE.
477
478        *************************************************************
479        *************************************************************
480        *************************************************************
481        *************************************************************
482        *************************************************************
483        *************************************************************
484        *************************************************************
485        *************************************************************
486        *************************************************************
487        *************************************************************
488        *************************************************************
489        *************************************************************
490        *************************************************************
491        *************************************************************
492        *************************************************************
493        *************************************************************
494        *************************************************************
495        *************************************************************
496        *************************************************************
497        *************************************************************
498        *************************************************************
499        *************************************************************
500        *************************************************************
501
502        -->
503        ''' % error_message)
504
505    def _showDecidePage(self, request):
506        id_url_base = self.base_url+self.paths['path_id'] + '/'
507        # XXX: This may break if there are any synonyms for id_url_base,
508        # such as referring to it by IP address or a CNAME.
509       
510        # TODO: OpenID 2.0 Allows request.identity to be set to
511        # http://specs.openid.net/auth/2.0/identifier_select.  See,
512        # http://openid.net/specs/openid-authentication-2_0.html.  This code
513        # implements this overriding the behaviour of the example code on
514        # which this is based.  - Check is the example code based on OpenID 1.0
515        # and therefore wrong for this behaviour?
516#        assert request.identity.startswith(id_url_base), \
517#               repr((request.identity, id_url_base))
518        expected_user = request.identity[len(id_url_base):]
519
520        if request.idSelect(): # We are being asked to select an ID
521            msg = '''\
522            <p>A site has asked for your identity.  You may select an
523            identifier by which you would like this site to know you.
524            On a production site this would likely be a drop down list
525            of pre-created accounts or have the facility to generate
526            a random anonymous identifier.
527            </p>
528            '''
529            fdata = {
530                'path_allow': self.paths['path_allow'],
531                'id_url_base': id_url_base,
532                'trust_root': request.trust_root,
533                }
534            form = '''\
535            <form method="POST" action="%(path_allow)s">
536            <table>
537              <tr><td>Identity:</td>
538                 <td>%(id_url_base)s<input type='text' name='identifier'></td></tr>
539              <tr><td>Trust Root:</td><td>%(trust_root)s</td></tr>
540            </table>
541            <p>Allow this authentication to proceed?</p>
542            <input type="checkbox" id="remember" name="remember" value="yes"
543                /><label for="remember">Remember this
544                decision</label><br />
545            <input type="submit" name="yes" value="yes" />
546            <input type="submit" name="no" value="no" />
547            </form>
548            '''%fdata
549           
550        elif expected_user == self.user:
551            msg = '''\
552            <p>A new site has asked to confirm your identity.  If you
553            approve, the site represented by the trust root below will
554            be told that you control identity URL listed below. (If
555            you are using a delegated identity, the site will take
556            care of reversing the delegation on its own.)</p>'''
557
558            fdata = {
559                'path_allow': self.paths['path_allow'],
560                'identity': request.identity,
561                'trust_root': request.trust_root,
562                }
563            form = '''\
564            <table>
565              <tr><td>Identity:</td><td>%(identity)s</td></tr>
566              <tr><td>Trust Root:</td><td>%(trust_root)s</td></tr>
567            </table>
568            <p>Allow this authentication to proceed?</p>
569            <form method="POST" action="%(path_allow)s">
570              <input type="checkbox" id="remember" name="remember" value="yes"
571                  /><label for="remember">Remember this
572                  decision</label><br />
573              <input type="submit" name="yes" value="yes" />
574              <input type="submit" name="no" value="no" />
575            </form>''' % fdata
576        else:
577            mdata = {
578                'expected_user': expected_user,
579                'user': self.user,
580                }
581            msg = '''\
582            <p>A site has asked for an identity belonging to
583            %(expected_user)s, but you are logged in as %(user)s.  To
584            log in as %(expected_user)s and approve the login request,
585            hit OK below.  The "Remember this decision" checkbox
586            applies only to the trust root decision.</p>''' % mdata
587
588            fdata = {
589                'path_allow': self.paths['path_allow'],
590                'identity': request.identity,
591                'trust_root': request.trust_root,
592                'expected_user': expected_user,
593                }
594            form = '''\
595            <table>
596              <tr><td>Identity:</td><td>%(identity)s</td></tr>
597              <tr><td>Trust Root:</td><td>%(trust_root)s</td></tr>
598            </table>
599            <p>Allow this authentication to proceed?</p>
600            <form method="POST" action="%(path_allow)s">
601              <input type="checkbox" id="remember" name="remember" value="yes"
602                  /><label for="remember">Remember this
603                  decision</label><br />
604              <input type="hidden" name="login_as" value="%(expected_user)s"/>
605              <input type="submit" name="yes" value="yes" />
606              <input type="submit" name="no" value="no" />
607            </form>''' % fdata
608
609        return self._showPage(200, 'Approve OpenID request?',msg=msg,form=form)
610
611
612    def _showIdPage(self, path):
613        link_tag = '<link rel="openid.server" href="%sopenidserver">' %\
614              self.base_url
615        yadis_loc_tag = '<meta http-equiv="x-xrds-location" content="%s">'%\
616            (self.base_url+self.paths['path_yadis']+'/'+path[4:])
617        disco_tags = link_tag + yadis_loc_tag
618        ident = self.base_url + path[1:]
619
620        approved_trust_roots = []
621        for (aident, trust_root) in self.approved.keys():
622            if aident == ident:
623                trs = '<li><tt>%s</tt></li>\n' % cgi.escape(trust_root)
624                approved_trust_roots.append(trs)
625
626        if approved_trust_roots:
627            prepend = '<p>Approved trust roots:</p>\n<ul>\n'
628            approved_trust_roots.insert(0, prepend)
629            approved_trust_roots.append('</ul>\n')
630            msg = ''.join(approved_trust_roots)
631        else:
632            msg = ''
633
634        self._showPage(200, 'An Identity Page',head_extras=disco_tags, msg='''\
635        <p>This is an identity page for %s.</p>
636        %s
637        ''' % (ident, msg))
638       
639
640    def _showPage(self, response_code, title,
641                 head_extras='', msg=None, err=None, form=None):
642
643        if self.user is None:
644            user_link = '<a href="/login">not logged in</a>.'
645        else:
646            user_link = 'logged in as <a href="%s/%s">%s</a>.<br />'\
647                        '<a href="%s?submit=true&'\
648                        'success_to=%s">Log out</a>' % \
649                        (self.paths['path_id'], self.user, self.user, 
650                         self.paths['path_loginsubmit'],
651                         self.paths['path_login'])
652
653        body = ''
654
655        if err is not None:
656            body +=  '''\
657            <div class="error">
658              %s
659            </div>
660            ''' % err
661
662        if msg is not None:
663            body += '''\
664            <div class="message">
665              %s
666            </div>
667            ''' % msg
668
669        if form is not None:
670            body += '''\
671            <div class="form">
672              %s
673            </div>
674            ''' % form
675
676        contents = {
677            'title': 'Python OpenID Server Example - ' + title,
678            'head_extras': head_extras,
679            'body': body,
680            'user_link': user_link,
681            }
682
683        response = '''<html>
684  <head>
685    <title>%(title)s</title>
686    %(head_extras)s
687  </head>
688  <style type="text/css">
689      h1 a:link {
690          color: black;
691          text-decoration: none;
692      }
693      h1 a:visited {
694          color: black;
695          text-decoration: none;
696      }
697      h1 a:hover {
698          text-decoration: underline;
699      }
700      body {
701        font-family: verdana,sans-serif;
702        width: 50em;
703        margin: 1em;
704      }
705      div {
706        padding: .5em;
707      }
708      table {
709        margin: none;
710        padding: none;
711      }
712      .banner {
713        padding: none 1em 1em 1em;
714        width: 100%%;
715      }
716      .leftbanner {
717        text-align: left;
718      }
719      .rightbanner {
720        text-align: right;
721        font-size: smaller;
722      }
723      .error {
724        border: 1px solid #ff0000;
725        background: #ffaaaa;
726        margin: .5em;
727      }
728      .message {
729        border: 1px solid #2233ff;
730        background: #eeeeff;
731        margin: .5em;
732      }
733      .form {
734        border: 1px solid #777777;
735        background: #ddddcc;
736        margin: .5em;
737        margin-top: 1em;
738        padding-bottom: 0em;
739      }
740      dd {
741        margin-bottom: 0.5em;
742      }
743  </style>
744  <body>
745    <table class="banner">
746      <tr>
747        <td class="leftbanner">
748          <h1><a href="/">Python OpenID Server Example</a></h1>
749        </td>
750        <td class="rightbanner">
751          You are %(user_link)s
752        </td>
753      </tr>
754    </table>
755%(body)s
756  </body>
757</html>
758''' % contents
759
760        self.start_response('%s %s' % (response_code, 
761                                       httplib.responses[response_code]), 
762                [
763                    ('Content-type', 'text/html'+self.charset),
764                    ('Content-length', str(len(response)))
765                ]
766            )
767        return [response]
768
769
770from authkit.authenticate import middleware, AuthKitConfigError, strip_base
771from authkit.authenticate.multi import MultiHandler, status_checker
772
773def make_handler(app,
774    auth_conf,
775    app_conf=None,
776    global_conf=None,
777    prefix='authkit.method.openidprovider.'):
778   
779    app = MultiHandler(app)
780    app.add_method('openidprovider', Handler)
781    app.add_checker('openidprovider', status_checker)
782    return app
Note: See TracBrowser for help on using the repository browser.