Opened 17 years ago
Closed 13 years ago
#4617 closed Bug (fixed)
permission_required decorator behaviour is odd
Reported by: | Owned by: | ctrochalakis | |
---|---|---|---|
Component: | contrib.auth | Version: | dev |
Severity: | Normal | Keywords: | easy-pickings dceu2011 |
Cc: | Tom Christie | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | yes | UI/UX: | no |
Description
The permission_required() decorator is a great idea, but in practice its behaviour is odd.
When used, it first checks whether the user is logged in. If they're not, it redirects to the login page. So far, so good.
If they are logged in, it then checks whether they have been granted the specified permission. If they have, it calls the view function and displays the result. Also good.
If they're logged in but don't have the specified permission, it redirects to the login page. This is odd. Sure, they might have another user id they can use, but that sounds unusual to me. In most cases, this is just going to confuse them because they're already logged in.
Surely it would make more sense to return a HttpResponseForbidden in this case, even if the code to achieve that is a little more complex.
Attachments (10)
Change History (39)
comment:1 by , 17 years ago
comment:3 by , 17 years ago
Triage Stage: | Unreviewed → Design decision needed |
---|
comment:4 by , 17 years ago
Triage Stage: | Design decision needed → Accepted |
---|
comment:5 by , 17 years ago
Has patch: | set |
---|---|
Version: | 0.96 → SVN |
Added a check for the user being already authenticated. In that case a HttpResponseForbidden object is returned.
by , 17 years ago
Attachment: | django-auth-decorators-plus-tests.diff added |
---|
django-auth-decorators+tests
comment:6 by , 17 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
New patch. Fixes test cases.
comment:8 by , 17 years ago
Keywords: | easy-pickings added |
---|---|
Patch needs improvement: | set |
Looks good. Should probably have a new test to show the behavior when a user is not logged in.
comment:9 by , 17 years ago
Patch needs improvement: | unset |
---|
The existing tests already handle this. They first try to access the view without logging in, and assert a redirect to the login page.
comment:11 by , 17 years ago
Perhaps this could be made even better by having it render a 403.html template, similar to the current 404 and 500 behaviour. Apps could then display a styled error page, informing the user that they do not possess the required authorization to view that page.
Maybe even a handler403...
comment:12 by , 16 years ago
Needs documentation: | set |
---|---|
Patch needs improvement: | set |
Triage Stage: | Ready for checkin → Accepted |
This is a terrible usability regression. With this patch, if you are logged in and the permission is denied you get a single, non-customisable, non-styled string back as a result. The current code lets you display a proper page at the "login URL" (which is not going to be just the login box).
If any change like this is going in, the user has to be able to specify what happens when the permission is denied. It shouldn't be a setting (we don't need another one of those). It shouldn't be anything like a handler403
, since that's a slippery slope that will never end (and HTTP response codes are the final stage of the processing pipeline -- we don't need to turn them all into exceptions). So either allow a template to be specified or a callable or something.
The approach here is reasonable and not forcing a redirect upon failure isn't a bad idea (it's one reason I don't use the current permission_denied decorator), but the result must be controllable, otherwise it's not use to anybody.
by , 16 years ago
Attachment: | decorator.diff added |
---|
redirect in case of test failed and user is already authenticated
comment:13 by , 16 years ago
How about this patch? It will redirect to a developer supplied url, that can be set in code like:
permission_required('polls.can_vote', noperm_url='custom_url')(my_view)
The question is where to redirect in case the noperm_url is set to None, patch assumes '/', which is again odd. Any suggestions?
comment:14 by , 16 years ago
Patch needs improvement: | unset |
---|
New patch. Now it is possible to specify template that should be rendered via 403 in case the user did not pass the test, but he is still authenticated.
Better now?
comment:15 by , 16 years ago
This patch will surely be usefull for a lot of people.
Is it planned to integrate it in 1.1 or 1.2 ?
comment:16 by , 15 years ago
*Bump*
Has there been any discussion about this (or a similar) patch being added to a future django release?
comment:17 by , 15 years ago
Patch needs improvement: | set |
---|
Patch needs to be updated to current trunk.
comment:18 by , 14 years ago
comment:19 by , 14 years ago
To me it doesn't seem obvious that the permission required checks if you are logged in first.
With the whole change to authentication backends so anonymous users can have permissions the logic should be that you check the permission first and if the user does not have the permission and they are not logged in go to the login page, else show a 403 (Which really should be added together with a handler imho)
comment:20 by , 14 years ago
Please consider another approach:
from django.core.exceptions import PermissionDenied def permission_required(perm, login_url=None): """ Decorator for views that checks whether a user has a particular permission enabled, redirecting to the log-in page if necessary. """ def check_perms(user): if user.is_anonymous(): return False if user.has_perm(perm): return True raise PermissionDenied return user_passes_test(check_perms, login_url=login_url)
This one doesn't change semantics of 'user_passes_test'. It depends on #9847 for handling PermissionDenied exception.
comment:21 by , 14 years ago
Cc: | added |
---|
comment:22 by , 14 years ago
Component: | Contrib apps → contrib.auth |
---|
comment:23 by , 14 years ago
Severity: | → Normal |
---|---|
Type: | → Bug |
comment:24 by , 14 years ago
Easy pickings: | set |
---|
by , 13 years ago
Attachment: | decorators.3.diff added |
---|
Version using PermissionDenied as suggested in comment 20
comment:25 by , 13 years ago
Patch needs improvement: | unset |
---|
by , 13 years ago
Attachment: | documentation.diff added |
---|
Add missing documentation about the fix. Doc depends on ticket #9847
comment:26 by , 13 years ago
Needs documentation: | unset |
---|---|
UI/UX: | unset |
by , 13 years ago
Attachment: | permission_required.diff added |
---|
comment:27 by , 13 years ago
Keywords: | dceu2011 added |
---|
Improved the documentation and added it to decorators.3.diff
by , 13 years ago
Attachment: | permission_required_tinyfix.diff added |
---|
Tiny fix on Roalds version: removed old commented out code
comment:28 by , 13 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
I have made a very small fix to the patch, removing a commented line of old code. Other than that, it all looks good, and if I understand the comments properly all existing disputes are resolved with this patch.
RFCed.
http://www.djangosnippets.org/snippets/254/