In my forbidden view, I want to redirect all members to their dashboard if they visit a page for guests, and I want to redirect all guests to the login page if they visit a page for members. This is easy enough.
However, there are some cases where I need to throw an HTTPForbidden error which is not the cause of a failed permission and simply display the reason to the user. How can I determine whether an HTTPForbidden is a result of a failed permission or some other reason? I suppose I could do some weird stuff with pyramid.security.has_permission (haven't tried it yet), but there has to be an easier way.
An old question from 2011 where it was stated that this was on the to do list How to check what permission failed in authorization in pyramid (pylons 2)?
This is not exactly what you asked for but it may still help you.
The right answer to your question was already given: you'd better raise a specific exception that is more appropriate than HTTPForbidden.
But if you really want to change the behavior of your forbidden view depending on the missing permission that triggered the HTTPForbidden exception, you need to grab its name.
The name of the missing permission can be found inside the HTTPForbidden exception object in HTTPForbidden.result.permission.
But first, the HTTPForbidden exception needs to be passed as a context of your forbidden view.
For instance, here is how I use this in my webapps to inform the user why he cannot access a particular feature so that he can
ask a manager to re-configure ACL accordingly if appropriate.
#forbidden_view_config(renderer='/forbidden.mako')
def forbidden(context, request):
return { 'required_permission': context.result.permission }
It works with pyramid 1.4.
I couldn't find anything in the documentation so I hacked this by debugging pyramid.
This is more a workaround than a clean solution.
I haven't tested it, but what I'd try to do is to raise something else than HTTPForbidden in the places where you do this manually. You can even subclass HTTPForbidden:
class WeDontLikeYourFace(HTTPForbidden):
pass
def my_view(context, request):
if request['face'] != 'beautyful':
raise WeDontLikeYourFace("go away")
Then you can register a custom view for your custom exception, or do some if/else stuff in the common view method registered for HTTPForbidden.
You can also add custom fields to your subclass to pass any information you need.
Related
I stumbled upon a weird behaviour: I add a permission to a user object but the permission check fails.
permission = Permission.objects.get_by_natural_key(app_label='myapp', codename='my_codename', model='mymodel')
user.user_permissions.add(permission)
user.has_permission('myapp.my_codename') # this is False!
I found some posts about user permission caching here and here and the solution seems to be to completely reload the object from the database.
# Request new instance of User
user = get_object_or_404(pk=user_id)
# Now note how the permissions have been updated
user.has_perms('myapp.my_codename') # now it's True
This seems really overkill for me and very un-django-like. Is there really no way to either clear the permission cache oder reload the foreign keys like you can do for an object with refresh_from_db()?
Thanks in advance!
Ronny
You can force the recalculation by deleting the user object's _perm_cache and _user_perm_cache.
permission = Permission.objects.get_by_natural_key(app_label='myapp', codename='my_codename', model='mymodel')
user.user_permissions.add(permission)
user.has_permission('myapp.my_codename') # returns False
del user._perm_cache
del user._user_perm_cache
user.has_permission('myapp.my_codename') # should be True
But this will essentially hit the database again to fetch the updated permissions. Since these are based on internal workings in django and not part of the public API, these cache keys might change in the future, so I would still suggest to just fetch the user again. But that's totally up to you.
The caching is only an issue for you because of the has_perms method. If you follow it all the way down the stack, you eventually end up here. You'll see this method explicitly checking the cache before proceeding.
If you really need to know this user's permissions at this point in time and really don't want to refresh from the DB, then you can check more manually/directly without the has_perm helper method, since this permission .add() is a standard m2m operation and the model field has been updated.
In practice, it's unlikely you'll check a permission right after it is added, while the object is in scope, and while the permissions are cached since it's a bit redundant. I'm sure the Django devs considered this, and decided the benefits of caching it for you by default was the right call.
I know we can use try and except function to catch the error. But everyday I monitor the sentry, the system always get an exception in any views. As usual I put try and except to catch the errors in the views.
My question is. Is it possible to catch all the errors from any views in just one function? Then the user will redirect to another page. Where is the best place to do this? I'm thinking about middleware but I don't have knowledge about it.
yes, can handle all exceptions from any view. try Googling "django middleware exception", you'll find many solutions .
I have created a function log_error(request, traceback), which I call in my exceptions. This writes error information to the database. Now, before I open up every one of my views and add this in an exception handler, is there a way to automatically have all exceptions raise to a function, which then calls this?
I have seen this Python error logging, which says to write your own version of sys.excepthook. This function is automatically called when there is an exception. I tried this, but my_excepthook was not called even though I copy-pasted the solution into views.py and raised an error. However, I didn't try too hard because it's not getting all the information that I need, anyway. I also need request so I can log information abut the user, url, etc.
Maybe that's asking too much?
(I'm using Django, but this does not seem like a Django-specific thing) Edit: yes, it is.
J.F Sebastian's suggestion worked. This is a Django solution.
In settings.py MIDDLEWARE_CLASSES:
(I added it as the last one, not sure if this is right or will cause errors down the line. Works for now.)
'myapp.middleware.ExceptionMiddleware',
In myapp.middleware.py:
import traceback
class ExceptionMiddleware(object):
def process_exception(self, request, exception):
log_error(traceback, request)
That's it. log_error is my function and writes to the database. It also appears from the documentation https://docs.djangoproject.com/en/dev/howto/error-reporting/ that I can get the local variables as well as the request attributes.
I want to display error message in case the instance I am creating is more than the maximum limit I have specified in a certain model. This is a inner condition.
I know that we can hide the + and override the has_add_permission method. This is used for user authentication in my application.
However, I wish to allow the instance to be created for a certain login who is a superuser based on the inner condition.
ValidationError gives me error u' max...' ValidationError at the url...
Can anyone guide?
I don't know what an "inner condition" is.
However, you don't do this in the save method. You do it in a validator. For instance, you could define a clean method on your model to handle this, or use a custom form with the validation in.
I would like to run a check on the IP-adress when users post with django comments.
I can easily override and customize the form used by django.comments, but I need access to the request object to add an IP-test to its clean(). Is it possible to get access to this in a clean way?
An alternative could be to check the IP when recieving the save signal, but then the only way to abort the save seems to be returning a code 400 to the user.
The comments framework provides a comment_will_be_posted signal:
http://docs.djangoproject.com/en/1.2/ref/contrib/comments/signals/#comment-will-be-posted
If you register at this signal, your handler will be passed the (not yet saved) comment object and the request as arguments. If your handler returns False, the post_comment view answers with CommentPostBadRequest, as it does on any other sort of error like failed form validation.
One possible way, but you still do not have request object in this level of validation...
class SomeForm(forms.ModelForm):
somefield = forms.CharField(...)
def check_somefield(self):
somefield = self.cleaned_data['somefield']
... #do what you want
return somefield
Hope this can help, or i do not understand what you want correctly.
I think this is the answer to your question:
How do I access the request object or any other variable in a form's clean() method?