Django's logout function remove locale settings - django

When I use Django's logout function to log out an authenticated user, it switched locale to en_US, the default one.
from django.contrib.auth import logout
def someview(request):
logout(request)
return HttpResponseRedirect('/')
How to keep user's locale after logged out?

I solved the problem by wrapping the django.contrib.auth.views.logout within a custom view and resetting the session language after logout. There's some code.
I have an app named login with following urls.py:
# myproject/login/urls.py
from django.conf.urls.defaults import patterns
urlpatterns = patterns('myproject.login.views',
...
(r'^logout/$', 'logoutAction'),
...
)
So now URL /logout/ calls a view named logoutAction in views.py. In logoutAction, the old language code is stored temporarily and inserted back to the session after calling Django's contrib.auth.views.logout.
# myproject/login/views.py
...
from django.contrib.auth.views import logout as auth_logout
def logoutAction(request):
# Django auth logout erases all session data, including the user's
# current language. So from the user's point of view, the change is
# somewhat odd when he/she arrives to next page. Lets try to fix this.
# Store the session language temporarily if the language data exists.
# Its possible that it doesn't, for example if session middleware is
# not used. Define language variable and reset to None so it can be
# tested later even if session has not django_language.
language = None
if hasattr(request, 'session'):
if 'django_language' in request.session:
language = request.session['django_language']
# Do logout. This erases session data, including the locale language and
# returns HttpResponseRedirect to the login page. In this case the login
# page is located at '/'
response = auth_logout(request, next_page='/')
# Preserve the session language by setting it again. The language might
# be None and if so, do nothing special.
if language:
request.session['django_language'] = language
# Now user is happy.
return response
The end of the LAN Quake problem :)

You could try explicitly setting the language cookie when the user logs out, there's a bit about how django determines the language setting here in the djangodocs.
I would have presumed that the language setting would have remained as a session value on logging out, maybe it needs to be reinitialised if django completely destroys the session on logging out the user.

You can create a UserProfile model (that has unique foreign key to User) and save the user's language preference there (and any other extra user specific setting). Then on every user login, the locale can be set to the language code saved in the user's UserProfile.
Django has a setting AUTH_PROFILE_MODULE, where you can set a model as an "official" user profile model for django.contrib.auth

Related

django admin page and JWT

We are using django-rest-framework with django-rest-framework-jwt for authentication and it works everywhere except the django admin page at ip:port/admin/. That still wants username and password.
Is there a setting or way to bypass that so it recognizes the JWT?
Is the /admin/ page always required to use name/password? I think the built in token auth works with it.
jwt is the only auth set in the settings.py file. Session authentication is not in there anymore.
The issue is that Django isn't aware of djangorestframework-jwt, but only djangorestframework, itself. The solution that worked for me was to create a simple middleware that leveraged the auth of djangorestframework-jwt
In settings.py:
MIDDLEWARE = [
# others
'myapp.middleware.jwt_auth_middleware',
]
Then in my myapp/middleware.py
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from django.contrib.auth.models import AnonymousUser
from rest_framework import exceptions
def jwt_auth_middleware(get_response):
"""Sets the user object from a JWT header"""
def middleware(request):
try:
authenticated = JSONWebTokenAuthentication().authenticate(request)
if authenticated:
request.user = authenticated[0]
else:
request.user = AnonymousUser
except exceptions.AuthenticationFailed as err:
print(err)
request.user = AnonymousUser
response = get_response(request)
return response
return middleware
Important Note:
This is a naive approach that you shouldn't run in production so I only enable this middleware if DEBUG. If running in production, you should probably cache and lazily evaluate the user as done by the builtin django.contrib.auth module.
The problem can be not in the authentication method you use. If you customize User model, it can happen that create_superuser method doesn't update is_active flag in user instance details to True. This case django authentication backend (if you use ModelBackend) can recognize that user is not active and do not allow to authenticate. Simple check - just see what value has is_active field of the superuser you create. If it False, update it manually to True, and try to login. If it is the reason of your problem you need to override create_superuser and create_user method of UserManager class.

Django : intercept authentication to store session variable or cookie

I am working on a project that's running on django. I would like to authenticate in multiple places. First, I would like to maintain the standard authentication mechanism and continue using it for site administration. Second, I would like to intercept the login request in addition to the standard authentication, and check if the user has is authenticated on another system and store a session variable or cookie to be used later if authenticated. Then on logout remove the session variable or cookie. The second authentication mechanism should not affect the first. In addition, if the first succeeds and the second fails, it should have no affect on the standard administration.
I have looked into declaring a custom authentication backend in the settings AUTHENTICATION_BACKENDS tuple. But from what I understand, it authenticates in order and will stop authenticating once a match is made.
Any guidance in regards to this would be most appreciated. Thanks
If all you need is set and unset cookie or session var you can use signals sent by authentication module (docs)
Example:
from django.contrib.auth.signals import user_logged_in, user_logged_out
def user_logged_in_hook(sender, **kwargs):
# kwargs will contain request and user objects
print kwargs
def user_logged_out_hook(sender, **kwargs):
# kwargs will contain request and user objects
print kwargs
user_logged_in.connect(user_logged_in_hook)
user_logged_out.connect(user_logged_out_hook)

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

How can I redirect a user to a specific page or run some code logic in a common place after login in Django?

What I want is to redirect the user to a specific page when they log in if a flag is raised. So I know that all of the login requests go through django.contrib.auth.views.login. Do I have to modify this to accomplish what I want to do or is there some other way to do this. So when I log in I hit the index page and that's fine, I can work with that but what if the user tries to access a page where log in is required. Here's what it will look like
login/?next=/page_that_requires_login
so now after login, it will not hit the index page anymore, it will go directly to page_that_requires_login. I am a little new to django but my instinct tells me that I shouldn't have to check this flag on every page. Is there a common place that I can do some code logic after a user is logged in no matter what page they get redirect to?
Django provides decorator called, login_required() which we can attach to any view, where we require the user to be logged in. If a user is not logged in and they try to access a page which calls that view, then the user is redirected to another page which you can set, typically the login page.The following is an example code for a view called restricted.
#login_required
def restricted(request):
return HttpResponse("since you are logged in you can see this page)
Note that to use a decorator, you place it directly above the function signature, and put a # before naming the decorator. Python will execute the decorator before executing the code of your function/method. To use the decorator you will have to import it, so also add the following import:
from django.contrib.auth.decorators import login_required
To set a page to which the user is redirected if he is not logged in, you need to add the something like this to settings.py
LOGIN_URL = "/app_name/login/"
This ensures that the login_required() decorator will redirect any user not logged in to the URL /app_name/login/.
I redirected by groupwise as :
from django.http import HttpResponseRedirect
def loggedin_view(request):
usergroup = None
if request.user.is_authenticated():
usergroup = request.user.groups.values_list('name', flat=True).first()
if usergroup == "staffGroup":
return HttpResponseRedirect("/cmit/userDash")
elif usergroup == "supervisorGroup":
return HttpResponseRedirect("/cmit/supervisorDash")
elif usergroup == "auditGroup":
return HttpResponseRedirect("/cmit/auditDash")
elif usergroup == "mgtGroup":
return HttpResponseRedirect("/cmit/mgtDash")
else:
return HttpResponseRedirect("/admin/")

Django: How to destroy user session after a password reset/change?

i've recently implemented a simple change password view in my django project. The thing is that the old session should be destroyed for security reasons. What's the best way of doing this without asking the user to log in again.
I think i could just logout/login him/her, something like this:
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
#login_required
def change_password(request):
# My stuff
request.user.set_password(new_password)
request.user.save()
# I need this:
logout(request)
login(request,request.user)
But i think this is not the best idea. What do you think?
Is there another way to do this?
Am I missing something? (I mean, is this secure)
Take a look at this app https://github.com/atugushev/django-password-session.
This package makes invalidated all sessions (except a current session) after change a password.
Also this feature finally was implemented in Django 1.7. See: https://docs.djangoproject.com/en/dev/topics/auth/default/#session-invalidation-on-password-change
I just found out that this is now a built-in feature of Django, and has been since 1.7:
https://docs.djangoproject.com/en/1.7/topics/auth/default/#session-invalidation-on-password-change
Essentially, all sessions now include a hash of the users' password, so if the user ever changes their password, all their existing sessions are automatically invalidated.
So, the short answer to your question is: upgrade django.
One possibly undesirable side effect of this change is that, by default, a user ends up having to log in again as soon as they change their password. So you probably actually want the current user session to stay logged in. See the docs already linked, Django's built-in views for password change do that for you default, or you can manually call a function called update_session_auth_hash
django clears the session on logout so you will be fine:
https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.logout
When you call logout(), the session data for the current request is completely cleaned out. All existing data is removed. This is to prevent another person from using the same Web browser to log in and have access to the previous user's session data.
I don't understand whats are these security reasons that forces to reset session. But, the way is:
#login_required
def change_password(request):
request.user.set_password(new_password)
request.user.save()
username = request.user.username
logout(request)
user = authenticate(username=username, password=new_password) #<-- here!!
if user is not None:
login(request,user)
else:
#raise your exception
you should authenticate before login. Quoting doc:
Calling authenticate() first When you're manually logging a user in,
you must call authenticate() before you call login(). authenticate()
sets an attribute on the User noting which authentication backend
successfully authenticated that user (see the backends documentation
for details), and this information is needed later during the login
process.