django - test is_authenticated() in middleware instead of views - django

Most of my views require login. Is there a way to specify that all views or a subset of views must have the user is_authenticated() = True before proceeding. If not, the user is redirected to a login page?

https://docs.djangoproject.com/en/1.4/topics/auth/#the-login-required-decorator
from django.contrib.auth.decorators import login_required
#login_required
def my_view(request):
...

If you want so, there are some rules that you must follow,
request.user is set on AuthenticationMiddleware, so placing your custom middleware before that will fail.
you can only use process_view method, which is executed before your view is called
But, somehow; you must make a kind of list like login free views, but that is not likely because checking the list everytime you create a new view might be problem. On the other hand, login_required decorator works just like that and it is easier to use... So you must think twice before doing a such thing and consider if you really need something like that.
Here is the doc for Custom Middlewares and here is the django defalut middleware classes which is important because order of the middelware classes is really matters.

Related

Django Redirect after Successful Login

I have a requirement that whenever I login or attempt to request a view that has a login_decorator then the next page be a page where I am required to ask the user to select a business entity (irreespective of the original view requested).
Let's say that the page is http://127.0.0.1:8999/APP/business_unit
To achieve this I configured the following in my settings.py
LOGIN_REDIRECT_URL='/APP/business_unit_selector'
Now when i try to access http://127.0.0.1:8999/APP/range_list
the page goes to http://127.0.0.1:8999/APP/login?next=/APP/range_list I was expecting that the next page after login be /APP/business_unit
but instead, the next page was /APP/range_list
The browser address bar has http://127.0.0.1:8999/APP/login?next=/APP/range_list
Is it possible to achieve what I am trying in Django?
LOGIN_REDIRECT_URL is used unly when next is unspecified. In your test request there is next=/APP/range_list - and that address is used to redirect user after login.
Probably the easiest and most effective solution is to make your own decorator, similar to login_required which redirects to /APP/business_unit_selector&next=<redirect_url> if unit is not selected, and apply it together with login_required. It is not the most efficient solution in terms of redirects number, but is quite clean, and doesn't mess up the login page.
You will also have to handle next parameter in your business_unit_selector view, if you like to achieve natural flow.
Your decorator should be something like
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.shortcuts import redirect
from django.utils.http import urlquote
import functools
def business_unit_required(view):
#login_required # probably you want to incorporate it here
#functools.wraps(view)
def wrapped(request, *args, **kwargs):
if not 'selected_business_unit' in request.session:
return redirect('%s?next=%s' % (
reverse('business_unit_selector'),
urlquote(request.get_full_path())))
return view(request, *args, **kwargs)
return wrapped
The reason that http://127.0.0.1:8999/APP/login?next=/APP/range_list is redirecting you to range_list after logging in, is because with next= you are overriding what is specified in your settings file, LOGIN_REDIRECT_URL='/APP/business_unit_selector'.
If I understand correctly you need to user to choose a business entity after logging in.
A couple solutions that come to mind are as follows:
1.) Don't use a separate forms for login and business entity. Instead combine them.
Username
Password
Business Entity
2.) You can also specify in your view if the user doesn't have a buisness entity ResponseRedirect("/APP/business_unit_selector")
docs here

Suppress "?next=blah" behavior in django's login_required decorator

I love django's #login_required decorator, but there's one thing I can't figure out how to make it do.
If an unauthenticated user tries visits a #login_required page (e.g. "/private-stuff/"), I want to kick them back to the home page (e.g. "/home/"). But I don't want to append a "?next=" argument to the url. In other words, I just want to redirect to "/home/", not "/home/?next=/private-stuff/".
How can I do that? Is there a better way than just writing my own decorator?
Isn't it just simply as:
#decorators.login_required(redirect_field_name=None)
Well, there's two ways that I can think of. First, would be the "correct" way, in the sense that you're not breaking any functionality, only adding new functionality: create your own login_required decorator. The problem though is that Django has really tucked the redirect after login functionality away, and it requires a lot of parts. The login_required decorator is really just a wrapper around the user_passes_test decorator, which in turn calls the redirect_to_login view, and it's that view that adds the next param to the querystring. In your custom decorator, you can roll all or some of this functionality straight into the decorator, but you'll need to reference all three for the necessary code.
The other, and far simpler option, is to create some middleware to remove the querystring if it's set:
from django.conf import settings
from django.http import HttpResponseRedirect
class RemoveNextMiddleware(object):
def process_request(self, request):
if request.path == settings.LOGIN_URL and request.GET.has_key('next'):
return HttpResponseRedirect(settings.LOGIN_URL)
And, then add the import path to that middleware to MIDDLEWARE_CLASSES. Remember that in the request phase, middleware is processed first to last or top-down, in other words. This should come relatively early in the request phase, but you may need to play around a bit with it to see what can and can't come before it.
The only real problem with this method is that it "breaks" the next redirect functionality, and not in a very intuitive way, if a later developer inherits your codebase along with a mandate to allow the redirect, it might be a bit flummoxing.
login(request, user)
if request.POST['next']:
return redirect(request.POST['next'])
else:
msg = u"Welcome..."
return render_to_response('members/welcome.html', {'msg':msg},
context_instance=RequestContext(request))

In which order to sort my middleware path match requirements?

I created a middleware which allows me to use a list of dictionaries to specify some access rules for any of my views. Each of these dictionaries looks like this:
REQUIREMENTS=(
{'viewname':'addtag',
'permissions':'can_add_tags'},
{'regex':re.compile(r'^somestart'),
'user_check':lambda request:request.user.username=='sam'}
)
In my middleware i then try to find out which of those requirements match the current request. To do so, i filter the complete REQUIREMENTS, and in the filter function i use this code to check if the path matches:
def process_request(self,request):
def path_matches(self,req):
path_matches = False
if (req.has_key('url') and req['url'] == request.path ) or\
(req.has_key('regex') and req['regex'].search(request.path)) or\
(req.has_key('viewname') and resolve(request.path).url_name==req['viewname']):
path_matches=True
return path_matches
requirements = filter(path_matches,REQUIREMENTS)
# now use the returned requirements to determine if a user
# matches the requirement and
My question now is: in which order should i use the checks? It's pretty clear that the check for the url is the fastest, so this has to be first. But then the question is if the regex search or django's url resolve function should follow first.
As i do not have any performance issues right now, this is more of an academic question. And if someone would have an all better solution to solve this, that would be even better.
edit:
To react to the given answers: what i'm trying to do is to create a possibility to restrict views of several external apps in a single file. So decorators are not an option, as long as i do not want to do something like this:
from ext_app1 import view1,view2
from ext_app2 import view3
#permission_required('can_do_stuff')
def view1_ext(*args,**kwargs):
return view1(args,kwargs)
which would lead to rewriting the url specifications each time i change permissions. I want to avoid that. Additionally my solution allows for a user_check function to do a check on a user like this:
def check_user(user):
if len(Item.objects.get(creator=user,datetime=today)) > 3:
return False
return True
That would be a simple way to, ie., restrict how many items a user can upload each day. (Ok, that would be possible with user_passes_test as well).
One more thing is that i sometimes want to check for a permission only if the request is a POST, or if the request contains a certain key:value pair (like 'action':'delete' should require the permission, while 'action':'change' should be allowed for anyone). This could be done with a custom decorator as well, but as soon as i would need a new check, i would need a new decorator.
If you're using Django's built in user authentication and permissions system (django.contrib.auth) then you should consider using the views decorators it provides instead of a middleware. These give you several advantages:
Code that changes together is in the same place, i.e. it's more likely that you'll need to change permissions for a specific view when you are changing the view's code than when you are changing other permissions.
You don't have to write much of the code yourself, which makes your project smaller and easier to maintain.
For simple situations you can use the login_required and permission_required decorators, and for a more complex condition the user_passes_test decorator allows you to check if the user passes any condition you care to specify.
The code looks something like this for a view function (example is lifted from the documentation):
from django.contrib.auth.decorators import permission_required
#permission_required('polls.can_vote')
def my_view(request):
...
If you're using class-based views then it looks a little different (again, this example is lifted from the documentation):
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView
class ProtectedView(TemplateView):
template_name = 'secret.html'
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ProtectedView, self).dispatch(*args, **kwargs)
If you have a good reason not to use Django's permissions system, then you can still adopt a similar approach. The code for the django.contrib.auth decorators could easily be used as the basis of your own decorators.
You might be looking for user_passes_test decorator.
You can decorate the views you need instead of using a middleware.
The code will looks like this:
from django.contrib.auth.decorators import user_passes_test
#user_passes_test(lambda user: user.has_perm('model.can_add_tags') \
and user.username == 'sam')
def my_view(request):
...

Django: re-use login_required decorator inside other decorators

According to one of the comments in https://stackoverflow.com/a/8715790/210481, which I agree with, we should avoid multiple decorators if one depends on the other.
So, in the example, if we have a decorator "active_required" for active users, we should not have to use both active_required and login_required on the same view.
We should have "login_required" decorator "called" somehow inside the "active_required" one.
Is it possible to do it with the standard "login_required" decorator that comes with django?
My requirements are:
1) if the user is not authenticated, I should redirect him to LOGIN_URL
2) if the user is authenticated (passed login_required), but is not active, I should redirect him to a page to "re-activate" his account
3) if the user is authenticated and active, the user can access the view
Thanks in advance
When thinking about your question, I found it easier to create a simple active_required decorator first. This is very easy, because we can use the the user_passes_test function in django.contrib.auth.decorators.
The question then changes to "how do I combine the login_required and active_required into one decorator?". We need to define a function which:
takes a view function as it's argument
applies both decorators to it to create a new view function
returns the new view function
Putting it all together, you have the following:
from django.contrib.auth.decorators import user_passes_test, login_required
active_required = user_passes_test(lambda u: u.is_active, login_url=REACTIVATE_URL)
def active_and_login_required(view_func):
decorated_view_func = login_required(active_required(view_func))
return decorated_view_func

Django perform an action just before login_required is called?

In django, I want to do a particular action just before login_required() is called for a view. One hack or easy way out is to just have that url point to a view where login is not required and do that particular action (action: setting some things in the request.session) and then redirect back to this main view where login is required.
How should I go about it, if there's some option to cater to such things in django?
Is there a better or simpler way than to write my own decorator to do this?
Decorator is the best way, because it make code easy to understand and as for security reason, user can block redirections so you'll need to think about how to avoid this.
This arcticle seems usefull How to make a chain of function decorators?, because I think you'll need to pass request variable and also place your decorator to process checking first.
Just use the user_passes_test decorator instead and do your custom stuff before manually validating that they are authenticated. See: https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.decorators.user_passes_test
Your question doesn't give much detail, but going by your example (setting some request variables), it sounds like you need Request Middleware. Take a look at the builtin django.contrib.auth.middleware.AuthenticationMiddleware for an example:
class AuthenticationMiddleware(object):
def process_request(self, request):
assert hasattr(request, 'session'), "The Django authentication middleware requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
request.user = SimpleLazyObject(lambda: get_user(request))
You'll need to define a class with the process_request method (as shown above). The process_request method will be called for each request, and passed a request instance. Within the method, you can check for request.user, and if the user is authenticated, and set whatever variables you want for the current request.
Writing your own decorator is the best way to approach this. Anywhere that you have #login_required, just add your own decorator in addition to the login_required decorator.
Or, you could call the login_required function from your decorator.