I am trying to set a reset password view without using the reset FormViews that Django uses by default. This means that I don't really use the auth application, more than for the tokens and some other small stuff.
What I did now is a normal view that displays an email/username form, and send an email to the user with a token:
password_reset_token = PasswordResetTokenGenerator()
def sendPasswordResetEmail(user, current_site):
mail_subject = 'Password reset'
message = render_to_string('myapp/password_reset_email.html', {
'user': user,
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'token': password_reset_token.make_token(user),
})
to_email = user.email
email = EmailMessage(mail_subject, message, from_email='testing#testing.com', to=[to_email])
email.send()
After this, the user should be displayed with a view that asks for the user to fill a SetPasswordForm if the token is correct and it's a GET request. Or it should check for the token and check if the form is valid. If the form and token are valid then the password should be changed.
I am trying to replicate a bit Django's PasswordResetConfirmView, but I'm not really sure if I'm doing it right. This is how I would do it, but I can't tell if there's any way to exploit the view somehow:
def passwordResetConfirm(request, uidb64, token):
if request.user.is_authenticated:
return redirect("myapp:index")
try:
uid = force_str(urlsafe_base64_decode(uidb64))
user = CustomUser.objects.get(pk=uid)
except(TypeError, ValueError, OverflowError, CustomUser.DoesNotExist):
user = None
if user is not None:
if password_reset_token.check_token(user, token):
if request.method == 'POST':
#TODO: Check SetPasswordForm is valid
#TODO: if form is not valid redirect to error url
#TODO: user = form.save()
#TODO: redirect to done url
pass
else:
return render(request, "myapp/password_reset_confirm.html")
return HttpResponse('Password reset link is invalid!')
Is this safe to do? I see in the PasswordResetConfirmView they perform some more redirections but I'm not sure what they are doing exactly.
Also, will these token expire? Or they remain there working forever? Meaning that if I request two reset password tokens and don't use them, if I use them after a few days, will they still be working?
Using Python3 and Django4. Also my website uses HTTPS everywhere.
Related
i have a problem with a functionality of flask-login.
I need to create a system, that prevents multiple users from logging in on one account in the same time. The goal i am trying to achieve, is a way to check if the user is currently logged in, and deny access to people that are trying to log in if the account is already logged in. I was trying to use:
user.is_active
user.is_authenticated
But they always are displaying "True", here is the rest of the code: is there a property that changes after the users logs in/out?
def login():
if request.method == 'GET':
return render_template('login.html')
else:
login = request.form['login']
password = request.form['password'].encode('utf-8')
user = User.query.filter_by(username=f"{login}").first()
if user == None:
return "Non-Existing User"
else:
if bcrypt.checkpw(password,user.password.encode('utf-8')):
login_user(user)
return "Success"
else:
return "Bad creds."
#app.route('/logout')
#login_required
def logout():
logout_user()
return 'You are logged out'```
I don't know about any property but we can do it another way. You can store a token like thing in database with the user details.
class user(db.Model):
[...]
token = db.Column(db.String(20))
when the user login, you can generate a random hex or string and store it in database.
When user logout set token to False
while logging in the user , you can check if token is not False.
user = User.query.filter_by(username=user_input).first()
if user.token != False:
// allow user to login
else:
// don't allow him and show warning message.
I want to use django's default password reset view "PasswordResetView" which let's the user reset his password when he forgets it in a template that already has a view that i built on my own, after looking at the tutorials and the questions i found how to use it only on a different template that is made only for the password reset, but i don't want the user to go to a different page just to change his password when he forgets it, i want to make it in a bootstrap modal in the home page.
here is my home view that i want to add PasswordResetView functionality to it:
def home(request):
user = request.user
signin_form = SigninForm()
signup_form = SignupForm()
if request.method == "POST":
if 'signin_form' in request.POST:
signin_form = SigninForm(request.POST)
if signin_form.is_valid():
email = request.POST['email']
password = request.POST['password']
user = authenticate(email=email, password=password)
if user:
login(request, user)
elif user is None:
messages.error(request, 'ُEmail or password is incorrect')
if 'signup_form' in request.POST:
signup_form = SignupForm(request.POST)
if signup_form.is_valid():
signup_form.save()
full_name = signup_form.cleaned_data.get('full_name')
email = signup_form.cleaned_data.get('email')
raw_password = signup_form.cleaned_data.get('password1')
account = authenticate(email=email, password=raw_password)
login(request, account)
context = {'signin_form': signin_form,'signup_form': signup_form}
return render(request, 'main/home.html', context)
PS: i tried copy pasting the source code of that view (PasswordResetView) from django's source code in my view but i found some errors because it's a class based view, so if you find this the proper way, guide me to do it
or if i can't merge them somehow how to create a custom one
this is what i found in the other answers which lets you use it in a certain template that has only that view (PasswordResetView) which is not what i want:
from django.contrib.auth import views as auth_views
path('password_reset/', auth_views.PasswordResetView.as_view(template_name="myapp/mytemplate.html",form_class=mypasswordresetform),name="reset_password"),
I'll give you a simple approach to having a password reset feature on your django application. Before having any code, let me give a brief exlanation of the process. What you want to do is get a user to input their email, check if there is any user with that email, then if there is one, send an email to that address with a uniquely generated link.
From this link, you should be able to extract the user object which you need to change password. An example would be to use django's signing module. This link will simply need to redirect the user to a template where there is a form with 2 fields i.e. New Password and Verify Password.
Django's generic views come with this functionality out-of-the-box if you are using Django's authentication module, but you aren't forced to use it, but its best to do so.
Here I'll only show you how to collect the email address on the same view as you said you wanted.
def home(request):
# ...your other code
if request.method == 'post':
if 'reset_password' in request.POST:
email = request.POST.get("email", "")
user_qs = User.objects.filter(email=email)
if not user_qs.exists():
# send error message to user here
else:
user = user_qs.get()
# send email with uniquely generated url here.
The other aspects of generating a URL and sending the mail, I believe you can research these separately. But I hope you now have an idea of where and what to search.
I have custom Django login forms that, since upgrading from Django 1.8 to 2.2, no longer keep user logged in after HttpResponseRedirect. Here's is a generalized sample of the code:
#render_with('users/login.html')
def login_view(request, campaign_id=None):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
email = form.cleaned_data['email']
password = form.cleaned_data['password']
# Login user
user = authenticate(request, email=email, password=password)
if user:
if user.is_active:
login(request, user)
return HttpResponseRedirect('/redirect-user/')
I know that login() is working properly because if I use render instead of HttpResponseRedirect, the user is logged in. However, I want to redirect the user to a different view after login. What is the best way to do this?
UPDATE
It appears that the culprit is my custom authenticate backend EmailOrUsernameModelBackend. Any ideas what I am doing wrong?
settings.py
AUTHENTICATION_BACKENDS = (
'social_core.backends.open_id.OpenIdAuth',
'social_core.backends.google.GoogleOpenId',
'social_core.backends.facebook.FacebookOAuth2',
'social_core.backends.google.GoogleOAuth2',
'social_core.backends.linkedin.LinkedinOAuth2',
'users.backends.EmailOrUsernameModelBackend',
'django.contrib.auth.backends.ModelBackend',
)
__init__.py
class EmailOrUsernameModelBackend(object):
supports_object_permissions = False
supports_anonymous_user = False
supports_inactive_user = False
def authenticate(self, request, email=None, password=None):
if '#' in email:
kwargs = {'email': email.lower() }
else:
kwargs = {'username__iexact': email.lower() }
try:
user = User.objects.get(**kwargs)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
Also, if I change the code and logout/login again to test, it works the 1st test and fails on subsequent tests with the same user.
Thanks to #Sayse's suggestion about middleware/backends causing the issue. Removing 'users.backends.EmailOrUsernameModelBackend' fixed the issue in the question above, but consequently broke my login after a new user registers. In order to get both login and registration working, I switched the other of EmailOrUsernameModelBackend and ModelBackend:
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'users.backends.EmailOrUsernameModelBackend',
)
Per Django docs:
The order of AUTHENTICATION_BACKENDS matters, so if the same username
and password is valid in multiple backends, Django will stop
processing at the first positive match.
I am still not sure what is wrong with the EmailOrUsernameModelBackend causing it to break my login, but this did fix the issue. If anybody has a better fix for the root cause (ie what is wrong with this backend), I will accept as the best answer.
I had the same problem, but reordering the backends in settings did not help.
login(request, user) can take a third argument, the backend to use, e.g.
backend = `django.contrib.auth.backends.ModelBackend'
login(request, user, backend)
If you remove django.contrib.auth.backends.ModelBackend from settings.AUTHENTICATION_BACKENDS, inside django/contrib/auth/__init__.py -> get_user is:
if backend_path in settings.AUTHENTICATION_BACKENDS:
...
else:
return None
So if you specify the backend, but its not in your settings.AUTHENTICATION_BACKENDS, your user will never authenticate.
Django's authenticate function seems to always be returning none, ive already checked out this thread and updated the authentication backends in the settigns.py file.
This is the code that i am using to save the accounts being created:
if request.method == 'POST':
# Getting the information from the filled in UserCreationForm
user_form = UserCreationForm(data=request.POST)
# If the the form is valid then save the users data to the database
# hash the password using set_password and save the user again
# set registered to True
if user_form.is_valid():
user = user_form.save()
user.set_password(user.password)
user.save()
registered = True
# Invalid form or forms - mistakes or something else?
# Print problems to the terminal.
# They'll also be shown to the user.
else:
print user_form.errors
# Not a HTTP POST, so these forms will be blank, ready for user input.
else:
user_form = UserCreationForm()
# Render the template depending on the context.
return render_to_response(
'contracts/register.html',
{'user_form': user_form, 'registered': registered}, context)
This seems to be working, since after i create a new user on the site, the access database its linked to updates with that username and the encrypted password.
The user_from is created from UserCreationForm which is:
class UserCreationForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = ('username','password','first_name','last_name','email','is_staff','is_active')
Here is the code in my user_login method which should be grabbing the username and password from the request and authenticating the combo
def user_login(request):
# context for the user's request.
context = RequestContext(request)
# form = AuthenticationForm()
# If the request is a HTTP POST
if request.method == 'POST':
# Gather the username and password provided by the user.
# This information is obtained from the login form.
user = request.user
userSubmit = user.username
passSubmit = user.password
#username = request.POST.get['username']
#password = request.POST.get['password']
user = authenticate(username = userSubmit, password=passSubmit)
#try:
# user = authenticate(username=userSubmit, password=passSubmit)
#except LockedOut:
# messages.error(request, 'You have been locked out because of too many login attempts. Please try again in 10 minutes.')
# If we have a User object, the details are correct.
# If None (Python's way of representing the absence of a value), no user
# with matching credentials was found.
else:
if user:
# Is the account active? It could have been disabled.
if user.is_active:
# If the account is valid and active, we can log the user in.
# We'll send the user back to the homepage.
login(request, user)
return redirect('home')
else:
# An inactive account was used - no logging in!
messages.error(request, 'Your account is disabled.')
else:
messages.error(request, 'The credentials you entered are invalid.')
# Bad login details were provided. So we can't log the user in.
# The request is not a HTTP POST, so display the login form.
# This scenario would most likely be a HTTP GET.
# No context variables to pass to the template system, hence the
# blank dictionary object...
return render_to_response('administrative/login.html', {'form': form}, context)
So I'm currently trying to implement an already existing application for payment processing via Braintree (https://github.com/Tivix/django-braintree for reference). It seems like all the meat of this application is placed at the /payments-billing/ directory but I can't seem to get into it to check it out. It seems like what's stopping me is a #login_required decorator placed just before the view since whenever I access the directory it sends me back to the defined LOGIN_URL. However I have set up a login feature at the LOGIN_URL that authenticates the user and then sends them to /payments-billing/ but it just redirects back again. Here is my code:
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = authenticate(username=username, password=password)
if user is not None:
# Password verified for user
if user.is_active:
return redirect(self.success_url)
else:
return redirect('/')
else:
return redirect('/')
Clearly the user is being authenticated and is active since it passes both tests when you try it, but it always just sends the user back to the LOGIN_URL rather than /payments-billing/. Anyone know what's the deal here?
The authenticate function doesn't log a user in, it just checks their username/password. You also have to call django.contrib.auth.login() to do the actual logging in. See the example in the documentation.
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = authenticate(username=username, password=password)
login(request, user)
### Now it should get redirected to /payments-billing/
if user is not None:
# Password verified for user
if user.is_active:
return redirect(self.success_url)
else:
return redirect('/')
else:
return redirect('/')