Changeset 4809

Show
Ignore:
Timestamp:
06/24/08 11:52:44 (5 months ago)
Author:
faide
Message:

This is the first part of the implementation for the SecureController?. The rest will be in the quickstart template.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/tg/controllers.py

    r4761 r4809  
    33 
    44  DecoratedController allows the decorators in tg.decorators to work 
    5    
     5 
    66  ObjectDispatchController is a specialised form of DecoratedController that 
    77  converts URL portions into traversing Python objects.  This controller is 
    88  usable in plain pylons if you route to it's "routes_placeholder" method 
    9    
     9 
    1010  TGController is a specialised form of ObjectDispatchController that forms the 
    1111  basis of standard TurboGears controllers.  The "Root" controller of a standard 
     
    2020import pylons 
    2121from pylons.controllers import WSGIController 
    22  
    2322from pylons.controllers.util import abort 
    2423 
    25 from tg.exceptions import HTTPFound, HTTPNotFound, HTTPException 
     24from tg.exceptions import HTTPFound, HTTPNotFound, HTTPException, HTTPClientError 
    2625from tw.api import Widget 
     26from webob.exc import HTTPForbidden 
    2727 
    2828log = logging.getLogger(__name__) 
     
    4545    The decorators in tg.decorators create an attribute named 'decoration' on 
    4646    the controller method, creating rules as to: 
    47      
     47 
    4848    1) how to validate the request, 
    4949    2) how to render the response, 
    5050    3) allowing hooks to be registered to happen: 
    51          
     51 
    5252        a) before validation 
    5353        b) before the controller method is called 
     
    7171 
    7272        The before_render hook provides a place for functions that are called 
    73         before the template is rendered. For example, you could use it to  
     73        before the template is rendered. For example, you could use it to 
    7474        add and remove from the dictionary returned by the controller method, 
    7575        before it is passed to rendering. 
     
    7878        rendering. 
    7979        """ 
    80          
     80 
    8181        self._initialize_validation_context() 
    8282 
     
    100100 
    101101        except formencode.api.Invalid, inv: 
    102             controller, output = self._handle_validation_errors(controller,  
     102            controller, output = self._handle_validation_errors(controller, 
    103103                                                                remainder, 
    104104                                                                params, inv) 
     
    135135        if validation is None: 
    136136            return params 
    137          
     137 
    138138        #Initialize new_params -- if it never gets updated just return params 
    139139        new_params = {} 
    140          
     140 
    141141        # The validator may be a dictionary, a FormEncode Schema object, or any 
    142142        # object with a "validate" method. 
     
    166166                    formencode.schema.format_compound_error(errors), 
    167167                    params, None, error_dict=errors) 
    168              
     168 
    169169        elif isinstance(validation.validators, formencode.Schema): 
    170170            # A FormEncode Schema object - to_python converts the incoming 
    171171            # parameters to sanitized Python values 
    172172            new_params = validation.validators.to_python(params) 
    173              
     173 
    174174        elif hasattr(validation.validators, 'validate'): 
    175175            # An object with a "validate" method - call it with the parameters 
    176176            new_params = validation.validators.validate(params) 
    177          
     177 
    178178        # Theoretically this should not happen... 
    179179        if new_params is None: 
    180180            return params 
    181              
     181 
    182182        return new_params 
    183183 
     
    204204 
    205205        # Always set content type 
    206         pylons.response.headers['Content-Type'] = content_type  
     206        pylons.response.headers['Content-Type'] = content_type 
    207207        req = pylons.request 
    208208 
     
    218218                    warnings.warn(msg, DeprecationWarning) 
    219219                    setattr(pylons.c.w, key, item) 
    220          
     220 
    221221        # Prepare the engine, if it's not already been prepared. 
    222222        if engine_name not in _configured_engines(): 
     
    228228        #if there is an identity, push it to the pylons template context 
    229229        pylons.tmpl_context.identity = pylons.request.environ.get('repoze.who.identity') 
    230              
     230 
    231231        # Setup the template namespace, removing anything that the user 
    232232        # has marked to be excluded. 
     
    262262 
    263263        pylons.c.validation_exception = exception 
    264         pylons.c.form_errors = {}  
    265          
     264        pylons.c.form_errors = {} 
     265 
    266266        # Most Invalid objects come back with a list of errors in the format: 
    267267        #"fieldname1: error\nfieldname2: error" 
     
    272272            field_value = error.split(':') 
    273273 
    274             #if the error has no field associated with it,  
     274            #if the error has no field associated with it, 
    275275            #return the error as a global form error 
    276276            if len(field_value) == 1: 
     
    279279 
    280280            pylons.c.form_errors[field_value[0]] = field_value[1].strip() 
    281              
     281 
    282282        pylons.c.form_values = exception.value 
    283283 
     
    292292 
    293293        return error_handler, output 
    294          
     294 
    295295    def _initialize_validation_context(self): 
    296296        pylons.c.form_errors = {} 
     
    349349            url_path = pylons.request.path.split('/')[1:] 
    350350        else: 
    351             url_path = url.split('/')  
    352          
     351            url_path = url.split('/') 
     352 
    353353        controller, remainder = _object_dispatch(self, url_path) 
    354354        # XXX Place controller url at context temporarily... we should be 
     
    364364    def _perform_call(self, func, args): 
    365365        controller, remainder, params = self._get_routing_info(args.get('url')) 
    366         return DecoratedController._perform_call(self, controller, params, 
    367                                                 remainder=remainder) 
    368  
    369     def routes_placeholder(self, url='/', start_response=None, **kwargs):         
     366        return DecoratedController._perform_call( 
     367            self, controller, params, remainder=remainder) 
     368 
     369    def routes_placeholder(self, url='/', start_response=None, **kwargs): 
    370370        """ 
    371371        This function does not do anything.  It is a placeholder that allows 
     
    383383            obj, remainder = _find_object(obj, remainder, notfound_handlers) 
    384384            return obj, remainder 
     385 
     386        # identity error should be treated separatly from "not found" errors 
     387        except HTTPForbidden, httpe: 
     388            log.debug("a 403 error occured for obj: %s" % obj) 
     389            raise 
     390 
    385391        except HTTPException: 
    386392            if not notfound_handlers: 
    387393                raise HTTPNotFound().exception 
     394 
    388395            name, obj, remainder = notfound_handlers.pop() 
    389396            if name == 'default': 
    390397                return obj, remainder 
     398 
    391399            else: 
    392400                obj, remainder = obj(*remainder) 
     
    397405        if obj is None: 
    398406            raise HTTPNotFound().exception 
     407 
     408        _check_security(obj) 
     409 
    399410        if _iscontroller(obj): 
    400411            return obj, remainder 
     
    415426        if not remainder: 
    416427            raise HTTPNotFound().exception 
     428 
    417429        obj = getattr(obj, remainder[0], None) 
    418430        remainder = remainder[1:] 
     431 
     432def _check_security(obj): 
     433    """this function checks if a controller has a 'require' attribute and if 
     434    it is the case, test that this require predicate can be evaled to True. 
     435    It will raise a Forbidden exception if the predicate is not valid. 
     436    """ 
     437    if hasattr(obj, "im_self"): 
     438        klass_instance = obj.im_self 
     439    else: 
     440        klass_instance = obj 
     441 
     442    if hasattr(klass_instance, "check_security"): 
     443        if not klass_instance.check_security(): 
     444            raise HTTPForbidden().exception 
     445            #abort(403) 
    419446 
    420447def _iscontroller(obj): 
     
    429456    An ObjectDispatchController-derived class for stock-standard TurboGears 
    430457    controllers. 
    431      
    432     This controller can be used as a baseclass for anything in the  
     458 
     459    This controller can be used as a baseclass for anything in the 
    433460    object dispatch tree, but it MUST be used in the Root controller 
    434     ad any controller which you intend to do object dispatch from 
     461    and any controller which you intend to do object dispatch from 
    435462    using Routes. 
    436463    """ 
    437      
     464 
    438465    def _perform_call(self, func, args): 
    439466        setup_i18n() 
    440467        routingArgs = None 
    441          
     468 
    442469        if isinstance(args, dict) and 'url' in args: 
    443470            routingArgs = args['url'] 
    444              
     471 
    445472        try: 
    446473            controller, remainder, params = self._get_routing_info(routingArgs) 
    447474            result = DecoratedController._perform_call( 
    448475                self, controller, params, remainder=remainder) 
     476 
    449477        except HTTPException, httpe: 
    450478            result = httpe 
     
    467495    if not params: 
    468496        params = {} 
    469      
     497 
    470498    curent_url = pylons.request.url 
    471499    url = urlparse.urljoin(curent_url, url) 
     
    476504        url = url.encode('utf8') 
    477505    found = HTTPFound(location=url).exception 
    478      
     506 
    479507    #TODO: Make this work with WebOb 
    480      
     508 
    481509    ## Merging cookies and headers from global response into redirect 
    482510    #for header in pylons.response.headerlist: 
     
    495523        app_root = pylons.request.application_url[len(pylons.request.host_url):] 
    496524        tgpath = app_root + tgpath 
    497         tgpath = pylons.config.get("server.webpath", "") + tgpath  
     525        tgpath = pylons.config.get("server.webpath", "") + tgpath 
    498526        result = tgpath 
    499527    else: 
    500         result = tgpath  
     528        result = tgpath 
    501529 
    502530    if tgparams is None: 
     
    525553        result += "?" + "&".join(args) 
    526554    return result 
    527      
     555 
    528556 
    529557def setup_i18n(): 
     
    543571            set_lang(languages[0]) 
    544572            log.info("Set request language to %s", languages[0]) 
    545 __all__ = [  
    546     "DecoratedController", "ObjectDispatchController", "TGController",  
     573__all__ = [ 
     574    "DecoratedController", "ObjectDispatchController", "TGController", 
    547575    "url", "redirect" 
    548576    ]