Changeset 136

Show
Ignore:
Timestamp:
11/06/07 00:37:54
Author:
thejimmyg
Message:

Added some updates to support OpenID 2

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • AuthKit/trunk/authkit/authenticate/open_id.py

    r114 r136  
    1818This middleware actually sets two cookies, one for AuthKit and one for a session 
    1919store to store the ID which OpenID information is keyed against. 
     20 
     21The OpenID sreg variables you can use include: 
     22 
     23``nickname`` 
     24    Any UTF-8 string that the End User wants to use as a nickname.  
     25 
     26``email`` 
     27    The email address of the End User as specified in section 3.4.1 of 
     28    [RFC2822] (Resnick, P., "Internet Message Format," .).  
     29 
     30``fullname`` 
     31    UTF-8 string free text representation of the End User's full name.  
     32 
     33``dob`` 
     34    The End User's date of birth as YYYY-MM-DD. Any values whose representation 
     35    uses fewer than the specified number of digits should be zero-padded. The 
     36    length of this value MUST always be 10. If the End User user does not want 
     37    to reveal any particular component of this value, it MUST be set to zero. 
     38    For instance, if a End User wants to specify that his date of birth is in 
     39    1980, but not the month or day, the value returned SHALL be "1980-00-00". 
     40 
     41``gender`` 
     42    The End User's gender, "M" for male, "F" for female.  
     43 
     44``postcode`` 
     45    UTF-8 string free text that SHOULD conform to the End User's country's 
     46    postal system.  
     47 
     48``country`` 
     49    The End User's country of residence as specified by ISO3166.  
     50 
     51``language`` 
     52    End User's preferred language as specified by ISO639.  
     53 
     54``timezone`` 
     55    ASCII string from TimeZone database 
     56    For example, "Europe/Paris" or "America/Los_Angeles". 
    2057""" 
    2158 
     
    2764from paste.request import construct_url 
    2865from openid.consumer import consumer 
    29 from openid.oidutil import appendArgs 
    30 from yadis.discover import DiscoveryFailure 
    31 from urljr.fetchers import HTTPFetchingError 
     66from openid import sreg 
     67from openid.cryptutil import randomString 
    3268from authkit.authenticate import get_template, valid_password, \ 
    3369   get_authenticate_function, strip_base, RequireEnvironKey, \ 
    3470   AuthKitUserSetter, AuthKitAuthHandler 
    3571from authkit.authenticate.multi import MultiHandler, status_checker 
    36 from beaker.middleware import SessionMiddleware 
    3772 
    3873def template(): 
     
    116151        from openid.store.sqlstore import MySQLStore 
    117152        from sqlalchemy.engine.url import make_url 
    118          
     153 
    119154        def create_conn(dburi): 
    120155            url = make_url(dburi) 
     
    134169        raise Exception("Invalid store type %r"%store) 
    135170    return conn, cstore 
    136      
     171 
    137172class AuthOpenIDHandler: 
    138173    """ 
     
    148183        path_signedin,  
    149184        template=None, 
    150         session_secret=None, 
    151         session_key='authkit_openid', 
    152185        session_middleware='beaker.session', 
    153186        path_verify='/verify',  
     
    166199        self.path_process = path_process 
    167200        self.session_middleware = session_middleware 
    168         self.session_key = session_key 
    169         self.session_secret = session_secret 
    170201        self.app = app 
    171202        self.urltouser = urltouser 
     
    218249                [ 
    219250                    ('Content-type', 'text/html'+self.charset), 
    220                     ('Content-length', len(response)) 
     251                    ('Content-length', str(len(response))) 
    221252                ] 
    222253            ) 
     
    225256        try: 
    226257            request_ = oidconsumer.begin(openid_url) 
    227         except HTTPFetchingError, exc: 
    228             response = render( 
    229                 self.template, 
    230                 message='Error retrieving identity URL: %s' % ( 
    231                     cgi.escape(str(exc.why)) 
    232                 ), 
    233                 value=self._quoteattr(openid_url), 
    234                 css_class='error', 
    235                 action=baseurl + self.path_verify 
    236             ) 
    237             start_response( 
    238                 '200 OK',  
    239                 [ 
    240                     ('Content-type', 'text/html'+self.charset), 
    241                     ('Content-length', len(response)) 
    242                 ] 
    243             ) 
    244             return response 
    245         except DiscoveryFailure, exc: 
     258        except consumer.DiscoveryFailure, exc: 
    246259            response = render( 
    247260                self.template, 
     
    257270                [ 
    258271                    ('Content-type', 'text/html'+self.charset), 
    259                     ('Content-length', len(response)) 
     272                    ('Content-length', str(len(response))) 
    260273                ] 
    261274            ) 
     
    276289                    [ 
    277290                        ('Content-type', 'text/html'+self.charset), 
    278                         ('Content-length', len(response)) 
     291                        ('Content-length', str(len(response))) 
    279292                    ] 
    280293                ) 
    281294                return response 
    282295            else: 
     296                session = environ[self.session_middleware] 
     297                if 'HTTP_REFERER' in environ and \ 
     298                        not environ['HTTP_REFERER'].endswith(self.path_verify) and \ 
     299                        not environ['HTTP_REFERER'].endswith(self.path_process): 
     300                    session['referer'] = environ['HTTP_REFERER'] 
     301 
     302                if self.sreg_required or self.sreg_optional or self.sreg_policyurl: 
     303                    required_list = [] 
     304                    if self.sreg_required: 
     305                        required_list = [opt.strip() for opt in self.sreg_required.split(',')] 
     306                    optional_list = [] 
     307                    if self.sreg_optional: 
     308                        optional_list = [opt.strip() for opt in self.sreg_optional.split(',')] 
     309                    sreg_request = sreg.SRegRequest( 
     310                        required=required_list, 
     311                        optional=optional_list, 
     312                        policy_url=self.sreg_policyurl) 
     313                    request_.addExtension(sreg_request) 
     314 
    283315                trust_root = baseurl 
    284316                return_to = baseurl + self.path_process 
    285                 if self.sreg_required: 
    286                     request_.addExtensionArg('sreg', 'required', self.sreg_required) 
    287                 if self.sreg_optional: 
    288                     request_.addExtensionArg('sreg', 'optional', self.sreg_optional) 
    289                 if self.sreg_policyurl: 
    290                     request_.addExtensionArg('sreg', 'policy_url', self.sreg_policyurl) 
    291  
    292                 redirect_url = request_.redirectURL(trust_root, return_to) 
    293                 start_response( 
    294                     '301 Redirect',  
    295                     [ 
    296                         ('Content-type', 'text/html'+self.charset), 
    297                         ('Location', redirect_url) 
    298                     ] 
    299                 ) 
    300                 return [] 
     317 
     318                if request_.shouldSendRedirect(): 
     319                    redirect_url = request_.redirectURL( 
     320                        trust_root, return_to) 
     321 
     322                    start_response( 
     323                        '301 Redirect',  
     324                        [ 
     325                            ('Content-type', 'text/html'+self.charset), 
     326                            ('Location', redirect_url) 
     327                        ] 
     328                    ) 
     329                    return [] 
     330                else: 
     331                    # This gets called with sites such as myopenid.com 
     332                    form_html = request_.formMarkup( 
     333                        trust_root, return_to, 
     334                        form_tag_attrs={'id':'openid_message'}) 
     335                    content = """\ 
     336                        <html><head><title>OpenID transaction in progress</title></head> 
     337                        <body onload='document.getElementById("%s").submit()'> 
     338                        %s 
     339                        </body></html> 
     340                    """%('openid_message', form_html) 
     341                    start_response( 
     342                        "200 OK", 
     343                        [ 
     344                            ('Content-Type', 'text/html'+self.charset), 
     345                            ('Content-Length', str(len(content))) 
     346                        ] 
     347                    ) 
     348                    return [content] 
    301349 
    302350    def process(self, environ, start_response): 
     
    309357        css_class = 'error' 
    310358        message = '' 
     359 
    311360        params = dict(paste.request.parse_querystring(environ)) 
    312361        oidconsumer = self._get_consumer(environ) 
    313362        info = oidconsumer.complete(dict(params)) 
    314         css_class = 'error' 
     363 
    315364        if info.status == consumer.FAILURE and info.identity_url: 
    316365            fmt = "Verification of %s failed." 
     
    321370        elif info.status == consumer.SUCCESS: 
    322371            username = info.identity_url 
    323             user_data = str(info.extensionResponse( 'sreg' )) 
     372            if info.endpoint.canonicalID: 
     373                username = cgi.escape(info.endpoint.canonicalID) 
     374            user_data = str(sreg.SRegResponse.fromSuccessResponse(info).getExtensionArgs()) 
    324375            # Set the cookie 
    325376            if self.urltouser: 
     
    327378            environ['paste.auth_tkt.set_user'](username, user_data=user_data) 
    328379            # Return a page that does a meta refresh 
     380            session = environ[self.session_middleware] 
     381            if 'referer' in session: 
     382                redirect_url = session.pop('referer') 
     383            else: 
     384                redirect_url = self.baseurl + self.path_signedin 
     385 
    329386            response = """ 
    330387<HTML> 
     
    337394</BODY> 
    338395</HTML> 
    339             """ % (self.baseurl + self.path_signedin
     396            """ % (redirect_url
    340397            start_response( 
    341398                '200 OK',  
    342399                [ 
    343400                    ('Content-type', 'text/html'+self.charset), 
    344                     ('Content-length', len(response)) 
     401                    ('Content-length', str(len(response))) 
    345402                ] 
    346403            ) 
     
    348405        elif info.status == consumer.CANCEL: 
    349406            message = 'Verification cancelled' 
     407        elif info.status == consumer.SETUP_NEEDED: 
     408            if info.setup_url: 
     409                message = '<a href=%s>Setup needed</a>' % ( 
     410                    self._quoteattr(info.setup_url),) 
     411            else: 
     412                message = 'Setup needed' 
    350413        else: 
    351414            environ['wsgi.errors'].write("Passurl Message: %s"%info.message) 
     
    363426            [ 
    364427                ('Content-type', 'text/html'+self.charset), 
    365                 ('Content-length', len(response)) 
     428                ('Content-length', str(len(response))) 
    366429            ] 
    367430        ) 
     
    375438        session = environ[self.session_middleware] 
    376439        session['id'] = session.id 
    377         idconsumer = consumer.Consumer(session, self.store) 
     440        oidconsumer = consumer.Consumer(session, self.store) 
     441        oidconsumer.consumer.openid1_nonce_query_arg_name = 'passurl_nonce' 
    378442        session.save() 
    379         return idconsumer 
     443        return oidconsumer 
    380444 
    381445    def _quoteattr(self, s): 
     
    384448        qs = cgi.escape(s, 1) 
    385449        return '"%s"' % (qs,) 
    386              
    387450 
    388451class OpenIDUserSetter(AuthKitUserSetter): 
     
    402465            sreg_policyurl=options['sreg_policyurl'], 
    403466        ) 
    404  
    405         if options['session_middleware'] == 'beaker.session': 
    406             app = SessionMiddleware( 
    407                 app,  
    408                 key=options['session_key'],  
    409                 secret=options['session_secret'] 
    410             ) 
    411467        self.app = app 
    412468 
    413469    def __call__(self, environ, start_response): 
    414470        return self.app(environ, start_response) 
    415  
    416471 
    417472def load_openid_config( 
     
    447502        'sreg_optional': auth_conf.get('sreg.optional'), 
    448503        'sreg_policyurl': auth_conf.get('sreg.policyurl'), 
    449         # XXX This need to actually be configurable, not hard coded 
    450         'session_secret': 'asdasd', 
    451         'session_key': 'authkit_openid', 
    452504        'session_middleware': 'beaker.session', 
    453505    } 
    454     if user_setter_params['session_middleware'] == 'beaker.session': 
    455         if not user_setter_params['session_secret']: 
    456             raise AuthKitConfigError('No session_secret set') 
    457506    auth_handler_params={ 
    458507        'template':user_setter_params['template'],