Allen GJ
Allen GJ

Reputation: 83

Django - Best way to implement authorization (both on a per-role as well as per-user basis)?

I'm working on a django web-app that requires two kinds of authorization:

  1. Role-based (users should not be able to access urls that are for other roles)
  2. User-based (Users of the same role should not be able to access content of other users)

For eg, we have a role called HOD (head of department). These are some of the urls that they can access:

    url(r'^retest/(?P<retest_id>[0-9]+)/$' , views.details, name='details' ),
    url(r'^retest/(?P<retest_id>[0-9]+)/accept$' , views.accept, name='accept' ),
    url(r'^retest/(?P<retest_id>[0-9]+)/request$' , views.request, name='request' ),
    url(r'^retest/(?P<retest_id>[0-9]+)/accepted$' , views.accepted, name='accepted' ),
    url(r'^retest/(?P<retest_id>[0-9]+)/retreq$' , views.retreq, name='retreq' )

Some of the controller functions that correspond to these are as follows:

@login_required
def details(request, retest_id):
    try:
        retest= Retest.objects.get(pk=retest_id)
    except Retest.DoesNotExist:
        raise Http404("Request does not exit")
    return render(request, 'retest/details.html' , { 'retest': retest })

@login_required
def accept(request, retest_id):
    retest = get_object_or_404(Retest, pk=retest_id)
    # update is_hod attribute only if the request is a POST.
    if request.method == 'POST':
        retest.is_hod = True
        retest.save(update_fields=['is_hod'])
    return render(request, 'retest/details.html' , {'retest': retest})

Now, these are the concerns that I have and want to safeguard against:

  1. Right now, anyone can view any retest request (views.details function), irrespective of role. How should I implement this authorization? Through group filtering or using the inbuilt permissions system in django? Either of these would mean that I would have to modify each function (there are six other user roles besides HOD). What would be the most elegant solution?

  2. A bit more trickier, an HOD of a particular department can view retest requests of other departments by simply typing the URL with a retest_id of another department. This cannot be solved by group filtering. (Note that HODs also have other items that have different models & mappings)

If this is to be solved, I believe I would have to check if the content being addressed in the URL is really owned by the current user & redirect otherwise, at the beginning of each function. There are currently around 60 functions in views, spread across different user roles.

What would be the most elegant way to go about implementing authorization on these 2 levels? Should I change the way my URLs are setup?

Upvotes: 1

Views: 244

Answers (1)

Eyad Arafat
Eyad Arafat

Reputation: 611

I'm not an expert but I'd personally go with the built-in django permissions system. It'll solve both your problems and you can easily implement it using the permission_required function decorator.

A couple of tips that might come in handy are: looking into maybe using class-based views instead of function-based ones if your views are similar and you want to keep it DRY, and that you can use decorators on the URL pattern itself (in urls.py) instead of on the view function.

Upvotes: 1

Related Questions