How to get rid of the csrf validation with EmailMultiAlternatives in DJANGO? - django

I want to send a html email to some address.
This is part of my code:
views.py
def addNewEvent(request):
try:
eventStatus = EventStatus.objects.filter(event=request.GET["id"])[0]
#try to send the mail
html = get_template('mail.html')
d = Context({ 'username': usuario.name,'title':'Testing mail!!','eventStatusId': str(eventStatus.id)})
html_content = html.render(d)
msg = EmailMultiAlternatives('Testing yo','Skyforger!', 'mymail#gmail.com', [mail2#gmail.com])
msg.attach_alternative(html_content, "text/html")
msg.send()
print('EventStatus id '+str(eventStatus.id))
except Exception, e:
print ('the error %s',(str(e)))
response = getBaseJSON()
response["event_id"]= eventStatus.id
return HttpResponse(json.dumps(response), content_type="application/json")
mail.html
<html>
<body>
<h1>{{title}}</h1>
<h2>Hi {{username}}</h2>
<h3>Message to friend</h3>
<form action="http://localhost:8000/confirmEvent" method="POST">
{% csrf_token %}
<input type="hidden" name="id" value="{{eventStatusId}}">
<textarea name="message_to_friend"></textarea><br>
<input type="submit" value="I'LL BE THERE!!">
</form>
</body>
</html>
The mail is sent OK, but when its form is submitted, this error is displayed:
Forbidden (403)
CSRF verification failed. Request aborted.
I can't find how to solve that error.
I followed many answers like these:
https://stackoverflow.com/a/10388110
Forbidden (403) CSRF verification failed. Request aborted. Even using the {% csrf_token %}
with no success.
How can I send the form inside the html mail avoiding the CSRF error.

You can use the csrf_exempt decorator to disable CSRF protection for a particular view.
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def someview():
Its not recommended to disable csrf , but you can try this if you want :)

I replaced render() by render_to_string() with the param context_instance=RequestContext(request) and now this is working.
def addNewEvent(request):
try:
eventStatus = EventStatus.objects.filter(event=request.GET["id"])[0]
#try to send the mail
html = get_template('mail.html')
d = Context()
html_content = render_to_string('mail.html',{ 'username': usuario.name,'title':'Testing mail!!','eventStatusId': str(eventStatus.id)}, context_instance=RequestContext(request))
msg = EmailMultiAlternatives('Testing yo','Skyforger!', 'mymail#gmail.com', [mail2#gmail.com])
msg.attach_alternative(html_content, "text/html")
msg.send()
print('EventStatus id '+str(eventStatus.id))
except Exception, e:
print ('the error %s',(str(e)))
response = getBaseJSON()
response["event_id"]= eventStatus.id
return HttpResponse(json.dumps(response), content_type="application/json")

Related

Variable 'html' referenced before assigned: UnboundLocalError

This code previously worked and outputed what I wanted on the website, but then this error happened
from django.shortcuts import render
import json
def get_html_content(fplid):
import requests
API_KEY = "eb9f22abb3158b83c5b1b7f03c325c65"
url = 'https://fantasy.premierleague.com/api/entry/{fplid}/event/30/picks/'
payload = {'api_key': API_KEY, 'url': url}
for _ in range(3):
try:
response = requests.get('http://api.scraperapi.com/', params= payload)
if response.status_code in [200, 404]:
break
except requests.exceptions.ConnectionError:
response = ''
#userdata = json.loads(response.text)
return response.text
def home(request):
if 'fplid' in request.GET:
fplid = request.GET.get('fplid')
html = get_html_content(fplid)
return render(request, 'scrape/home.html', {'fpldata': html})
here is my views.py file. I think I assigned html before, but I'm not sure, how is it referenced before it renders. I added scraperapi for many ip addresses, as I thought maybe I was banned from the api. I am unsure what is going on.
<body>
<h1>Enter Your FPL id </h1>
<form method="GET">
<label for="fplid"> </label>
<input type="text", name="fplid", id="fplid"> <br>
<input type="submit" value="Submit" />
</form>
<h3> {{fpldata}}</h3>
</body>
This is a part of the home.html file if it is relevant
When you initially load the page there probably wont'be an initialized ?fplid=xx. When this isn't present the variable is not assigned a value.
You could initialize the variable with html = None or this:
def home(request):
if 'fplid' in request.GET: # <- when this isnt true
fplid = request.GET.get('fplid')
html = get_html_content(fplid)
return render(request, 'scrape/home.html', {'fpldata': html})
return render(request, 'scrape/home.html')

Unittest Flask-App CSRF-token missing after Post-Request

When unittesting my flask-app and sending post-requests with a Testclient I find a form.error that the CSRF token is missing.
I have been to this and to this post, also read the documentation, however I still cant get my Problem solved.
How the CSRF-Protection is created:
class Routes:
app = Flask(__name__)
app.config['SECRET_KEY'] = WebserverConfig().secret_key
CSRFProtect().init_app(__app)
The view to be tested:
#__app.route("/settings", methods=["GET", "POST"])
def settings():
form = SettingsForm()
if request.method == "POST":
if form.validate_on_submit():
Routes.__reqhandler.put_settings(form=form)
return redirect(url_for("settings"))
if form.errors != {}:
Routes.__reqhandler.error_put_settings(form=form)
data = Routes.__prepdata.prep_settings()
return render_template("settings.html", form=form, data=data)
The template which contains the form:
<form method="POST">
{{ form.hidden_tag() }}
<div class="text-center">
<h3>Einstellungen</h3>
</div>
{{ form.intervall.label }}
{{ form.intervall(id="intervall", class="form-control", placeholder=data["intervall"])}}
</form>
The fixture creating the testclient:
#pytest.fixture
def client():
app = Routes().get_app()
app.config["WTF_CSRF_METHODS"] = []
with app.test_client() as client:
yield client
The test:
def test_settings_valid_intervall(client):
res = client.post("settings", data={"intervall": "00:01:00"}, follow_redirects=True)
assert b'value="00:01:00' in res.data
When printing the data (print(res.data) an alert (created with a flash if there are errors in a form) with the info that "The CSRF token is missing" is sent. What am I missing to tell form.validate_on_submit to not check if a valid CSRF-token was passed?
I solved it by not only emptying the list of the WTF_CSRF_METHODS but also disabling CSRF within the fixture that creates the client:
app.config["WTF_CSRF_ENABLED"] = False

Error in Django to show the sender's email

I am trying to view the sender's email that is being sent to the email host. I am able to send emails to the email_host_user using environment variables and have allowed access for less secure apps in Yahoo account. However, when I do the "from_email" I received an error: SMTPSenderRefused--(550, b'Request failed; Mailbox unavailable', 'bill#yahoo.com')--'bill#yahoo.com is just a random sender's email though I am not able to send to the email_host. I think there must be some issues with my views.py? I appreciate your feedback!
settings.py
EMAIL_HOST='smtp.mail.yahoo.com'
EMAIL_HOST_USER=os.environ.get('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD=os.environ.get('EMAIL_HOST_PASSWORD')
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend'
print(os.environ.get('EMAIL_HOST_USER'))
print(os.environ.get('EMAIL_HOST_PASSWORD'))
print(os.environ.get('SECRET_KEY'))
views.py
def contact(request):
if request.method=='POST':
message=request.POST.get('message', '')
from_email=request.POST.get('from_email', '')
send_mail('Contact Form',
message,
from_email,
[settings.EMAIL_HOST_USER],
fail_silently=False
)
return render(request, 'first_app/contact.html')
contact.html
<form action="/contact" method="POST">
{% csrf_token %}
<input type="email" name="from_email" placeholder="Your email">
<textarea name="message" placeholder="Message...">
</textarea>
<input type="submit">
</form>
Yahoo won't allow you to send email from an unauthorized email. You can only use your EMAIL_HOST_USER email address to send emails.
What you can do is set EMAIL_HOST_USER as sender and add a Reply-To header with the email address provided through the form.
You should use EmailMessage for this
from django.core.mail import EmailMessage
def contact(request):
if request.method=='POST':
message=request.POST.get('message', '')
from_email=request.POST.get('from_email', '')
email = EmailMessage(
subject='Contact Form',
message=message,
from_email=settings.EMAIL_HOST_USER,
recipient_list=[settings.EMAIL_HOST_USER'],
reply_to=[from_email]
)
email.send(fail_silently=False)
return render(request, 'first_app/contact.html')
docs

Django request.method automatically set to GET and not POST

I'd like to setup an LDAP Authentication Backend in Django, and I've already used ldap3 to confirm a bind, with success.
I'm now realising that writing a class for my LDAP Backend with just ldap3 is not so straightforward, and that installing
django_auth_ldap could be another route to explore.
I've tested already some code to create a bind to the LDAP "server", and then perform a simple search. All okay. This method I tested is outside of my Django framework.
When implementing the same method into my Django framework, I encounter an issue when the method gets called. Because of the print statements, I know the view is getting called as exptected, but the part of the code whereby the first "if" statement should be executed, does not get called unless I change from "POST"
to "GET". But then this seems to create the next issue (when I set to "GET", so to force the next lines to be executed), because I'd like that a login page then gets called, where I can then input my LDAP credentials,
ultimately confirming the LDAP connection. Here is my code:
views.py
def login_ldap(request):
LDAP_SERVER = '10.222.4.88'
searchFilter='random'
print (request)
print (LDAP_SERVER)
print ("request.method:{0}".format(request.method))
if request.method == "GET":
print ("if statement executed")
username = request.GET['username']
print ("U:{0}".format(username))
password = request.GET['password']
print ("P:{0}".format(password))
# Define the server and bind_dn
server = Server(LDAP_SERVER, get_info=ALL)
bind_dn = 'cn={0}, ou=Prod, ou=Extern, ou=User, ou=ABC, dc=DEF, dc=com'.format(username)
# Define the Connection
conn = Connection(server, bind_dn, password, auto_bind=True) # Use raise_exceptions=True for exceptions
print ("search: {0}",format(conn.search))
print ("conn: {0}",format(conn))
conn.start_tls() #Session now on a secure channel. See output from following print statement and "tls started"
print ("conn_tls: {0}",format(conn))
d = conn.extend.standard.who_am_i()
print (d)
#print ("Server Info: {0}",format(server.info))
conn.open()
conn.bind()
# The LDAP search base for looking up users.
LDAP_AUTH_SEARCH_BASE = "ou=ABC, dc=DEF, dc=com"
if conn.bind():
conn.search(
search_base=LDAP_AUTH_SEARCH_BASE,
search_filter= '(cn={})'.format(searchFilter), # This is the user being searched for
search_scope=SUBTREE # BASE & LEVEL also possible settings
)
entry = conn.entries[0]
res = conn.bind()
print (res)
return render(request, 'search_page.html', {'entry':entry})
The error message received on my webpage:
MultiValueDictKeyError at /login_ldap/
"'username'"
Request Method: GET
Request URL: http://127.0.0.1:8000/login_ldap/
Django Version: 1.11
Exception Type: MultiValueDictKeyError
Exception Value:
"'username'"
I assume this is related to the GET and no longer the POST method. Why is the request.method being automatically set to GET and not POST when implementing
this in Django? Is this class for the authentication in the correct location, in the views.py file or should there be a separate file for this?
What should be included in the settings.py exactly (related to BACKEND_AUTH)?
EDIT:
views.py
def login_view(request):
if request.POST:
username = request.POST['username']
print ("U:{0}".format(username))
password = request.POST['password']
print ("P:{0}".format(password))
user = authenticate(username=username, password=password)
print (user)
if user is not None:
if user.is_active:
login(request, user)
return redirect('index')
else:
messages.error(request, "User is not active in Database")
else:
print ("Please check your credentials!")
messages.error(request, "Please check your username and password!")
return render(request, 'login.html')
index.html
<div class="row">
<div class="col-lg-3"></div>
<div class="col-lg-6"><H1>This is the public page, Please Login</H1></div>
</div>
urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login_ldap/$', login_ldap, name='login_ldap'),
url(r'^login/$', login_view, name='login'),
url(r'^logout/$', logout_view, name='logout'),
url(r'^change_password/$', change_password, name='change_password'),
url(r'^$', index, name='index'),
]
settings.py
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
)
search_page.html
{% load static %}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link href='{% static "login.css" %}' rel="stylesheet">
<div class="wrapper">
<form method='post' action="" class="form-signin">{% csrf_token %}
<h2 class="form-signin-heading">Please login</h2>
<input type="text" class="form-control" name="username" placeholder="Username" required="" autofocus=""/>
<br>
<input type="password" class="form-control" name="password" placeholder="Password" required=""/>
<br>
<button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>
</form>
</div>
You can use get() function to get the data.
if request.method == "GET":
print ("if statement executed")
username = request.GET.get('username')
For more reference you can see here,
MultiValueDictKeyError in Django
And about POST method you may require to import the csr_exempt and use the decorator before your view.
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def myView(request):

Django 1.9.2 "reverse" error during password reset attempt

My app sits on Django 1.9.2 and Django REST Framework 3.3.2, with a single page app on the front-end. I must admit I'm new to Django but the docs are unmatched.
I'm trying to implement a custom PasswordResetForm view. My strategy is as follow:
User uses on a front-end form that POSTs data (email string) to an API endpoint (api/v1/password/reset) when she wants to reset her password.
If email is found in DB, send an email and return a successful response.
For part 1, here's the relevant code:
# urls.py
url(r'^api/v1/password/reset/?$', PasswordResetView.as_view(), name='password-reset-link')
-
# views.py
class PasswordResetView(GenericAPIView):
serializer_class = PasswordResetSerializer
permission_classes = (AllowAny,)
def post(self, request, *args, **kwargs):
# Use DRF serializer to validate data.
serializer = self.get_serializer(data=request.data)
# Ensure data is valid before proceeding.
if serializer.is_valid() == False:
return Response('error': serializer.errors, 'message': 'Error while attempting to reset password'}, status.HTTP_400_BAD_REQUEST)
else:
# Ensure the user exists.
existing_user = get_user_model().objects.filter(
email=serializer.validated_data.get('email')
)
if not existing_user:
return Response({'error': {'code': 'email_not_in_db'}}, status.HTTP_404_NOT_FOUND)
# Validated data fed to a child class of PasswordResetForm.
form = UserForgotPasswordForm(serializer.validated_data)
if form.is_valid():
path = os.path.join(os.path.dirname(os.path.abspath(__file__ + '../../')), 'templates/registration/password_reset_email.html')
try:
# Save form, effectively attempting to trigger mail sending.
# Unfortunately an exception gets thrown!
form.save(from_email='no-reply#abc.xyz', email_template_name=path, request=request)
return Response({'message': 'Password reset request sent'}, status=status.HTTP_200_OK)
except Exception as e:
return Response({'error': str(e), 'message': 'Error while attempting to reset password'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
-
# forms.py
class UserForgotPasswordForm(PasswordResetForm):
email = forms.EmailField(required=True, max_length=254)
class Meta:
model = CustomUser
fields = ('email',)
-
# my_app/templates/registration/password_reset_email.html
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you requested a password reset for your user account.{% endblocktrans %}
{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'password-reset-link' uidb64=uid token=token %}
{% endblock %}
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}
{% endautoescape %}
-
But I get the following error:
Reverse for 'password-reset-link' with arguments '()' and keyword arguments '{'uidb64': b'MTI1', 'token': '4g9-f370fd6ee48d90a40b67'}' not found. 1 pattern(s) tried: ['api/v1/password/reset/?$']
Any idea why? What am I missing? Thanks for your help.
In the url in your html you are passing a token and a uidb64:
{{ protocol }}://{{ domain }}{% url 'password-reset-link' uidb64=uid token=token %}
However, in your actual url, you do not reference the token or uidb64:
url(r'^api/v1/password/reset/?$', PasswordResetView.as_view(), name='password-reset-link')
I would recommend changing your url to:
url(r'^api/v1/password/reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', PasswordResetView.as_view(), name='password-reset-link')
Your URL named password-reset-link doesn't support the keyword arguments uidb64 and token that you're sending it. Those keyword arguments aren't just extra data, they need to match somewhere in the URL pattern.
According to the error, your URL pattern is:
api/v1/password/reset/?$
In order to support what you want you'd need to put two named arguments called <uidb64> and <token>. For example:
api/v1/password/reset/(?P<uidb64>[a-zA-Z0-9]+)/(?P<token>[a-zA-Z0-9]+)/?$
You'll have to adjust your regex to support the uidb64 and token formats.