All responses are now json. Added a honeypot field

This commit is contained in:
Adrian Amaglio 2020-09-16 14:49:15 +02:00
parent 3e4e3854d4
commit 4fe3ce5652
3 changed files with 54 additions and 55 deletions

View File

@ -1,6 +0,0 @@
<h2>Liste</h2>
<ul>
% for item in data:
<li>{{item}}</li>
% end
</ul>

97
main.py
View File

@ -11,7 +11,6 @@ import pymongo # database
from dotenv import load_dotenv
import random, string # for tokens
import html # for sanitization
import datetime # to name unsent mails
from bson.json_util import dumps
@ -114,7 +113,7 @@ def submission ():
token = request.forms.getunicode('token')
else:
response.status = 400
return 'Le jeton dautentification est requis'
return resp('error', 'Le jeton dautentification est requis')
# Getting mail address
if 'mail' in request.forms:
@ -128,19 +127,22 @@ def submission ():
form = mongodb_database['forms'].find({'token': token})[0]
except IndexError as e:
response.status = 400
return 'Le formulaire demandé est introuvable, merci de vérifier que le token utilisé est le bon'
return resp('error', 'Le formulaire demandé est introuvable, merci de vérifier que le token utilisé est le bon')
except pymongo.errors.ServerSelectionTimeoutError as e:
response.status = 500
return 'La base de donnée nest pas accessible.'
return resp('error', 'La base de donnée nest pas accessible.')
try:
subject_fields = fill_fields(request, get_fields(form['subject']))
content_fields = fill_fields(request, get_fields(form['content']))
print(subject_fields)
print(content_fields)
# Did the bot filled the honeypot field?
if 'honeypotfield' in form and form['honeypotfield'] in request.forms and request.forms.get(form['honeypotfield']) != '':
response.status = 400
return resp('error', 'We identified you as a bot. If this is an error, try to contact us via another way.')
except MissingParameterException as e:
response.status = 404
return str(e)
return resp('error', str(e))
subject = re.sub(form_regex, r'{\1}', form['subject']).format(**subject_fields)
content = re.sub(form_regex, r'{\1}', form['content']).format(**content_fields)
@ -148,33 +150,26 @@ def submission ():
try:
if not send_mail(from_address, form['mail'], subject, content):
response.status = 500
return 'Le mail na pas pu être envoyé.'
return resp('error', 'Le mail na pas pu être envoyé.')
except smtplib.SMTPDataError as e:
save_mail (token, form['mail'], from_address, subject, content)
response.status = 500
error = 'Le mail a été refusé. Votre message a été enregistré, il sera remis manuellement à son destinataire.'
error = 'Le mail a été refusé. Merci de réessayer plus tard.'
except smtplib.SMTPRecipientsRefused as e:
save_mail (token, form['mail'], from_address, subject, content)
response.status = 500
error = 'Impossible de trouver le destinataire du mail. Votre message a été enregistré, il sera remis manuellement à son destinataire.'
error = 'Impossible de trouver le destinataire du mail. Merci de réessayer plus tard'
except Exception as e:
save_mail (token, form['mail'], from_address, subject, content)
raise
# Redirection
#bottle.redirect(success_redirect_default)
origin = request.headers.get('origin')
return '<p>Mail envoyé !</p>' + ('<p>Retour au <a href="{}">formulaire de contact</a></p>'.format(origin) if origin else '')
return resp('success', 'Mail envoyé !')
##################################################### Helpers ############################################
def save_mail (token, to, from_address, subject, content):
with open('unsent/unsent_{}_{}_{}.txt'.format(str(datetime.datetime.now()), token, to), 'w') as f:
f.write("Unsent mail\nSubject: {}\nFrom: {}Content:\n{}".format(
subject,
from_address,
content
))
def resp (status, msg, data='{}'):
return '{{"status": "{}", "msg": "{}", "data": {}}}'.format(status, msg, data)
def get_fields (string):
""" Parse the string looking for template elements and create an array with template to fill and their default values. None if mandatory. """
@ -255,26 +250,31 @@ def create_form ():
subject = mail_default_subject
else:
response.status = 400
return 'Le champs « sujet » est requis'
return resp('error', 'Le champs « sujet » est requis')
# Getting mail content
if 'content' in request.forms:
content = request.forms.getunicode('content')
else:
response.status = 400
return 'Le champs « contenu » est requis'
return resp('error', 'Le champs « contenu » est requis')
if 'honeypotfield' in request.forms:
honeypotfield = request.forms.getunicode('honeypotfield')
else:
honeypotfield = None
# Getting from address
if 'mail' in request.forms:
mail = request.forms.getunicode('mail')
else:
response.status = 400
return 'Le champs « adresse » est requis'
return resp('error', 'Le champs « adresse » est requis')
user = login(request)
if user['_privilege'] > 1:
response.status = 400
return 'Privilèges insufisants'
return resp('error', 'Privilèges insufisants')
# TODO limit the insertion rate
token = ''.join(random.sample(token_chars, token_len))
@ -285,12 +285,13 @@ def create_form ():
'subject': subject,
'user_id': user['_id'],
'token': token,
'honeypotfield': honeypotfield,
})
except pymongo.errors.ServerSelectionTimeoutError as e:
response.status = 500
return 'La base de donnée nest pas accessible'
return resp('error', 'La base de donnée nest pas accessible')
return 'Créé : ' + token
return resp('success', 'Créé : ' + token)
@app.post('/form/list')
def list_forms ():
@ -302,12 +303,12 @@ def list_forms ():
filt = {'user_id': user['_id']}
else:
response.status = 400
return 'Privilèges insufisants'
return resp('error', 'Privilèges insufisants')
data = mongodb_database['forms'].find(filt)
return dumps(list(data))
return resp('success','', dumps(list(data)))
except pymongo.errors.ServerSelectionTimeoutError as e:
response.status = 500
return 'La base de donnée nest pas accessible'
return resp('error','La base de donnée nest pas accessible')
@ -317,17 +318,17 @@ def delete_form(token):
user = login(request)
if user['_privilege'] > 1:
response.status = 400
return 'Privilèges insufisants'
return resp('error', 'Privilèges insufisants')
# Actually delete
try:
form = mongodb_database['forms'].find({'token':token })[0]
except IndexError as e:
response.status = 400
return 'Le token nest pas valide'
return resp('error', 'Le token nest pas valide')
except pymongo.errors.ServerSelectionTimeoutError as e:
response.status = 500
return 'La base de donnée nest pas accessible'
return resp('error', 'La base de donnée nest pas accessible')
if user['_privilege'] == 0 or (form['user_id'] == user['_id']):
try:
@ -336,10 +337,10 @@ def delete_form(token):
})
except pymongo.errors.ServerSelectionTimeoutError as e:
response.status = 500
return 'La base de donnée nest pas accessible'
return 'Supprimé ' + token
return resp('error', 'La base de donnée nest pas accessible')
return resp('success', 'Supprimé ' + token)
response.status = 400
return 'Privilèges insufisants'
return resp('error', 'Privilèges insufisants')
##################################################### Users ############################################
@ -349,13 +350,13 @@ def list_users ():
user = login(request)
if user['_privilege'] > 0:
response.status = 400
return 'Privilèges insufisants'
return resp('error', 'Privilèges insufisants')
try:
data = mongodb_database['users'].find()
return dumps(list(data))
return resp('success', '', dumps(list(data)))
except pymongo.errors.ServerSelectionTimeoutError as e:
response.status = 500
return 'La base de donnée nest pas accessible'
return resp('error', 'La base de donnée nest pas accessible')
@app.route('/user/<username>', method=['OPTIONS', 'PUT'])
@ -363,23 +364,23 @@ def create_user (username):
user = login(request)
if user['_privilege'] > 0:
response.status = 400
return 'Privilèges insufisants'
return resp('error', 'Privilèges insufisants')
try:
mongodb_database['users'].find({'username': username})[0]
return 'Lutilisateur existe déjà'
return resp('error', 'Lutilisateur existe déjà')
except IndexError as e:
try:
inserted = mongodb_database['users'].insert_one({
'username': username,
'token': ''.join(random.sample(token_chars, token_len))
})
return 'Créé : ' + username
return resp('success', 'Créé : ' + username)
except pymongo.errors.ServerSelectionTimeoutError as e:
response.status = 500
return 'La base de donnée nest pas accessible'
return resp('error', 'La base de donnée nest pas accessible')
except pymongo.errors.ServerSelectionTimeoutError as e:
response.status = 500
return 'La base de donnée nest pas accessible'
return resp('error','La base de donnée nest pas accessible')
@app.delete('/user/<username>')
@ -387,19 +388,19 @@ def delete_user (username):
user = login(request)
if user['_privilege'] > 0:
response.status = 400
return 'Privilèges insufisants'
return resp('error', 'Privilèges insufisants')
try:
mongodb_database['users'].find({'username': username})[0]
mongodb_database['users'].delete_one({
'username': username,
})
return 'Supprimé ' + username
return resp('success', 'Supprimé ' + username)
except IndexError as e:
response.status = 400
return 'Lutilisateur nexiste pas'
return resp('error', 'Lutilisateur nexiste pas')
except pymongo.errors.ServerSelectionTimeoutError as e:
response.status = 500
return 'La base de donnée nest pas accessible'
return resp('error', 'La base de donnée nest pas accessible')

View File

@ -7,11 +7,15 @@
<body>
<div id="contact-mailer-message"></div>
<form action="http://localhost:8080/submit" method="POST" id="contact-mailer-form">
<input type="hidden" name="token" value="M9zyPf4sm6opGe58vbNql0S2UktQIdBOYAaL1VJhKXFwxnRZug" />
<input type="hidden" name="token" value="sYMXDz5UKuRF38LbQl20ikrmp7nhHcxTCgGZodqAaBtSvPOV4f" />
<div>
<label for="nom">Votre nom&nbsp;:</label>
<input type="text" name="nom" required="required"/>
</div>
<div>
<label for="nom">Votre prénom&nbsp;:</label>
<input type="text" name="prenom"/>
</div>
<div>
<label for="mail">Adresse mail&nbsp;:</label>
<input type="email" name="mail" required="required"/>