Flask: Clear session from admin prospective - flask

In my flask app, logout can clear the session.
#app.route('/logout')
def logout():
session.clear()
return redirect(url_for('index'))
But logout is called only when user click logout on my website.
I am implementing a functionality where i can block a mysterious user. So, i need to force logout the user without his consent. My logic would also work if i can clear his session. Please guide.
Note: someone suggested to change app.secret_key. But that would clear session for all users and not a particular user only.

So you have to check out if user is banned or not before each request!
#app.before_request
def before_request():
user = findUser()
if user.isBanned():
return redirect(url_for('logout'))
Something like this would work,
Ofc you have to implant findUser and isBanned, findUser should return a User object, and isBanned should return True or False

Related

Django user account delete and then return redirect and render

I want to allow a user to delete his account and upon deletion, I want the user to be logged out and see a html page account_deleted.html for confirmation.
students/views.py:
def delete_account(request):
user = User.objects.get(username=request.user)
user.delete()
context = {
"deleted_msg": "Account has been deleted",
}
return render(request, "students/account_deleted.html", context) and redirect("students:logout")
For logout, I'm using the built-in LogoutView function. The logout redirect URL in my settings.py is set to my home page.
students/urls.py:
path('logout/', LogoutView.as_view(), name='logout'),
In case of an account deletion, how can I make the delete_account(request) function return a render and redirect at the same time? Is that even possible?
Thanks!
You can log the user out before deleting the account by placing logout(request) before your user.delete() line. You will need to import it with from django.contrib.auth import logout.
As Bruno said in his answer, using a redirect instead of render is preferable after a POST request, and you should update your view to only respond to POST requests.
If you are having trouble with your website crashing after a user is deleted, make sure you are using the proper access control in all your views, eg by using the #login_required decorator or the equivalent mixin on all views that require a user to be logged in. If you do this the user will just be redirected to the login page if he or she is not logged in instead of crashing your site.
First things firsts: your view should 1/ only accept logged in users and 2/ only accept POST requests (you definitely dont want a GET request to delete anything from your database). Also, this:
User.objects.filter(username=request.user)
is useless - you already have the current user in request.user - and potentially dangerous if your auth backend allows for duplicated usernames.
and this:
return render(request, "students/account_deleted.html", context) and redirect("students:logout")
is of course plain wrong. A view returns one single HTTP response, you can't return two (it wouldn't make any sense), and you can't "and" two responses together (well, you can but the result is certainly not what you expect - read the FineManual about the and operator).
The proper solution is to 1/ manually log out the user (cf voodoo-burger's answer), 2/ use the messages framework to inform the user her accont has been deleted, and 3/ redirect to the home page (you ALWAYS want to redirect after a successful post, cf https://en.wikipedia.org/wiki/Post/Redirect/Get for the why).

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: 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.

django.contrib.auth: how to keep site and admin sessions separate?

I'm using a user profile model with a ForeignKey to the User, authenticated by django.contrib.auth.
The same auth module is of course used for the admin interface, so if a superuser/staff member is logged in to the admin interface, and enters the main site, the site will accept session cookie and authenticate him. This creates a problem because a superuser/admin doesn't need to have a UserProfile and shouldn't be recognized by the main site.
What's the easiest way to solve this, so that the sessions from admin don't carry on to the site?
I dont think there is a way to solve exactly this,
"What's the easiest way to solve this, so that the sessions from admin don't carry on to the site?"
But depending on what you wnat to do, you may try,
don't create UserProfile for superuser
if request.user.is_superuser():
UserProf.objects.create(...)
I always have the problem where I want to keep a logged in Admin user and a logged in normal user, simultaneously, when I am developing. To do this, I have two entries in /etc/hosts
127.0.0.1 uswaretech.tld
127.0.0.1 admin.uswaretech.tld
Now, normal user always logs in via uswaretech.tld and admin always via admin.uswaretech.tld so thy can both be logge din simultaneously.
From a design standpoint your idea seems like a bit of a hack, but if you really want to do this you may use a middleware.
class MyMiddleware(object):
def process_request(self, request):
if request.user.is_authenticated:
try:
UserProfile.objects.get(user=request.user)
except UserProfile.DoesNotExist:
from django.contrib.auth.models import AnonymousUser
request.user = request._cached_user = AnonymousUser()
return None
This should be at the top of the middleware list to prevent possible side-effects.
if request.session.get ('has_account', False):
return HttpResponse ("You have no account, sorry.")
Then make sure, every user of your front-end gets, if his session is initiated, the value has_account set properly.