Hello I am new to django and web programming. I am building a website for my school that allows students to schedule online advising appointments.
I need to be able to email students temporary passcodes to their student emails and then validate them on the next page. I have an email form :
class EmailForm(forms.Form):
student_email = forms.EmailField(max_length = 200, label = 'Mail')
def clean_student_email(self):
student_email = self.cleaned_data['student_email']
if not student_email.endswith('.edu'):
raise ValidationError("Please enter your school email that ends with #edu")
return student_email
and a login view
def login(request):
if request.method == 'POST':
form = EmailForm(request.POST)
if form.is_valid():
student_email = form.cleaned_data['student_email']
random_code = get_random_string()
subject = "Temporary Advising Code"
message = f'Your temporary code is: \n code: {random_code}'
send_mail(subject, message, 'advising email', [student_email])
return HttpResponseRedirect('/enter_code/', {'form' : form})
else:
form = EmailForm()
return render(request, 'login/login.html', {'form' : form})
Now I am able to generate a random string and send it to the students email but I am wondering if someone can tell me how I can validate that string on the next page.
I would advice against your approach.
But I would suggest to check how password reset token generator works in django .
Source: PasswordResetTokenGenerator
You can make something very similar to generate a token which you will send to students.
You don't need to store such token as you can easily generate it again ( when it comes to verification ).
So the idea here is to generate the token using some data which after password is changed or set by students - the token will no longer be valid.
You can even incorporate a expiration time if needed.
And you don't need to generate temporary password either ( which is something i don't like very much ).
So instead of sending students the temp password and asking them to log in with that - you would just send them a link with the token and by accessing the page with that token - they will be able to set a password.
Related
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.
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.
Maybe I am not asking the right question in the search area, but I can't find an answer for this. I am pretty sure that many people have this use case, but as a beginner in Django + Python, I need to ask it.
I have user that fills up a form and the data is stored in the database. Basically this form asks for an access to a Database and after the form is submitted I want my program to send an email to the user's manager and to the DBA to APPROVE or DENY it. Very simple, right?
My idea is that in this e-mail I send two URL's, one for approving and one for denying the request. When the URL the is clicked I send a response to the server with an update in the manager_approval field.
Has anyone implemented this solution, or could point me to something that could help me?
I am doing everything using Django + Python.
Regards,
Marcos Freccia
Basically this technique used in email verification. This is where you should look into.
Let's say you have model, named request, which has field like username to identify the person who requested access, database name, well, everything. But it will also have two "password-like" fields which will be used to determine if request was declined or not.
class Request(models.Model):
user = models.ForeignKey ...
databasename =
date =
...
access_granted = models.BooleanField(default=False)
deny_token = models.CharField()
allow_token = models.CharField()
The point is to generate those tokens on saving request in the View:
if request.method == POST:
form = RequestForm(request.POST)
if form.is_valid():
data['user'] = form.cleaned_data['user'])
data['databasename'] = form.cleaned_data['databasename'])
...
data['access_token'] = GENERATE_USING_HASH_FUNCTION()
data['deny_token'] = GENERATE_USING_HASH_FUNCTION()
form.save(data)
Then you can use module EmailMultiAlternatives to send html email like so:
subject, from_email, to = 'Request', 'admin#example.com', form.cleaned_data['manager_email']
html_content = render_to_string(HTML_TEMPLATE, CONTEXT) # Just as any regular templates
text_content = strip_tags(html_content)
msg = EmailMultiAlternatives(subject, text_content, from_email, [to], reply_to=["admin#example.com"])
msg.attach_alternative(html_content, "text/html")
msg.send()
And inside that template you construct reverse url:
{% url 'app:grant_access' allow_token=token %} # "token" you get from context
{% url 'app:deny_access' deny_token=token %} # will become example.com/deny_access/7ea3c95, where 7ea3c95 is token
Then add lines to urls.py of your app like that:
url(r'^allow_access/(?P<allow_token>[0-9]+)$', CheckAcessView.as_view(), name="app:grant_access"),
url(r'^deny_access/(?P<deny_token>[0-9]+)$', CheckAcessView.as_view(), name="app:deny_access"),]
Then create CheckAcessView view. Where you access request stored in your database and check if, for example, parameter of url "allow_token" is equal stored allow_token. If so, change request status to allowed.
I created a profile page with django to let users change their information that I gave in sing up form . (such as name, email, password)
I have two problem on password field :
1 - when user insert a password in text field, it's submitted in raw format, and I need django user's table format
<algorithm>$<iterations>$<salt>$<hash>
my view for profile page :
def user_profile(request):
current_user = request.user
form = UserProfileForm(request.POST or None, instance=current_user)
if request.POST:
if form.is_valid():
# pwd = form.cleaned_data['password']
# form_obj = form.save(commit=False)
# form_obj.password = make_password(pwd)
form.save()
message = "saved successfully"
return render(request, 'Profile.html', {'form':form, 'message':message}, context_instance=RequestContext(request))
return render_to_response('Profile.html', {
'form': form,
}, context_instance=RequestContext(request))
as you can see in comments, I used make_password function to hash password and it works fine BUT after submitting page, user can't go to other pages and need re-login ... why ?!
2 - when the profile page shows to user, it's filled with current informations in database, and password is also in the above format (hash)
and if user submit the form, without any change in password field, it's password changed (it sends hash one and hash it again !)
how can I solve this problems and make a simple working profile page in django ? (I really don't like it's own admin panel ! it doesn't look nice !)
From the documentation:
Changing a user’s password will log out all their sessions if the SessionAuthenticationMiddleware is enabled.
So you have to use update_session_auth_hash(request, user)
For more info, see https://docs.djangoproject.com/en/1.9/topics/auth/default/#session-invalidation-on-password-change
Regarding the password field being pre-populated: you should set the field as a passwordInput which, by default, is not pre-populated, see https://docs.djangoproject.com/en/1.9/ref/forms/widgets/#django.forms.PasswordInput
I've been trying to make django send a mail to one user once the user input his email but it keeps failing. For example, user A signup to my list and django sent him a mail. User B signup to my list and django sends both user A and B a mail.
I don't want that kind of process, I want to send a mail to each user once they fill in their email address. So that when another user signup django won't send the current user and the ones already on the database the same mail.
Below are my codes:
send invitation
Subject='Join Me'
message=loader.get_template('letter.txt')
from_email='test#testing.com'
def invite_me(request):
if request.method=="POST":
form=InviteForm(request.POST)
if form.is_valid():
form.save()
#get input data and send email to the user.
send_mail(Subject,message.render(Context()),from_email,Invite.objects.values_list('email_address', flat=True))
return HttpResponse('Thanks For Inputting Your Email, Go Check Your Inbox!')
else:
return HttpResponse('Invalid Email Address')
else:
form=InviteForm()
return render_to_response('home.html',{'InviteForm':InviteForm},context_instance=RequestContext(request))
You are using: Invite.objects.values_list('email_address', flat=True), which returns a List of all the email_address fields for all the Invites in your database.
This means that all the registered Invite's will receive an email.
I assume InviteForm is a ModelForm for you Invite object. ModelForm.save returns the newly created object, so you should be doing:
invite = form.save()
send_mail(Subject,message.render(Context()),from_email,[invite.email_address])
Remember that send_mail expects an iterable, so using a list here is required, this is achieved by using [invite.email_address] and not just invite.email_address.