The admin app in django has this behaviour, and I want to replicate it in my project.
In the admin, once you login and press the browser back button, shows the home page again, and the it does not let you go back any further.
It doesn't seem it uses javascript.
Any ideas how to accomplish this?
Thanks.
My code
def login_user(request):
if request.user.is_authenticated:
return redirect(reverse('sinapsis-home'))
else:
...# code processing the authentication
# the redirect
return redirect(reverse('sinapsis-home'))
The solution is partially the proposed answer, but adding a cache decorator.
It is not javascript, and it was so easy to prove just disabling javascript in the browser. I was slow on that point.
As you navigate the browser back and for, the cached page does not load again, that's why you can go back, in this case, to the login page even if you are logged in. I think this behaviour is prone to problems, for example a csrf error. I repeat, not permitting the user go back to the login page once logged in is a standard behaviour of web apps, including the django admin.
So the solution to this problem is adding to the login view a redirect if the user is logged in and a never_cache decorator:
from django.views.decorators.cache import never_cache
#never_cache
def login_user(request):
...
if user.is_authenticated:
return HttpResponseRedirect(reverse('my_redirect'))
...
Well, Like you said that isn't js, It's as simple as an if conditional like:
if not request.user.is_authenticated:
# Return a login form to login
else:
return redirect('success:page')
Edit:
After add your code, the problem was that you were using request.user.is_autenticated() with parentheses and it was deprecated in Django 1.10 more information
Related
I've searched for this question but could not found an answer.
Basically, let's say i have my_app/successfully_page that i want to be displayed within the application flow. I don't want that to be displayed if some one is typing into browser the direct url.
How can i make that url page INACCESSIBLE from browser search?
You can do it using request.META.get('HTTP_REFERER') in order to check from which url the user is coming. If this is not the authorized url, then you can redirect the user:
if request.META.get('HTTP_REFERER') == my_url:
# Do something...
else:
# Redirect...
Check the doc here: https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.META
I had some kind of same problem before , I solved it using sessions in the page before that set a session on the request and in the views of the url which needs to be inaccessible by direct browser set a if condition on it.
This need to be set in view of before page
request.session['is_accessible'] = True
And in the page u want to make the condition like
if (request.session['is_accessible'] == True):
redirect("Your_page_url.html")
else:
redirect("Page you want to show .html")
And if you are thinking that a page needs to be accessed only on login then there is a decorator in Django itself like
from django.contrib.auth.decorators import login_required
#login_required(login_url='/login.html')
def yourfunction(request):
return Httpresponse("Your page")
And if u have more problem in it u can write an email to me at kiranu941#gmail.com
You have to use user agent that will let you know the perfect output.
request.META['HTTP_USER_AGENT']
But I would also recommend you to take a look at the Django User-Agents library.
You can find more relevant examples over the link.
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).
I am making a custom administration page in Django. I do not want to reinvent the wheel and thus want to use Django admin login form for the staff to log in and redirect them to /my-url/ afterwards.
However, I can't find the way to redirect user to a custom url after successful login at /admin/.
since I stumbled across the same problem, I noticed the url of the the default login page:
/admin/login/?next=/admin/
so I changed the login page link to
/admin/login/?next=/
to point to the main page
works for the logout page too, nice and simple
I had the same issue.
Instead of redirect after login I used the #staff_member_required decorator
for my view /my-url/ which redirects to the admin login
from django.contrib.admin.views.decorators import staff_member_required
#staff_member_required
def test_list(request):
return HttpResponse('TEST')
If class based views is used check out the method_decorator
The Django auth app comes with a login view which you can hook up to /accounts/login/ or any other url you choose. You can probably use the admin's login template admin/login.html if you don't want to write your own.
By using the login view, the LOGIN_REDIRECT_URL parameter will work. The purpose of the /admin/ page is to display the admin index. I would avoid trying to use it as the login page.
Set LOGIN_REDIRECT_URL in your settings.py file. Documented here.
If you have ?next= parameter set is not enough to set LOGIN_REDIRECT_URL in you setting.py. You need to also handel this parameter. As say docs (https://docs.djangoproject.com/en/4.1/ref/settings/#login-redirect-url):
LOGIN_REDIRECT_URL
Default: '/accounts/profile/'
The URL or named URL pattern where requests are redirected after login
when the LoginView doesn’t get a next GET parameter.
In my case I just set REDIRECT_FIELD_NAME = "" instead of REDIRECT_FIELD_NAME = "next" in my django.contrib.auth and it works fine.
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.
I've noticed an interesting problem with Django's admin area. If I revoke my staff permissions and try to access /admin directly, I would normally expect a redirect to my login page with /admin/ in the query string as a future redirect. However, I get a proper page returned with HTTP code 200 which actually uses my admin/login.html template to render that requested page instead of redirecting. It seems the problem lies within the #staff_member_required decorator, which admin views obviously use.
The question is: is this done on purpose? If not, how can I change this behaviour without too much monkey-patching?
This is done on purpose, because many people implement redirects in thier sites which could block access to the admin panel. Because the admin panel is it's own app it redirects to itself.
# Put this code somewhere it will be imported REALLY early
from django.contrib.admin.views import decorators
def staff_member_required(view_func):
"""
Decorator for views that checks that the user is logged in and is a staff
member, displaying the login page if necessary.
"""
def _checklogin(request, *args, **kwargs):
if request.user.is_active and request.user.is_staff:
# The user is valid. Continue to the admin page.
return view_func(request, *args, **kwargs)
else:
return HTTPResponseRedirect('/my/login/page/')
return wraps(view_func)(_checklogin)
decorators.staff_member_required = staff_member_required #replaces the function in-place