After programming a new site on my laptop (standalone web server, Django standard authentication), I moved it to the DEV server (Apache, CAS for authentication). Some things didn’t work the same.
I had designed three security settings: Admin (can get to everything – superuser in Admin), Staff (can run reports, just a few permissions in Admin – marked as staff), and User (entry in Admin, but not staff and no permission, runs reports only).
The views that displayed the reports use the
Here’s the problem, and I saw this coming: any user who could sucessfully log in to CAS could run reports. Since I’m using a campus wide CAS database, this included any campus staff member or student. Not what I was going for.
I had forgotten that
django_cas adds every sucessfully loged in user as a User record in the Django Admin. At that point, the user passes the
@login_required test and can run those views.
My solution was two-fold. Mark the User level records as staff, and replace the
@login_required decorator with
@staff_member_required. Unfortunately, the system seemed to be using the Django internal login mechanism rather than CAS. A little code inspection showed that the decorator wasn’t using a URL redirect to login, and instead was calling the Admin login function directly. This bypasses both the
django_cas middleware and the
Next, I tried using the
@user_passes_test decorator testing for
user.is_staff in place of
@staff_member_required (which I will likely never use again). This also worked in the standalone setting, but not with
django_cas (kept throwing 500 errors). I then switched to the versions of the
@login_required decorators provided with
django_cas, but then I kept getting 403 errors.
TIME FOR A LUNCH BREAK!!
After a sandwich and a quick walk, my refreshed mind reminded me to check to see if
django_cas had been updated from v2.0.2 that I had installed. Sure enough! Version 2.0.3 specifically updated the decorators. The
@user_passes_test code had been updated to return a 403 only if a logged in user failed the test, and an unauthenticated user would get a proper login screen. Furthermore,
@login_required had been removed completely and replaced with an import of the standard django supplied version. Even though I am currently unable to update the version on the DEV (or higher) servers, I saw what I needed to do.
First, I changed my import statements to grab the correct decorators for the environment.
import settings if settings.USE_CAS: from django_cas.decorators import user_passes_test else: from django.contrib.auth.decorators import user_passes_test from django.contrib.auth.decorators import login_required
USE_CAS is a setting I’ve defined in the server specific settings file.)
This code pulls in the proper
@user_passes_test decorator, and the standard
Next, for each veiw that needs protection, I implemented the decorators thusly:
@login_required @user_passes_test(lambda u: u.is_staff) def view_staff(request): ...
@login_required first forces a login if the user hasn’t, and then
@user_passes_test checks for the staff attribute.
With the changes in
django_cas v2.0.3, the call to
@login_required will no longer be necessary. I hope to setup a project to update the version soon.