Custom Django Password Reset - Link does not get invalidated - django

I know that the one time link for the password reset should be invalidated following the email password change procedure as given here enter link description here. Yet, with my below implementation, although the link can reset the password each time, it does not get invalidated. The link works each time. What could be the reason for this ?
(I also see the last login timestamp is also updated in admin pages for the particular user that I am changing the password for)
(forms.py)
from django.contrib.auth.forms import UserCreationForm, SetPasswordForm
from django.contrib.auth import get_user_model
class ResetPasswordForm(SetPasswordForm):
class Meta:
model = get_user_model()
fields = ('password1', 'password2')
(tokens.py)
from django.contrib.auth.tokens import PasswordResetTokenGenerator import six
class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
def _make_hash_value(self, user, timestamp):
return (
six.text_type(user.pk) + six.text_type(timestamp) +
six.text_type(user.email_confirmed)
)
account_activation_token = AccountActivationTokenGenerator()
(views.py)
def activate_forgot_password(request, uidb64, token):
try:
uid = force_text(urlsafe_base64_decode(uidb64))
User = get_user_model()
user = User.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
if user is not None and account_activation_token.check_token(user, token):
if request.method == 'POST':
form = ResetPasswordForm(user, request.POST)
if form.is_valid():
user = form.save(commit=False)
print(user.password)
user.save()
login(request, user, backend='mysite.signup.views.EmailBackend')
return redirect('home')
else:
form = ResetPasswordForm(user)
return render(request,
'change_password.html',
{'form': form,
'uidb64': uidb64,
'token': token})
return render(request, 'account_forgot_password_token_invalid.html')
(template.html)
<form id="ResetPasswordForm" method="post" action="{% url 'activate_forgot_password' uidb64=uidb64 token=token %}" validate>
{% csrf_token %}
.
.
.
<div class="form-group">
<button type="submit" id="btn-signup" class="btn btn-block btn-primary btn-lg">Change Password</button>
</div>
</form>

The fields in _make_hash_value method never got update, the link still valid until one of the field assigned updated or the token timeout, you can add more fields to make sure that will trigger change like user.last_login or even user.password

Related

How to get the specific users details from the database in the dashboard after login in django?

I am having a registration,login and a dashboard page the registration form consists of username,email,password,goal,design,furniture,room so in these the username,email will be stored in custom user model and the remaining details will be stored in the user_requirement table and the login page consists of username and password which will be authenticated and get to the dashboard page now In dashboard page I am trying to display the goal,design,furniture,room details which the particular user given during registration
My views.py:
from django.shortcuts import render, redirect
from django.contrib import messages, auth
from django.contrib.auth.models import User
from contacts.models import Contact
from django.shortcuts import render,HttpResponseRedirect
from django.contrib import messages,auth
from account.forms import UserForm
from account.forms import UserRequirementForm
from django.contrib.auth import authenticate, login
def register(request):
return render(request, 'account/register.html',);
def user_register(request):
if request.method == 'POST': # if there is a post request in the form
user_form = UserForm(data=request.POST) #first of all it is a user_form will be posted details present in the user_form
user_requirement_form = UserRequirementForm(data=request.POST)# after posting the details of the user_form post the details
if user_form.is_valid() and user_requirement_form.is_valid():
# if user_form & user_requirement form is valid
User = user_form.save()#if form is valid save
User.set_password(request.POST['password'])
User.save()
user_requirement = user_requirement_form.save(commit=False)
# Set user
user_requirement.user = User
user_requirement.save()
user_requirement_form.save_m2m()
messages.success(request,('Project saved successfully'))
return render(request,'account/home1.html')
else:
messages.warning(request, 'Please correct the errors above')
else:
user_form = UserForm()
user_requirement_form = UserRequirementForm()
return render(request,'account/register.html', {'user_form': user_form, 'requirements_form': user_requirement_form})
def login(request):
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = auth.authenticate(username=username, password=password)
if user is not None:
if user.is_active:
auth.login(request, user)
messages.success(request, 'You are now logged in')
return redirect('dashboard')
else:
messages.error(request,'Invalid')
return redirect('login')
else:
return render(request, 'account/login.html')
def dashboard(request):
return render(request, 'account/dashboard.html',);
dashboard.html:
<details>
<summary class="summary ml-4">User Details</summary>
<p class="p1 ml-4">The contents that are selected by the user</p>
<p class="p2 ml-4">All content and graphics on this web site are the property of the company Refsnes Data.</p>
</details>
<p class="p3 ml-4 mt-4"><b>Room:<br></b>{{user.room}}</p>
<p class="p4 ml-4"><b>GOAL:</b><br>{{user.goal}}</p>
<p class="p5 ml-4"><b>FURNITURE:</b><br>{{user.furniture}}</p>
<p class="p6 ml-4"><b>DESIGN:</b><br>{{user.design}}</p>
My models.py:
class User_Requirement(models.Model):
user=models.OneToOneField(User,on_delete=models.CASCADE)
room = models.ForeignKey(room,on_delete=models.CASCADE)
goal = models.ManyToManyField(goal)
design = models.ManyToManyField(design)
furniture = models.ForeignKey(furniture,on_delete=models.CASCADE)
created_at = models.DateTimeField(default=datetime.now)
updated_at = models.DateTimeField(default=datetime.now)
request.user will give the currently logged-in user's details.Here you have the OneToOne relation with your User_Requirement model so you can try like this
<p class="p3 ml-4 mt-4"><b>Room:<br></b>{{request.user.user_requirement.room}}</p>
Edit based on Comment:
Since your goal is a ManyToMany Field you need to iterate it
<p class="p4 ml-4"><b>GOAL:</b>
{% for goal in request.user.user_requirement.goal.all %}
<br>{{goal}}</p>
{% if not forloop.last %}, {% endif %} #if you want to separate goals with comma
{% endfor %}

Django form clean() method not raising errors when a ValidationError occurs

I am trying to create a custom validator for my form by overwriting the clean() method, and for some reason validation errors are not being raised properly. Here's my code:
forms.py
from django import forms
from django.contrib.auth import get_user_model
class EmailForm(forms.Form):
email_field = forms.EmailField(label='E-mail address', max_length=128)
def clean(self):
cleaned_data = super(EmailForm, self).clean()
email = cleaned_data.get('email')
try:
u = get_user_model().objects.get(email=email)
raise forms.ValidationError("E-mail already in database.")
print('validation error raised')
except:
pass
views.py
from django.shortcuts import render
from django.contrib import messages
from .forms import EmailForm
def email_form(request):
if request.method == 'POST':
form = EmailForm(request.POST)
# If the form is valid...
if form.is_valid():
messages.add_message(request, messages.SUCCESS, "Kaboom! Your referral e-mail was sent. Hang tight - we'll be sure to let you know when your friend makes a purchase.")
else:
messages.add_message(request, messages.ERROR, 'Error!')
else:
form = EmailForm()
return render(request, 'template.html', {'form': form})
template.html
<form action="{% url 'form-process' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
When I submit this form with input that should raise a validation error (i.e., an e-mail address that is already associated with a user in my database), I get the 'success' message rather than an error. The strangest part about it is that 'validation error raised' is printed in my console — but for some reason the form processes and form.is_valid() passes as True.
Can anyone tell me why form.is_valid doesn't output an error in the code below when a duplicate e-mail is entered?
It can't raise exception, because you catch is in except.
def clean(self):
cleaned_data = super(EmailForm, self).clean()
email = cleaned_data.get('email')
users = get_user_model().objects.filter(email=email)
if users.exists():
raise forms.ValidationError("E-mail already in database.")

Redirect to Next after login in Django

When a user accesses a url which requires login. The view decorator redirects to the login page. after the user enters his username and password how can I redirect the user to the page he was trying to access ('next') ?
Views.py
def login_view(request):
template = 'pos/login.html'
form = LoginForm
if request.method == 'POST':
username = request.POST.get('username', '')
password = request.POST.get('password', '')
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
messages.success(request, "You have logged in!")
return redirect('home')
else:
messages.warning(request, "Your account is disabled!")
return redirect('/login')
else:
messages.warning(request, "The username or password are not valid!")
return redirect('/login')
context = {'form': form}
return render(request, template, context)
#login_required(redirect_field_name='next', login_url='/login')
def bar(request):
template = 'pos/bar.html'
drink = OrderItem.objects.filter(product__catgory__gt=1).order_by('-created')
context = {'drink': drink}
return render(request, template, context)
Login.html
<form action="/login" id="login_form" method="post" class="form-signin">
{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-lg btn-primary btn-block" type="submit" value="login">Sign in</button>
<input type="hidden" name="next" value="{{next}}" />
</form>
url.py
url(r'^login', views.login_view, name='login'),
forms.py
class LoginForm(AuthenticationForm):
username = forms.CharField(label="Username", required=True, max_length=30,
widget=forms.TextInput(attrs={
'class': 'form-control',
'name': 'username'}))
password = forms.CharField(label="Password", required=True, max_length=30,
widget=forms.PasswordInput(attrs={
'class': 'form-control',
'name': 'password'}))
You can try:
return redirect(self.request.GET.get('next'))
The accepted answer does not check for the next parameter redirecting to an external site. For many applications that would be a security issue. Django has that functionality inbuilt in form of the django.utils.http.is_safe_url function. It can be used like this:
from django.shortcuts import redirect
from django.utils.http import url_has_allowed_host_and_scheme
from django.conf import settings
def redirect_after_login(request):
nxt = request.GET.get("next", None)
if nxt is None:
return redirect(settings.LOGIN_REDIRECT_URL)
elif not url_has_allowed_host_and_scheme(
url=nxt,
allowed_hosts={request.get_host()},
require_https=request.is_secure()):
return redirect(settings.LOGIN_REDIRECT_URL)
else:
return redirect(nxt)
def my_login_view(request):
# TODO: Check if its ok to login.
# Then either safely redirect og go to default startpage.
return redirect_after_login(request)
You can try by simply add this input field before submit button in accounts/login.html template
<input type="hidden" name="next" value="{{ request.GET.next }}"/>
Passing next to the login form and then the form passing that value on to view in a hidden input can be a bit convoluted.
As an alternative, it's possible to use django.core.cache here.
This way there is no need to pass anything extra to the form or to give the form an extra input field.
def login_view(request):
if request.method == 'GET':
cache.set('next', request.GET.get('next', None))
if request.method == 'POST':
# do your checks here
login(request, user)
next_url = cache.get('next')
if next_url:
cache.delete('next')
return HttpResponseRedirect(next_url)
return render(request, 'account/login.html')
This actually works for me quite nice:
from django.shortcuts import redirect
def login(request):
nxt = request.GET.get("next", None)
url = '/admin/login/'
if nxt is not None:
url += '?next=' + nxt
return redirect(url)
If previous URL contained next - call "login" URL and append the previous "next" to it.
Then, when you logged in - you'll continue with the page that was previously intended to be next.
In my project I made the following helper which works for Swagger login/logout:
def _redirect(request, url):
nxt = request.GET.get("next", None)
if nxt is not None:
url += '?next=' + nxt
return redirect(url)
def login(request):
return _redirect(request, '/admin/login/')
def logout(request):
return _redirect(request, '/admin/logout/')
Yes Arun Ghosh option is better but it leads to an exception in cases where there is no next value is found.
Hence I used this approach.
try: return redirect(request.GET.get('next')) except TypeError: return HttpResponseRedirect(reverse("default_app:url_name"))
OR
except Exception as e: return HttpResponseRedirect(reverse("default_app:url_name"))
path_redirect = request.get_full_path().split('?next=',1)
if '?next=' in request.get_full_path():# Redirecting After Login
return redirect(path_redirect[1])
else:
return redirect('index')

Is there a example in django 1.6 and python 3 to build a accounts app (include:register , login , and logout )

I do have read the offical document, but it describes every facility separately, after reading
'User authentication in Django' ,'First steps' , 'The model layer', 'The view layer' and 'The template layer' and 'Forms' , I still donot know how to create a account system.
there seems no django 1.6 and python 3 built account app source code or tutorial. where can I get them, thanks.
update:
All I what is a account app which I can plug it into any new project. Its urls will look like this:
accounts/register (the form class of this page is created from the class User in django.contrib.auth)
accounts/login
accounts/logout
accounts/profile (the form class of this page is created from the model which has a field OneToOneField(User))
In your views.py
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth import authenticate, login, logout
from django.core.context_processors import csrf
#Import a user registration form
from YourApp.forms import UserRegisterForm
# User Login View
def user_login(request):
if request.user.is_anonymous():
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
#This authenticates the user
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
#This logs him in
login(request, user)
else:
return HttpResponse("Not active")
else:
return HttpResponse("Wrong username/password")
return HttpResponseRedirect("/")
# User Logout View
def user_logout(request):
logout(request)
return HttpResponseRedirect('/')
# User Register View
def user_register(request):
if request.user.is_anonymous():
if request.method == 'POST':
form = UserRegisterForm(request.POST)
if form.is_valid:
form.save()
return HttpResponse('User created succcessfully.')
else:
form = UserRegisterForm()
context = {}
context.update(csrf(request))
context['form'] = form
#Pass the context to a template
return render_to_response('register.html', context)
else:
return HttpResponseRedirect('/')
In your forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class UserRegisterForm(UserCreationForm):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email', 'username', 'password1', 'password2')
In your urls.py:
# Accounts urls
url(r'accounts/login/$', 'YourApp.views.user_login'),
url(r'accounts/logout/$', 'YourApp.views.user_logout'),
url(r'accounts/register/$', 'YourApp.views.user_register'),
At last, in register.html:
<form action="/accounts/register/" method="POST"> {% csrf_token %}
<h2>Please enter your details . . .</h2>
{{ form.as_p }}
<input type="submit" value="Sign Up">
</form>
Hope this helps.

Taking User Input to create Users in Django

I would like to create/add new users to my app in Django using the user input. I am using the default login provided by django. I am trying to add users to the default login. The example in https://docs.djangoproject.com/en/dev/topics/auth/:
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('john', 'lennon#thebeatles.com', 'john password')
passes the username and password. But i would like this to happen with user input. How do i do this?
Need some guidance...
I tried this but it doesn't seem to work:
def lexusadduser(request):
"""
add user
"""
if request.method == "POST":
user.save()
auth_user = authenticate(username=user.username,password=user.password)
if auth_user is not None:
lexusmain(request)
else:
return render('adduser.html')
First thing you need to do is create a ModelForm:
forms.py
from django.contrib.auth.models import User
class UserForm(ModelForm):
class Meta:
model = User
fields = ('username', 'email', 'password')
A ModelForm automatically builds your form off a model you provide. It handles the validations based on the fields.
views.py
from forms import UserForm
from django.contrib.auth import login
from django.http import HttpResponseRedirect
def lexusadduser(request):
if request.method == "POST":
form = UserForm(request.POST)
if form.is_valid():
new_user = User.objects.create_user(**form.cleaned_data)
login(new_user)
# redirect, or however you want to get to the main view
return HttpResponseRedirect('main.html')
else:
form = UserForm()
return render(request, 'adduser.html', {'form': form})
If its a POST request, we create a UserForm from the POST values. Then we check if its a valid form. If it is, we create the user, otherwise we return the form with the errors. If its not a POST request, we send a clean form
template
<form method="post" action="">
{% csrf_token %}
{{ form }}
<input type="submit" value="Create new user account" />
</form>
Use a form. Air coding here:
class SignupForm(forms.Form):
username = forms.CharField()
email = forms.EmailField()
password = forms.CharField(widget=forms.PasswordInput)