Newtt
Newtt

Reputation: 6190

Check authorization for each request in django

I have a Django app setup as follows:

MyApp
    CustomAdmin
        urls
        models
        views
    MainApp
        settings
        urls
        wsgi
    SomeOtherApp
        admin
        models
        views

Now, in my MainApp.urls, I've setup a the following url:

url(r'^api/admin/', include('CustomAdmin.urls')),

In CustomAdmin, I want to have a single mechanism where it checks if the request, regardless of the associated view, is being made by a superuser or not. If it is made by a superuser, the request should be processed by the associated view function else it should throw a 403 or similar error.

I've used something similar in Laravel as follows

Route::group(
    array(
        'before' => 'auth.admin',
        'prefix' => 'api/admin'        
        ),
    function(){
        ....
    });

I'm not sure if such a mechanism exists in Django. If there is, what should I be doing?

Upvotes: 5

Views: 2984

Answers (3)

sthzg
sthzg

Reputation: 5554

I think it is not possible to define it on the url pattern that includes the others (will test a bit more about that later), but it is at least possible in CustomAdmin.urls by using the user_passes_test decorator.

# CustomAdmin/urls.py
from django.contrib.auth.decorators import user_passes_test
from CustomAdmin import views

requires_superuser = user_passes_test(lambda x: x.is_superuser)

urlpatterns = patterns(
    '',
    url(  # with a class based view
        r'^$', 
        requires_superuser(views.SomeView.as_view()), 
        name='someview'
    ),
    url(  # with a functional view
        r'^(?P<foo>\w+)/$', 
        requires_superuser(views.someotherview),    
        name='someotherview'
    ),
)

Upvotes: 1

Gocht
Gocht

Reputation: 10256

From ccbv.co.uk and from Django Class-bases views docs, dispatch() is the first method called in a class-bases view.

View class workflow

  1. dispatch()
  2. http_method_not_allowed()
  3. options()

Keep in mind than all generic views inherits View class

Middleware is a good solution, but if you do not need to pre-process every request you could use an access mixin.

As I said before, dispatch() is the first executed method, so you can re write it to grant or deny access to a view.

Here is dispatch default code:

def dispatch(self, request, *args, **kwargs):
    # Try to dispatch to the right method; if a method doesn't exist,
    # defer to the error handler. Also defer to the error handler if the
    # request method isn't on the approved list.
    if request.method.lower() in self.http_method_names:
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    else:
        handler = self.http_method_not_allowed
    return handler(request, *args, **kwargs)

You can write a mixin class:

class SuperuserRequiredMixin(object):
    """
    Mixin allows you to require a user with `is_superuser` set to True.
    """

    login_url = settings.LOGIN_URL  # LOGIN_URL from project settings
    raise_exception = False  # Default whether to raise an exception to none
    redirect_field_name = REDIRECT_FIELD_NAME  # Set by django.contrib.auth

    def dispatch(self, request, *args, **kwargs):
        if not request.user.is_superuser:  # If the user is a standard user,
            if self.raise_exception:  # *and* if an exception was desired
                return HttpResponseForbidden()  # return a forbidden response.
            else:
                # otherwise, redirect the user to the login page.
                # Also, handily, sets the `next` GET argument for
                # future redirects.
                path = urlquote(request.get_full_path())
                tup = self.login_url, self.redirect_field_name, path
                return HttpResponseRedirect("%s?%s=%s" % tup)

        return super(SuperuserRequiredMixin, self).dispatch(request, *args, **kwargs)

Then you can use it in your views. Let's suppose a ListView:

from django.views.generic import ListView
from somewhere import SuperuserRequiredMixin

class MyView(ListView, SuperuserRequiredMixin):
    ...
    # Do what you usually do...

I hope you find this useful.

Upvotes: 0

mimo
mimo

Reputation: 2629

You can write a middleware that will allow you to do some work on the request before processing it in the views. In a file called middleware.py, put:

from django.contrib.auth.views import redirect_to_login

class AllowSuperUserOnly(object):
    def process_request(self, request):
        if request.path.startswith('/api/admin/'):
            if not request.user.is_superuser:
                return redirect_to_login(request.path)
        # Continue processing the request as usual:
        return None

And add the middleware to your settings.py. It should look similar to this:

MIDDLEWARE_CLASSES = (
    ...
    'your_app.middleware.AllowSuperUserOnly',
)

Upvotes: 5

Related Questions