2010-02-12 14 views
11

Se voglio assicurarmi che una vista sia elencata come dotata di accesso pubblico, c'è un decoratore equivalente a @public_access che sarebbe l'opposto di @login_required e chiarire che la vista dovrebbe essere accessibile pubblicamente sempre?Qual è l'opposto di @login_required decorator per le viste Django?

Un caso d'uso che ho in mente è quello di aggiungere automaticamente "@csrf_exempt" per tutte le opinioni pubbliche oltre a rendere in chiaro nel codice che la vista dovrebbe essere accessibile al pubblico.

risposta

10

Purtroppo, non c'è attualmente alcun supporto integrato per questo in Django, lasciando a rischio di esporre informazioni sensibili quando @login_required è accidentalmente dimenticato.

Ecco una soluzione da uno dei miei progetti:

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', 
    #... 
) 

e, infine, codice della vista:

from <projname>.middleware import publi 

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

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

Inoltre, potresti voler dare un'occhiata al post del blog "Automatically decorating all views of a django project"

+0

penso che sia anche utile in alcuni casi per limitare l'* * accesso a determinate funzionalità se un utente è connesso. per esempio, non consentono una utente registrato per completare un modulo di registrazione ... In questo caso è possibile utilizzare una combinazione di is_authenticated e is_anonymous: https://docs.djangoproject.com/en/dev/topics/auth/#authorization-for-anonymous-users – g33kz0r

0

"Login non richiesto" è l'impostazione predefinita. Se si desidera annotare che una vista non deve mai essere limitata al login, è necessario farlo nella docstring.

5

Come un precedente poster menzionato, l'accesso non richiesto è l'impostazione predefinita.

Tuttavia, a volte è utile per bloccare certe viste da utenti registrati - per esempio, non ha senso per un utente connesso per essere in grado di utilizzare pagina di registrazione del sito. In tal caso, si potrebbe fare qualcosa di simile, in base al largo la login_required decoratore esistente

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

Usa "u.is_anonymous()" insted di "not u.is_authenticated()" . Vedere https://docs.djangoproject.com/en/dev/ref/contrib/auth/#django.contrib.auth.models.AnonymousUser per ulteriori informazioni. – Krozark

1

un po 'tardi, ma un altro modo semplice per affrontare questo problema sarebbe quella di fare affidamento su un'altra decoratore e aggiungere una funzione lambda:

from django.contrib.auth.decorators import user_passes_test 

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

Che diamine: "le modifiche devono avere almeno 6 caratteri, c'è qualcos'altro da migliorare in questo post?" Diavolo, no! Ci sono solo parentesi mancanti in "u.is_anonymous', quindi l'esempio non è corretto. – simplylizz

+0

L'esempio è corretto e funziona. Dove volevi mettere parentesi aggiuntive? –

+0

Oh, ho controllato i documenti. Hai ragione, è corretto, dal momento che django 1.10+ 'is_anonymous' è diventato un attributo. Ma per le versioni precedenti di Django ti darà sempre 'True', perché' is_anonymous' era un metodo. – simplylizz

1

@permission_classes ([permissions.AllowAny])

Problemi correlati