Django poll showing after user logs in - django

When user logs in I would like to check if he has already voted the poll..and if not, redirect him to the poll form page. Tried with user_logged_in signal but it completely ignores my redirect() function.
#receiver(user_logged_in)
def check_poll(sender, user, request, **kwargs):
"""Check if any active polls available and redirect user if needed"""
try:
# there can only be 1 active poll
poll = Poll.objects.get(active=True)
if not poll.has_user_voted(request.user):
return redirect(poll)
except ObjectDoesNotExist:
pass
Maybe I'm doing this the wrong way..Maybe I should modify the request as I am not sure I should return the response here ... Is there any method without modifying the login() view?

The user_logged_in signal doesn't allow you to change the response to the login, it just allows you to take ancillary actions. In order to change the response to the login (which by default is just a redirect to LOGIN_REDIRECT_URL), you do need to use your own login view. Your view, however, can just wrap the built-in login view: call it, and if the response is a redirect and the user hasn't completed the poll, change the "Location" header of the redirect to the poll page before returning it.

Related

Logout user when they leave site

I want to be able to automatically log users out when they leave my Django site. Not just when they close the browser, but whenever they navigate away to a different site. Is this possible?
I'm displaying sensitive data to the user, and if they were to go to a new URL, and then hit the back button, I don't want the sensitive data displayed. I want them to be forced to log back in.
Is this possible? Or is this something I would have to do on the front end?
Check out this answer on how to detect when a visitor moves away from the page.
Documentation: Page Visibility API
Logout user via Ajax:
from django.views.generic import View
from django.http import JsonResponse
from django.contrib.auth import logout
class LogoutView(View):
"""
The logout view class. This will log the user out and invalidate the session.
"""
def post(self, *args, **kwargs):
logout(self.request)
return JsonResponse({'success': True}, **kwargs)
If you only want to 'logout' user if they leave the page, try hooking into onbeforeunload event.
$(window).unload(function () {
/* login for ajax call goes here */
});
WindowEventHandlers.onbeforeunload

Determine when user logs via a regular login or via a remember_me

I would like to store the time and date for each user log in.
Putting this code into the regular login view is simple.
The problem arises when Flask-Login's remember_me is used - login is no longer called giving me no way of knowing if the login is fresh.
I have tried the user_logged_in, user_login_confirmed signals provided by Flask-Login:
def user_logged_in_callback(*args, **kwargs):
import ipdb;ipdb.set_trace()
user_logged_in.connect(user_logged_in_callback, app)
user_login_confirmed.connect(user_logged_in_callback, app)
user_logged_in is only called on regular logins and user_login_confirms doesn't seem to be called at all.
Getting the user again after login is called "user load" and is not really a login event, it's just the same user still. There is a signal for it, however it's not documented.
You can use the user_loaded_from_cookie signal:
from flask_login import user_loaded_from_cookie
user_loaded_from_cookie.connect(user_login_callback, app)
user_login_confirm is for when a login is refreshed - a fresh login is required and the user re-enters their password.
If all you want to do is record when a user last visited the site, you can just use app.before_request, you don't need to deal with the login events.
#app.before_request
def track_user():
if current_user.is_authenticated():
current_user.last_seen = datetime.utcnow()

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 not detecting logged in user upon redirect back from external page

I'm sending a user off-site to register on another service, and then as they are redirected back to a specific URL on my site I'm firing the view listed below:
#views.py
class OAuthView(View):
def get(self, request, *args, **kwargs):
if request.user.is_authenticated():
#do things
else:
#return HttpReponse with error message
I would say that I'm just not logging users in properly, but I use braces' LoginRequiredMixin elsewhere on the site, and that works fine after the user is logged in, but before they are directed off-site.
The weird thing is that after being redirected back to my site and seeing the error message triggered from OAuthView, LoginRequiredMixin no longer works for the pages it used to. It's as if the user has been logged out by being redirected off-site.
Even weirder is that I'm using the default sessions backend, and when I check the current session within admin, it says that the session is still active for the user who can no longer see pages with the LoginRequiredMixin.
So - session is still valid, but user seems to be logged out. How can I test to find out more information about this problem?

How to get notified when a user changes password or requests a password reset?

For a password change I am using auth_views.password_change and for the password reset auth_views.password_reset.
How can I be notified when a user successfully changes their password? I do not need to know old nor new password. Just that the event took place, and for which user.
Similarly, I would like to get notified when someone requested a password reset and also when they successfully completed the reset procedure.
Can i do the above with signals or some simple patching? Or do I need to write my own views to do this?
Create a decorator:
def notify_admins(func):
def wrapper(request, *args, **kwargs):
# send email to admins
return func(request, *args, **kwargs)
return wrapper
Then, just add wrap it around the appropriate views in your urls.py:
urlpatterns = patterns('',
...
(r'^password_change/done/$', notify_admins(auth_views.password_change_done)),
(r'^password_reset/done/$', notify_admins(auth_views.password_reset_done)),
(r'^reset/done/$', notify_admins(auth_views.password_reset_complete)),
...
)
Keep in mind that sending email directly from a view, or in this case a decorator, will tie up the request. Instead of sending the email there directly, it would be better to create a custom signal and a handler that will fire off a thread to actually send the email. Then, in the decorator, you simply send the signal.
You could write a custom password_change_form that you pass to password_change. This form would extend django's PasswordChangeForm overriding its save method to first notify you of the change and then call it's parent PasswordChangeForms save method.
Docs on password_change view:
https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.views.password_change
Docs on ChangeForm:
https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.forms.PasswordChangeForm
Code for PasswordChangeForm:
https://code.djangoproject.com/browser/django/trunk/django/contrib/auth/forms.py
Beginning in Django 1.9, you can define your define your own password validators. You could even simply re-define an existing one, if you like. When you do, add a method:
from django.contrib.auth.password_validation import MinimumLengthValidator
class MyPasswordValidator(MinimumLengthValidator):
def password_changed(self, password, user):
# put your password changed logic here
Be sure to include your new class in your settings as follows:
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'my_package.password_validators.MyPasswordValidator',
'OPTIONS': {
'min_length': 8,
}
},
...
]
Now, each time a password is changed by the user, your class MyPasswordValidator will be notified. In my experience, this is the best way to do this because:
When using signals to capture these events, you will also capture events where the system re-encoded an existing password due to a change in hashing parameters, in most cases, you would not want to capture these events and there's no obvious way to prevent it with signals.
You could simple add a function-call in the save() method of all your password-handling forms, but this becomes difficult when you want to do the same with the built in admin change password form and will not help you if password changes are made programmatically outside of a form.
I will caution you to be aware that the password parameter in password_changed() is in fact the user's raw password. Take care when handling this and absolutely never store this anywhere unencrypted/unhashed.
If you are already using the auth_views.password_change built in view, then it would be easy to notify yourself once they are redirected after a successful change:
https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.views.password_change
password_change(request[, template_name, post_change_redirect, password_change_form])
If you set the post_change_redirect url to redirect to one of your own views, then you simply take whatever action you want in that view to send a notification (email, database updates, etc).
You could even, in your redirect view, just do your notification and then return password_change_done(request[, template_name])
You could also capture the signal and check if the password has changed. Just keep in mind that this code will run every time a user changes.
#receiver(pre_save, sender=User)
def record_password_change(sender, **kwargs):
user = kwargs.get('instance', None)
if user:
new_password = user.password
try:
old_password = User.objects.get(pk=user.pk).password
except User.DoesNotExist:
old_password = None
if new_password != old_password:
# do what you need here