Changeset 88

Show
Ignore:
Timestamp:
06/13/07 20:58:55
Author:
bbangert
Message:

Cleaned up SSO API, switched SSO API to use middleware + handler for proper intercepting of return redirects. CAS 2.0 now using proxyValidate as it'll validate proxy tickets AND service tickets.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • AuthKit/branches/0.4/authkit/authenticate/sso/__init__.py

    r78 r88  
    2525SSO schemes like LID may also be supported. 
    2626 
    27  
     27These systems sub-class the ``RedirectingAuthMiddleware`` from the api package 
     28as they all utilize a similar scheme of authentcation via redirection with 
     29back-end verification. 
    2830 
    2931.. note:: 
  • AuthKit/branches/0.4/authkit/authenticate/sso/api.py

    r84 r88  
    2020 
    2121from authkit.authenticate.multi import MultiHandler, status_checker 
     22from authkit.authenticate import AuthKitConfigError 
    2223 
    2324class LoginFailure(HTTPForbidden): 
     
    3233    def __call__(self, environ, start_response): 
    3334        request = WSGIRequest(environ) 
    34         url = self.redirect_url(self, environ) 
    35         return HTTPSeeOther().wsgi_application(environ, start_response) 
     35        url = self.redirect_url(environ) 
     36        return HTTPSeeOther(url).wsgi_application(environ, start_response) 
    3637 
    3738class RedirectingAuthMiddleware(object): 
     
    9091    tempref = app 
    9192    while not isinstance(tempref, MultiHandler): 
    92         tempref = getattr(cur_app, 'app', getattr(cur_app, 'application', None)) 
     93        tempref = getattr(tempref, 'app', getattr(tempref, 'application', None)) 
    9394        if tempref is None: 
    9495            app = MultiHandler(app) 
  • AuthKit/branches/0.4/authkit/authenticate/sso/cas.py

    r78 r88  
    55service tickets. 
    66 
     7Note that this does not implement proxy granting ticket requests, merely 
     8CAS 2.0 service validation (which can take proxy tickets). 
    79 
    810`Protocol Reference <http://www.ja-sig.org/products/cas/overview/protocol/index.html>`_ 
    911""" 
     12import logging 
    1013import urllib 
    11 from elementtree import ElementTree 
    1214 
    13 from paste.request import construct_url 
    14 from paste.util.converters import asbool 
     15from authkit.authenticate.sso.api import * 
    1516 
    16 from authkit.authenticate.sso.api import RedirectingAuthMiddleware 
    17 from authkit.authenticate.multi import MultiHandler, status_checker 
     17log = logging.getLogger('authkit.authenticate.sso.cas') 
    1818 
    19 class AuthCASHandler(RedirectingAuthMiddleware): 
     19class AuthCASHandler(RedirectingAuthHandler): 
     20    """CAS 1.0 and 2.0 Redirect Handler 
     21     
     22    This small middleware piece handles generating the redirect URL for the 
     23    CAS  
     24    """ 
     25    def __init__(self, app, authority, path='casauth', use_cas2=False): 
     26        self.app = app 
     27        self.authority = authority 
     28        self.path = path 
     29        self._cas2 = use_cas2 
     30     
     31    def redirect_url(self, environ): 
     32        kwargs = {'service': construct_url(environ, script_name='',  
     33                                           path_info=self.path + '/verify')} 
     34         
     35        # XXX TODO: Store this for the middleware below, also look for a proxy 
     36        #           granting ticket option to store that its desired 
     37        if 'authkit.cas.renew' in environ: 
     38            kwargs['renew'] = 'true' 
     39        if 'authkit.cas.gateway' in environ: 
     40            kwargs['gateway'] = 'true' 
     41             
     42            # renew and gateway should not both be set at once according to 
     43            # CAS protocol 
     44            assert 'renew' not in kwargs 
     45         
     46        args = urllib.urlencode(kwargs) 
     47        return self.authority + "login?" + args 
     48 
     49class AuthCASMiddleware(RedirectingAuthMiddleware): 
    2050    """CAS 1.0 and 2.0 Capable Authentication Handler""" 
    2151    def __init__(self, app, authority, use_cas2=False): 
     
    2757        else: 
    2858            self._authtype = 'CAS 1.0' 
    29      
    30     def do_verify(self, req): 
    31         return req.GET.get('ticket') 
    32      
    33     def verify(self, req): 
     59        self.dispatch = {'/verify':'verify'} 
     60         
     61    def verify(self, environ, start_response): 
     62        req = WSGIRequest(environ) 
     63        if 'ticket' not in req.GET: 
     64            return HTTPNotFound().wsgi_application(environ, start_response) 
     65         
    3466        ticket = req.GET['ticket'] 
     67         
    3568        service = construct_url(environ) 
    36         kwargs = {'service': service,'ticket': ticket} 
     69        kwargs = {'service': service, 'ticket':ticket} 
    3770        if req.environ.get('authkit.sso.cas.renew'): 
    3871            kwargs['renew'] = 'true' 
    3972        args = urllib.urlencode(kwargs) 
     73         
     74        # XXX TODO: Store whether renew was used for this request to ensure 
     75        #           that the validation asks for it as well 
     76        #           Also store whether a proxy ticket was requested and ask for 
     77        #           it during validation 
    4078        if self._cas2: 
    41             requrl = self.authority + "serviceValidate?" + args 
     79            # We use proxyValidate for CAS 2.0 because it will handle both 
     80            # service and proxy ticket validation 
     81            requrl = self.authority + "proxyValidate?" + args 
    4282            response = urllib.urlopen(requrl).read() 
    4383            tree = ElementTree.fromstring(response) 
     
    4686            if success: 
    4787                results['user'] = tree[0][0].text 
     88                results['extra_environ'] = {} 
     89                 
     90                # Did we get a proxy ticket? 
     91                if len(tree[0]) > 1 and tree[0][1].tag.endswith('proxyGrantingTicket'): 
     92                    results['authkit.cas.proxyTicket'] = tree[0][1].text.strip() 
     93                     
     94                    # Any proxies returned as well? 
     95                    if len(tree[0] > 2): 
     96                        proxies = [x.text.strip() for x in tree[0][2]] 
     97                        results['authkit.cas.proxies'] = proxies 
     98            else: 
     99                log.info('Authentication failed for auth: %s, ticket %s, ' 
     100                         'response: %s', self._authtype, ticket,  
     101                         tree[0].attrib['code']) 
    48102        else: 
    49103            requrl = self.authority + "validate?" + args 
     
    53107            if valid: 
    54108                results['user'] = result[1] 
    55         return valid, results 
    56      
    57     def redirect_url(self, req): 
    58         args = urllib.urlencode({'service': construct_url(environ)}) 
    59         return self.authority + "login?" + args 
     109            else: 
     110                log.info('Authentication failed for auth: %s, ticket %s, ' 
     111                         'response: %s', self._authtype, ticket, result[0]) 
     112         
     113        if not valid: 
     114            return LoginFailure().wsgi_application(environ, start_response) 
     115        environ['AUTH_TYPE'] = self._authtype 
     116        environ['REMOTE_USER'] = results['user'] 
     117         
     118        # Add in optional environ data from the auth system 
     119        if 'extra_environ' in results: 
     120            environ.update(results['extra_environ']) 
     121         
     122        return self.app(environ, start_response) 
    60123 
    61124def make_cas_handler(app, auth_conf, app_conf=None, global_conf=None, 
    62125                     prefix='authkit.sso.cas'): 
    63     if not auth_conf.has_key('internalpath'): 
    64         raise AuthKitConfigError("No %sinternalpath key specified"%prefix) 
    65     app = MultiHandler(app) 
    66     cas2 = asbool(auth_conf.get('use_cas2', False)) 
    67     app.add_method('cas', AuthCASHandler, auth_conf['authority'], cas2) 
    68     app.add_checker('cas', status_checker) 
     126    if 'authority' not in auth_conf: 
     127        raise AuthKitConfigError("No %sauthority key specified" % prefix) 
     128    kwargs = dict(authority=auth_conf['authority']) 
     129    if 'use_cas2' in auth_conf: 
     130        kwargs['use_cas2'] = True 
     131    if 'path' in auth_conf: 
     132        kwargs['path'] = auth_conf['path'] 
     133     
     134    multi_app, app = find_multi_app(app) 
     135    multi_app.add_method('cas', AuthCASHandler, **kwargs) 
     136    multi_app.add_checker('cas', status_checker) 
     137     
     138    app = AuthCASMiddleware(app, **kwargs) 
    69139    return app