Reputation: 6190
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
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
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
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
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