Django AllAuth - How to manually send a reset-password email? - python

In my application I am using Django Allauth. I don't have any registration form for users. The admin is going to register users by uploading an excel file that contains user info. I have done all of this and users are saved in the user table by auto generating passwords. After I upload user lists and save them in database, I want to send a reset password email to each user.
In allauth to reset password you first need to go to reset page account/password/reset/ and type your email. then an email is send which directs you to change your password account/password/reset/key/(?P<uidb36>[0-9A-Za-z]+)-(?P<key>.+)/
Is it possible to send the email directly within the app? The url contains a key that I don't know how to generate!! Or is there any better way to do that?

It's possible. My solution implements a User model post_save signal to call the Allauth Password reset view which will send the user the email. The first thing to consider is to make the user email address mandatory in the admin user create form (as explained here). And then use this code:
from allauth.account.views import PasswordResetView
from django.conf import settings
from django.dispatch import receiver
from django.http import HttpRequest
from django.middleware.csrf import get_token
#receiver(models.signals.post_save, sender=settings.AUTH_USER_MODEL)
def send_reset_password_email(sender, instance, created, **kwargs):
if created:
# First create a post request to pass to the view
request = HttpRequest()
request.method = 'POST'
# add the absolute url to be be included in email
if settings.DEBUG:
request.META['HTTP_HOST'] = '127.0.0.1:8000'
else:
request.META['HTTP_HOST'] = 'www.mysite.com'
# pass the post form data
request.POST = {
'email': instance.email,
'csrfmiddlewaretoken': get_token(HttpRequest())
}
PasswordResetView.as_view()(request) # email will be sent!

You can try to get URL for specific user using something like this:
from allauth.account.forms import EmailAwarePasswordResetTokenGenerator
from allauth.account.utils import user_pk_to_url_str
token_generator = EmailAwarePasswordResetTokenGenerator()
user = User.objects.get(email='example#example.com')
temp_key = token_generator.make_token(user)
path = reverse("account_reset_password_from_key",
kwargs=dict(uidb36=user_pk_to_url_str(user), key=temp_key))

Following from #davecaputo's answer, you can also directly submit the form instead of creating and calling the view:
from allauth.account.forms import ResetPasswordForm
from django.conf import settings
from django.http import HttpRequest
def send_password_reset(user: settings.AUTH_USER_MODEL):
request = HttpRequest()
request.user = user
request.META["HTTP_HOST"] = "www.mysite.com"
form = ResetPasswordForm({"email": user.email})
if form.is_valid():
form.save(request)

Related

How can i make a post request to a rest api endpoint and the request to save my form in a view file in django?

So i am trying to send a JSON file to a rest API endpoint and i am trying to use the requests library and it doesn't work.
Also in the function i have a return render to another page, because when i save a form i want also to send a request to the rest api to send an email and they only send emails via the endpoint.
from django.shortcuts import render
from django.views.generic import View
from Upload_Page import models
from django.core.files.storage import FileSystemStorage
from Upload_Page.forms import Upload_Document
from django.shortcuts import redirect
#from django.core.mail import send_mail
from Upload_Page.mail import mail_class
import requests
import json
def upload_doc(request):
if request.method == 'POST':
form = Upload_Document(request.POST, request.FILES)
if form.is_valid():
form.save()
mail=mail_class()
jsonmail=mail.createSimpleMail()
requests.post("http://mail.internal.de1.bosch-iot-cloud.com:80/email",data=json.dumps(jsonmail))
#send_mail('Title','Body','DigitalLibrary.BoschECC#bosch.com',['fixed-term.Razvan.Sabou#ro.bosch.com'],fail_silently=False)
return render(request,'Upload_Page/upload_successful.html')
else:
form = Upload_Document(auto_id=True,label_suffix='')
return render(request, 'Upload_Page/upload_page.html', {'form':form})
So this is the code, and i do not know why it doesn't send the post request, i checked the terminal to see what requests are made and the request from the requests it doesn't appear.
I've searched but i am new in django and Rest API and i didn't find the answer for my problem, if you need any more information i can provide it to you.
So thank you for the answer and have a good day.

How to check django staff user first time login in admin panel?

I am working on a django project and I am using the default auth app for authentication.
I know that there is a last_login field in user model which stores the user's last login time.
When a staff user logs in first time into the admin panel, I want to check if last_login field is none & redirect him to the change password page.
Where should I put this check?
What I have tried so far:
I have tried to use a custom login form and override the default confirm_login_allowed method on it, but it seems like I can only raise a validation error to block login attempt using these.
I also tried using django.contrib.auth.signals.user_logged_in Signal but that also does not allow me to return a redirect response when last_login is None.
I want to know how I can return a redirect response after the user has been authenticated.
Customise Django admin using AdminSite and use login_form attribute to give the custom login form for the Admin login page.
admin.py
class MyAdminSite(AdminSite):
login_form = CustomAdminLoginForm
admin_site = MyAdminSite(name='myadmin')
admin_site.register(User)
admin_site.register(Group
urls.py
When overriding the Admin we have to get rid of Django default admin
from app.admin import admin_site
url(r'^admin/', admin_site.urls)
forms.py
AuthenticationForm have the confirm_login_allowed method use this to grant permission to login in or not login in.
class CustomAdminLoginForm(AuthenticationForm):
def confirm_login_allowed(self, user):
if user.last_login:
raise ValidationError(mark_safe('Hey first time user please reset your password here... test'), code='inactive')
Note: There is lot of edge cases you have to consider for this approach.
What if user not set the password in the first time and how you're going to handle second attempt..? This time last_long not None. Although date_joined comes rescue. last_login == date_joined
But What if the user not set the password in first day and comes next day ?
Edit:
You can use signal to check the logged in user and apply the config_login_allowed logic here...?
from django.contrib.auth.signals import user_logged_in
def change_password_first_time(sender, user, request, **kwargs):
# Your business logic here...
user_logged_in.connect(change_password_first_time)
Django admin is not that configurable. You should hook into its internal views. Login/logout views are inside Django AdminSite class (the one you usually access by admin.site). My implementation is a bit hacky but small:
Paste at the top of urls.py
from django.contrib import admin
from django.contrib.admin import AdminSite
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
class MyAdminSite(AdminSite):
def login(self, request, extra_context=None):
new_user = False
user = None
username = request.POST.get('username') # Hack to find user before its last_login set to now.
if username:
user = User.objects.filter(username=username).first()
if user:
new_user = user.last_login is None
r = super(MyAdminSite, self).login(request, extra_context)
if new_user and request.user == user and isinstance(r, HttpResponseRedirect): # Successful logins will result in a redirect.
return HttpResponseRedirect(reverse('admin:password_change'))
return r
admin.site = MyAdminSite()
If you want a cleaner solution I suggest to use a boolean inside user model instead of relying on last_login so you could just check request.user instead of my hack into request.POST.
You could read AdminSite.login and django.contrib.auth.views.login to see what is actually happening inside Django.
It was a big problem for me at once. I found a way to do it easy. Create a different variable to save the value of the users logged in data, when user is trying to login.
Below is my code:
if user.last_login is None:
auth.login(request,user)
return redirect(alertPassword)
# when user is login for the first time it will pass a value
else:
auth.login(request,user)
return redirect(moveToLogin)
def moveToLogin(request):
return render(request,"home.html")
def alertPassword(request):
first_login=True
return render(request,"home.html",{"first_login":first_login})
#it will pass a value to the template called first login is True
Then go to your template and add this:
{% if first_login==True%}
# Put anything you want to be done
# In my case I wanted to open a new window by using script.
<script>
window.open("popUpWindow")
</script>
{%endif%}

Django retrieve sessionID created with Login

I am using Django 1.9.
Base on the Django documentation:
" To log a user in, from a view, use login(). It takes an HttpRequest object and a User object. login() saves the user’s ID in the session, using Django’s session framework."
So how do I retrieve the information in that session.
For example: (this is also taken from the documentation)
from django.contrib.auth import authenticate, login
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
**login(request, user)**
# Redirect to a success page.
else:
How do I retrieve the user information such as username or if possible the password out on the success page.
P.S
If you could also kinda explain the solution in layman term as possible
EDIT
I was playing around with the framework back then. So I was testing if it can be done.
You don't get it from the session.
The authentication middleware adds the current user to the request, as request.user.
(And you definitely do not ever retrieve or display the password.)

How to programmatically trigger password reset email in django 1.7.6?

I've encountered a problem where I had to load more than 200 new users into my django app and right away send them a password reset email. This had to happen only once, done only by me and run quietly on backend. Surfing the internet brought me only to one more or less right answer: Trigger password reset email in django without browser?. The only problem was is that this post was about 4 years old and of course when I tried the solution, it didn't work...
Two most valuable points from the link I mentioned:
In most recent version of Django, we need to call form.is_valid()
Sending of email is done upon save().
Here is how I queried users that I needed and sent each of them a password reset link:
def find_users_and_send_email():
from django.http import HttpRequest
from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.models import User
import logging
logger = logging.getLogger(__name__)
users = User.objects.filter(date_joined__gt = '2015-04-16')
for user in users:
try:
if user.email:
logger.info("Sending email for to this email:", user.email)
form = PasswordResetForm({'email': user.email})
assert form.is_valid()
request = HttpRequest()
request.META['SERVER_NAME'] = 'help.mydomain.com'
request.META['SERVER_PORT'] = '443'
form.save(
request= request,
use_https=True,
from_email="username#gmail.com",
email_template_name='registration/password_reset_email.html')
except Exception as e:
logger.warning(str(e))
continue
return 'done'
Usually PasswordResetForm works with a "request" from the front-end, which I didn't have. So I simply created one.
When I followed the example in the link, it failed.. It couldn't find server name in the request. (Makes sense because I instantiated my request out of nowhere)
When I fixed server name, it asked for the server port. Since I used https, my port is 443, otherwise, use default port.
If you use https, don't forget to indicate it when you save the form use_https=True
I found it useful to wrap #chabislav's answer up in a custom management command which can then be reused as necessary. Note that I eliminated the user object filter, as I knew that I wanted to send password reset instructions to all users.
# myproject/myapp/management/commands/send_password_reset_emails.py
from django.core.management.base import BaseCommand, CommandError
# I use a custom user model, so importing that rather than the standard django.contrib.auth.models
from users.models import User
from django.http import HttpRequest
from django.contrib.auth.forms import PasswordResetForm
class Command(BaseCommand):
help = 'Emails password reset instructions to all users'
def handle(self, *args, **kwargs):
users = User.objects.all()
for user in users:
try:
if user.email:
print("Sending email for to this email:", user.email)
form = PasswordResetForm({'email': user.email})
assert form.is_valid()
request = HttpRequest()
request.META['SERVER_NAME'] = 'www.example.com'
request.META['SERVER_PORT'] = 443
form.save(
request= request,
use_https=True,
from_email="admin#mysite.com",
email_template_name='registration/password_reset_email.html'
)
except Exception as e:
print(e)
continue
return 'done'
This can then be run easily with python manage.py send_password_reset_emails.
Note that I got an error message (550, b'5.7.1 Relaying denied'), which ended up being an issue with the way that my localhost email server was set up, but once that was cleared this worked quite well. Having it in a management command is also nice because it doesn't clog up code that you use on a regular basis (views).
If you are reading this with django 3.x or 4.x AND are using django-allauth, this will work for you:
from allauth.account.forms import ResetPasswordForm # note the different form import
form = ResetPasswordForm({"email": "some#mail.io"})
assert form.is_valid() # required because we need clean() executed
form.save(
request=None,
from_email="noreply#example.io",
)
Please note that this will take all values from the allauth config, using django.contrib.sites.

Creating a fake request to render a view to a string in django

Problem
I'd like to render an arbitrary view, by calling the view (capture the response and extract the rendered content), to a string, inside another view.
The problem being I'd like to have a dummy user "logged-in" during the rendering of that view, along with changing a few other minor things in the request.
What I'd like to avoid is building a request completely from scratch, as 90% of the request I'll have at hand in the parent view will be the same.
I'm wondering how I should go about this as far as best practice is concerned, as well as technically?
I'm currently thinking something like this: (But I can't help but feel this is horrible and there's got to be a better way, I just cannot think of it)
View stuff...
Log current user out
Create/Login dummy user
Somehow modify request a bit
Render view to string
Log out dummy user
Log back in original user
End of view stuff...
Any ideas? Or a pointer into a better direction?
Thanks,
dennmat
Depending upon how much you're relying on getting information from the request during the view, it might be best that you just create a function to do all of the view's dirty work, and have your real view just pass in the values it needs into this function. So, instead of a view function that takes a request, create another function that takes a user:
def _real_view(user):
return render_to_response('template.html', {'user' : user})
def view(request):
# Now both of these are possible...
response = _real_view(User.objects.get(5))
response = _real_view(request.user)
return response
Then, when you want to a view using a different user, you only need to grab the information for that user and pass it into the view. No need to change the current user or modify the request at all.
You don't actually need to log the current user out, you could just change the user in the HttpRequest object you plan to use to render the other view. You could do something like this:
from django.contrib.auth.models import User
from django.http import HttpResponse
def view_inside_a_view(request):
return HttpResponse('hello %s' % request.user)
def view(request):
# change to dummy user, or whoever
request.user = User.objects.get(id=1)
response = view_inside_a_view(request)
return HttpResponse('rendered view: %s' % response.content)
If you need to login your dummy user you could use django.contrib.auth.authenticate or or django.contrib.auth.login to do so. Example using login (avoids necessity of using dummy user's password):
from django.contrib.auth.models import User
from django.contrib.auth import login, get_backends
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
#login_required
def view_inside_a_view(request):
return HttpResponse('hello %s' % request.user)
def view(request):
# login dummy user
user = User.objects.get(id=2)
backend = get_backends()[0]
user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
login(request, user)
# change request's user
request.user = user
# get response from view
response = view_inside_a_view(request)
return HttpResponse('rendered view: %s' % response.content)
just use the test client - it's not only good for testing:
from django.test import Client
c = Client()
# login if necessary
c.login(username='dummy', password='user')
# get the view
response = c.get(reverse('my-view', args=[foo,bar])
html = response.content
Since 1.6 this is even officially documented, however it works in older versions too: https://docs.djangoproject.com/en/1.6/topics/testing/tools/#module-django.test.client

Categories

Resources