2010-08-01 13 views
6

Quiero ocultar selectivamente algunos recursos en función de alguna forma de autenticación en web.py, pero su existencia se revela en 405 respuestas a cualquier método HTTP que no haya implementado.web.py: ¿Cómo ocultar selectivamente los recursos con 404s para cualquier método HTTP?

He aquí un ejemplo:

import web 

urls = (
    '/secret', 'secret', 
    ) 

app = web.application(urls, globals()) 

class secret(): 
    def GET(self): 
     if web.cookies().get('password') == 'secretpassword': 
      return "Dastardly secret plans..." 
     raise web.notfound() 

if __name__ == "__main__": 
    app.run() 

Cuando una solicitud de método indefinido se emite, el recurso se reveló:

$ curl -v -X DELETE http://localhost:8080/secret 
... 
> DELETE /secret HTTP/1.1 
... 
< HTTP/1.1 405 Method Not Allowed 
< Content-Type: text/html 
< Allow: GET 
... 

pude aplicar la misma comprobación de los otros métodos comunes en la especificación HTTP , pero un cretino creativo podría inventar el suyo:

$ curl -v -X SHENANIGANS http://localhost:8080/secret 
... 
> SHENANIGANS /secret HTTP/1.1 
... 
< HTTP/1.1 405 Method Not Allowed 
< Content-Type: text/html 
< Allow: GET 
... 

Es Hay una forma de implementar un método catch all en una clase web.py para cualquier método HTTP, así puedo asegurarme de que se ejecute la verificación de seguridad.

¿O existe una forma alternativa de ocultar estos recursos?

Respuesta

4

Iluminado por la respuesta de Daniel Kluev, terminé derivada de web.application para añadir soporte para un método por defecto en el _delegate método:

import types 

class application(web.application): 
    def _delegate(self, f, fvars, args=[]): 
     def handle_class(cls): 
      meth = web.ctx.method 
      if meth == 'HEAD' and not hasattr(cls, meth): 
       meth = 'GET' 
      if not hasattr(cls, meth): 
       if hasattr(cls, '_default'): 
        tocall = getattr(cls(), '_default') 
        return tocall(*args) 
       raise web.nomethod(cls) 
      tocall = getattr(cls(), meth) 
      return tocall(*args) 

     def is_class(o): return isinstance(o, (types.ClassType, type)) 
     ... 

de instancias:

app = application(urls, globals()) 

clase de página:

class secret(): 
    def _default(self): 
     raise web.notfound() 

    def GET(self): 
     ... 

Prefiero esta solución porque mantiene las clases de página limpias y permite una mayor personalización del proceso de delegación en un solo lugar. Por ejemplo, otra de las características que quería era transparente POSTAL sobrecargado (por ejemplo redirigir una solicitud POST con method=DELETE al método Delete de la clase de página.) Y es fácil de añadir que también aquí:

  ... 
      meth = web.ctx.method 
      if meth == 'POST' and 'method' in web.input(): 
       meth = web.input()['method'] 
      ... 
0

puede definir cualquier método en la clase de 'secreto', como Borrar o travesuras, como esto:

class secret(): 

    def DELETE(self): 
     ... 

    def SHENANIGANS(self): 
     ... 
+1

Un atacante puede inventar cualquier método nombre que les gusta. Si comienzo a definir los métodos para cada posibilidad, voy a extrañar mi fecha límite :) –

1

Puede implementar el método de la manija-all-métodos de la siguiente manera:

class HelloType(type): 
    """Metaclass is needed to fool hasattr(cls, method) check""" 
    def __getattribute__(obj, name): 
     try: 
      return object.__getattribute__(obj, name) 
     except AttributeError: 
      return object.__getattribute__(obj, '_handle_unknown')   

class hello(object): 
    __metaclass__ = HelloType 
    def GET(self, *args, **kw): 
     if web.cookies().get('password') == 'secretpassword': 
      return "Dastardly secret plans..." 
     raise web.notfound() 

    def _handle_unknown(self, *args, **kw): 
     """This method will be called for all requests, which have no defined method""" 
     raise web.notfound() 

    def __getattribute__(obj, name): 
     try: 
      return object.__getattribute__(obj, name) 
     except AttributeError: 
      return object.__getattribute__(obj, '_handle_unknown') 

__getattribute__ se implementa dos veces debido a los controles manera web.py para la existencia método:

def _delegate(self, f, fvars, args=[]): 
    def handle_class(cls): 
     meth = web.ctx.method 
     if meth == 'HEAD' and not hasattr(cls, meth): 
      meth = 'GET' 
     if not hasattr(cls, meth): # Calls type's __getattribute__ 
      raise web.nomethod(cls) 
     tocall = getattr(cls(), meth) # Calls instance's __getattribute__ 
+0

Muchas gracias por esta explicación clara y útil. Decidí usar un método diferente con el que me sentía más cómodo, ¡pero no tenía idea de dónde empezar antes de leer su respuesta! –

Cuestiones relacionadas