2010-02-12 16 views
11

Si quiero asegurarme de que una vista aparece como pública, ¿hay un decorador equivalente a @public_access que sería lo opuesto a @login_required y dejar en claro que la vista debe ser accesible públicamente siempre?¿Qué es lo opuesto a @login_required decorator for Django views?

Un caso de uso que tengo en mente es agregar automáticamente "@csrf_exempt" a todas las vistas públicas, además de dejar en claro en el código que la vista debe ser de acceso público.

Respuesta

10

Desafortunadamente, actualmente no hay soporte integrado para esto en Django, dejándolo en riesgo de exponer información confidencial cuando @login_required es olvidado accidentalmente.

Aquí es una solución de uno de mis proyectos:

middleware/security.py:

def public(function): 
    """Decorator for public views that do not require authentication 
    """ 
    orig_func = function 
    while isinstance(orig_func, partial): # if partial - use original function for authorization 
     orig_func = orig_func.func 
    orig_func.is_public_view = True 

    return function 

def is_public(function): 
    try:         # cache is found 
     return function.is_public_view 
    except AttributeError:     # cache is not found 
     result = function.__module__.startswith('django.') and not function.__module__.startswith('django.views.generic') # Avoid modifying admin and other built-in views 

     try:        # try to recreate cache 
      function.is_public_view = result 
     except AttributeError: 
      pass 

     return result 


class NonpublicMiddleware(object): 

    def process_view_check_logged(self, request, view_func, view_args, view_kwargs): 
     return 

    def process_view(self, request, view_func, view_args, view_kwargs): 
     while isinstance(view_func, partial): # if partial - use original function for authorization 
      view_func = view_func.func 

     request.public = is_public(view_func) 
     if not is_public(view_func): 
      if request.user.is_authenticated():  # only extended checks are needed 
       return self.process_view_check_logged(request, view_func, view_args, view_kwargs) 

      return self.redirect_to_login(request.get_full_path()) # => login page 

    def redirect_to_login(self, original_target, login_url=settings.LOGIN_URL): 
     return HttpResponseRedirect("%s?%s=%s" % (login_url, REDIRECT_FIELD_NAME, urlquote(original_target))) 

settings.py:

MIDDLEWARE_CLASSES = (
    #... 
    'middleware.security.NonpublicProfilefullMiddleware', 
    #... 
) 

y, por último, código de la vista:

from <projname>.middleware import publi 

@public 
def some_view(request): 
    #... 

# Login required is added automatically 
def some_private_view(request): 
    #... 

Además, es posible que desee consultar "Automatically decorating all views of a django project" publicación en el blog

+0

Creo que también es útil en algunos casos para restringir * * acceso a cierta funcionalidad si un usuario está conectado. Por ejemplo, no permiten una usuario registrado para completar un formulario de registro ... En este caso, puede usar una combinación de is_authenticated y is_anonymous: https://docs.djangoproject.com/en/dev/topics/auth/#authorization-for-anonymous-users – g33kz0r

0

"Iniciar sesión no requerido" es el valor predeterminado. Si desea anotar que una vista nunca debe estar restringida al inicio de sesión, debe hacerlo en la carpeta docstring.

5

Como se mencionó en un cartel anterior, no se requiere iniciar sesión por defecto.

Sin embargo, a veces es útil bloquear ciertas vistas de los usuarios que han iniciado sesión; por ejemplo, no tiene sentido que un usuario que ha iniciado sesión pueda usar la página de registro del sitio. En ese caso, se podría hacer algo como esto, con sede fuera del decorador login_required existente

from django.contrib.auth.decorators import user_passes_test 
from django.conf import settings 

LOGGED_IN_HOME = settings.LOGGED_IN_HOME 

def login_forbidden(function=None, redirect_field_name=None, redirect_to=LOGGED_IN_HOME): 
    """ 
    Decorator for views that checks that the user is NOT logged in, redirecting 
    to the homepage if necessary. 
    """ 
    actual_decorator = user_passes_test(
     lambda u: not u.is_authenticated(), 
     login_url=redirect_to, 
     redirect_field_name=redirect_field_name 
    ) 
    if function: 
     return actual_decorator(function) 
    return actual_decorator 
+0

Utilice "u.is_anonymous()" insted de "not u.is_authenticated()" . Consulte https://docs.djangoproject.com/en/dev/ref/contrib/auth/#django.contrib.auth.models.AnonymousUser para obtener más información. – Krozark

1

Un poco tarde, pero otra forma sencilla de hacer frente a este problema sería que depender de otra decorador y agregar una función lambda:

from django.contrib.auth.decorators import user_passes_test 

@user_passes_test(lambda u: u.is_anonymous) 
+0

Qué diablos: "las ediciones deben tener al menos 6 caracteres, ¿hay algo más que mejorar en esta publicación?" ¡Diablos, no! Solo faltan corchetes en 'u.is_anonymous', por lo que el ejemplo es incorrecto. – simplylizz

+0

El ejemplo es correcto y funciona. ¿Dónde quieres poner corchetes adicionales? –

+0

Oh, he comprobado los documentos. Tienes razón, es correcto, ya que django 1.10+ 'is_anonymous' se convirtió en un atributo. Pero para las versiones anteriores de Django, siempre te dará 'True', porque' is_anonymous' era el método. – simplylizz

1

@permission_classes ([permissions.AllowAny])

Cuestiones relacionadas