All responses are now json. Added a honeypot field
This commit is contained in:
parent
3e4e3854d4
commit
4fe3ce5652
6
list.tpl
6
list.tpl
@ -1,6 +0,0 @@
|
|||||||
<h2>Liste</h2>
|
|
||||||
<ul>
|
|
||||||
% for item in data:
|
|
||||||
<li>{{item}}</li>
|
|
||||||
% end
|
|
||||||
</ul>
|
|
97
main.py
97
main.py
@ -11,7 +11,6 @@ import pymongo # database
|
|||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
import random, string # for tokens
|
import random, string # for tokens
|
||||||
import html # for sanitization
|
import html # for sanitization
|
||||||
import datetime # to name unsent mails
|
|
||||||
from bson.json_util import dumps
|
from bson.json_util import dumps
|
||||||
|
|
||||||
|
|
||||||
@ -114,7 +113,7 @@ def submission ():
|
|||||||
token = request.forms.getunicode('token')
|
token = request.forms.getunicode('token')
|
||||||
else:
|
else:
|
||||||
response.status = 400
|
response.status = 400
|
||||||
return 'Le jeton d’autentification est requis'
|
return resp('error', 'Le jeton d’autentification est requis')
|
||||||
|
|
||||||
# Getting mail address
|
# Getting mail address
|
||||||
if 'mail' in request.forms:
|
if 'mail' in request.forms:
|
||||||
@ -128,19 +127,22 @@ def submission ():
|
|||||||
form = mongodb_database['forms'].find({'token': token})[0]
|
form = mongodb_database['forms'].find({'token': token})[0]
|
||||||
except IndexError as e:
|
except IndexError as e:
|
||||||
response.status = 400
|
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:
|
except pymongo.errors.ServerSelectionTimeoutError as e:
|
||||||
response.status = 500
|
response.status = 500
|
||||||
return 'La base de donnée n’est pas accessible.'
|
return resp('error', 'La base de donnée n’est pas accessible.')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subject_fields = fill_fields(request, get_fields(form['subject']))
|
subject_fields = fill_fields(request, get_fields(form['subject']))
|
||||||
content_fields = fill_fields(request, get_fields(form['content']))
|
content_fields = fill_fields(request, get_fields(form['content']))
|
||||||
print(subject_fields)
|
# Did the bot filled the honeypot field?
|
||||||
print(content_fields)
|
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:
|
except MissingParameterException as e:
|
||||||
response.status = 404
|
response.status = 404
|
||||||
return str(e)
|
return resp('error', str(e))
|
||||||
|
|
||||||
subject = re.sub(form_regex, r'{\1}', form['subject']).format(**subject_fields)
|
subject = re.sub(form_regex, r'{\1}', form['subject']).format(**subject_fields)
|
||||||
content = re.sub(form_regex, r'{\1}', form['content']).format(**content_fields)
|
content = re.sub(form_regex, r'{\1}', form['content']).format(**content_fields)
|
||||||
@ -148,33 +150,26 @@ def submission ():
|
|||||||
try:
|
try:
|
||||||
if not send_mail(from_address, form['mail'], subject, content):
|
if not send_mail(from_address, form['mail'], subject, content):
|
||||||
response.status = 500
|
response.status = 500
|
||||||
return 'Le mail n’a pas pu être envoyé.'
|
return resp('error', 'Le mail n’a pas pu être envoyé.')
|
||||||
except smtplib.SMTPDataError as e:
|
except smtplib.SMTPDataError as e:
|
||||||
save_mail (token, form['mail'], from_address, subject, content)
|
|
||||||
response.status = 500
|
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:
|
except smtplib.SMTPRecipientsRefused as e:
|
||||||
save_mail (token, form['mail'], from_address, subject, content)
|
|
||||||
response.status = 500
|
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:
|
except Exception as e:
|
||||||
save_mail (token, form['mail'], from_address, subject, content)
|
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
# Redirection
|
# Redirection
|
||||||
#bottle.redirect(success_redirect_default)
|
#bottle.redirect(success_redirect_default)
|
||||||
origin = request.headers.get('origin')
|
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 ############################################
|
##################################################### 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:
|
def resp (status, msg, data='{}'):
|
||||||
f.write("Unsent mail\nSubject: {}\nFrom: {}Content:\n{}".format(
|
return '{{"status": "{}", "msg": "{}", "data": {}}}'.format(status, msg, data)
|
||||||
subject,
|
|
||||||
from_address,
|
|
||||||
content
|
|
||||||
))
|
|
||||||
|
|
||||||
def get_fields (string):
|
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. """
|
""" 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
|
subject = mail_default_subject
|
||||||
else:
|
else:
|
||||||
response.status = 400
|
response.status = 400
|
||||||
return 'Le champs « sujet » est requis'
|
return resp('error', 'Le champs « sujet » est requis')
|
||||||
|
|
||||||
# Getting mail content
|
# Getting mail content
|
||||||
if 'content' in request.forms:
|
if 'content' in request.forms:
|
||||||
content = request.forms.getunicode('content')
|
content = request.forms.getunicode('content')
|
||||||
else:
|
else:
|
||||||
response.status = 400
|
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
|
# Getting from address
|
||||||
if 'mail' in request.forms:
|
if 'mail' in request.forms:
|
||||||
mail = request.forms.getunicode('mail')
|
mail = request.forms.getunicode('mail')
|
||||||
else:
|
else:
|
||||||
response.status = 400
|
response.status = 400
|
||||||
return 'Le champs « adresse » est requis'
|
return resp('error', 'Le champs « adresse » est requis')
|
||||||
|
|
||||||
user = login(request)
|
user = login(request)
|
||||||
if user['_privilege'] > 1:
|
if user['_privilege'] > 1:
|
||||||
response.status = 400
|
response.status = 400
|
||||||
return 'Privilèges insufisants'
|
return resp('error', 'Privilèges insufisants')
|
||||||
|
|
||||||
# TODO limit the insertion rate
|
# TODO limit the insertion rate
|
||||||
token = ''.join(random.sample(token_chars, token_len))
|
token = ''.join(random.sample(token_chars, token_len))
|
||||||
@ -285,12 +285,13 @@ def create_form ():
|
|||||||
'subject': subject,
|
'subject': subject,
|
||||||
'user_id': user['_id'],
|
'user_id': user['_id'],
|
||||||
'token': token,
|
'token': token,
|
||||||
|
'honeypotfield': honeypotfield,
|
||||||
})
|
})
|
||||||
except pymongo.errors.ServerSelectionTimeoutError as e:
|
except pymongo.errors.ServerSelectionTimeoutError as e:
|
||||||
response.status = 500
|
response.status = 500
|
||||||
return 'La base de donnée n’est pas accessible'
|
return resp('error', 'La base de donnée n’est pas accessible')
|
||||||
|
|
||||||
return 'Créé : ' + token
|
return resp('success', 'Créé : ' + token)
|
||||||
|
|
||||||
@app.post('/form/list')
|
@app.post('/form/list')
|
||||||
def list_forms ():
|
def list_forms ():
|
||||||
@ -302,12 +303,12 @@ def list_forms ():
|
|||||||
filt = {'user_id': user['_id']}
|
filt = {'user_id': user['_id']}
|
||||||
else:
|
else:
|
||||||
response.status = 400
|
response.status = 400
|
||||||
return 'Privilèges insufisants'
|
return resp('error', 'Privilèges insufisants')
|
||||||
data = mongodb_database['forms'].find(filt)
|
data = mongodb_database['forms'].find(filt)
|
||||||
return dumps(list(data))
|
return resp('success','', dumps(list(data)))
|
||||||
except pymongo.errors.ServerSelectionTimeoutError as e:
|
except pymongo.errors.ServerSelectionTimeoutError as e:
|
||||||
response.status = 500
|
response.status = 500
|
||||||
return 'La base de donnée n’est pas accessible'
|
return resp('error','La base de donnée n’est pas accessible')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -317,17 +318,17 @@ def delete_form(token):
|
|||||||
user = login(request)
|
user = login(request)
|
||||||
if user['_privilege'] > 1:
|
if user['_privilege'] > 1:
|
||||||
response.status = 400
|
response.status = 400
|
||||||
return 'Privilèges insufisants'
|
return resp('error', 'Privilèges insufisants')
|
||||||
|
|
||||||
# Actually delete
|
# Actually delete
|
||||||
try:
|
try:
|
||||||
form = mongodb_database['forms'].find({'token':token })[0]
|
form = mongodb_database['forms'].find({'token':token })[0]
|
||||||
except IndexError as e:
|
except IndexError as e:
|
||||||
response.status = 400
|
response.status = 400
|
||||||
return 'Le token n’est pas valide'
|
return resp('error', 'Le token n’est pas valide')
|
||||||
except pymongo.errors.ServerSelectionTimeoutError as e:
|
except pymongo.errors.ServerSelectionTimeoutError as e:
|
||||||
response.status = 500
|
response.status = 500
|
||||||
return 'La base de donnée n’est pas accessible'
|
return resp('error', 'La base de donnée n’est pas accessible')
|
||||||
|
|
||||||
if user['_privilege'] == 0 or (form['user_id'] == user['_id']):
|
if user['_privilege'] == 0 or (form['user_id'] == user['_id']):
|
||||||
try:
|
try:
|
||||||
@ -336,10 +337,10 @@ def delete_form(token):
|
|||||||
})
|
})
|
||||||
except pymongo.errors.ServerSelectionTimeoutError as e:
|
except pymongo.errors.ServerSelectionTimeoutError as e:
|
||||||
response.status = 500
|
response.status = 500
|
||||||
return 'La base de donnée n’est pas accessible'
|
return resp('error', 'La base de donnée n’est pas accessible')
|
||||||
return 'Supprimé ' + token
|
return resp('success', 'Supprimé ' + token)
|
||||||
response.status = 400
|
response.status = 400
|
||||||
return 'Privilèges insufisants'
|
return resp('error', 'Privilèges insufisants')
|
||||||
|
|
||||||
|
|
||||||
##################################################### Users ############################################
|
##################################################### Users ############################################
|
||||||
@ -349,13 +350,13 @@ def list_users ():
|
|||||||
user = login(request)
|
user = login(request)
|
||||||
if user['_privilege'] > 0:
|
if user['_privilege'] > 0:
|
||||||
response.status = 400
|
response.status = 400
|
||||||
return 'Privilèges insufisants'
|
return resp('error', 'Privilèges insufisants')
|
||||||
try:
|
try:
|
||||||
data = mongodb_database['users'].find()
|
data = mongodb_database['users'].find()
|
||||||
return dumps(list(data))
|
return resp('success', '', dumps(list(data)))
|
||||||
except pymongo.errors.ServerSelectionTimeoutError as e:
|
except pymongo.errors.ServerSelectionTimeoutError as e:
|
||||||
response.status = 500
|
response.status = 500
|
||||||
return 'La base de donnée n’est pas accessible'
|
return resp('error', 'La base de donnée n’est pas accessible')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/user/<username>', method=['OPTIONS', 'PUT'])
|
@app.route('/user/<username>', method=['OPTIONS', 'PUT'])
|
||||||
@ -363,23 +364,23 @@ def create_user (username):
|
|||||||
user = login(request)
|
user = login(request)
|
||||||
if user['_privilege'] > 0:
|
if user['_privilege'] > 0:
|
||||||
response.status = 400
|
response.status = 400
|
||||||
return 'Privilèges insufisants'
|
return resp('error', 'Privilèges insufisants')
|
||||||
try:
|
try:
|
||||||
mongodb_database['users'].find({'username': username})[0]
|
mongodb_database['users'].find({'username': username})[0]
|
||||||
return 'L’utilisateur existe déjà'
|
return resp('error', 'L’utilisateur existe déjà')
|
||||||
except IndexError as e:
|
except IndexError as e:
|
||||||
try:
|
try:
|
||||||
inserted = mongodb_database['users'].insert_one({
|
inserted = mongodb_database['users'].insert_one({
|
||||||
'username': username,
|
'username': username,
|
||||||
'token': ''.join(random.sample(token_chars, token_len))
|
'token': ''.join(random.sample(token_chars, token_len))
|
||||||
})
|
})
|
||||||
return 'Créé : ' + username
|
return resp('success', 'Créé : ' + username)
|
||||||
except pymongo.errors.ServerSelectionTimeoutError as e:
|
except pymongo.errors.ServerSelectionTimeoutError as e:
|
||||||
response.status = 500
|
response.status = 500
|
||||||
return 'La base de donnée n’est pas accessible'
|
return resp('error', 'La base de donnée n’est pas accessible')
|
||||||
except pymongo.errors.ServerSelectionTimeoutError as e:
|
except pymongo.errors.ServerSelectionTimeoutError as e:
|
||||||
response.status = 500
|
response.status = 500
|
||||||
return 'La base de donnée n’est pas accessible'
|
return resp('error','La base de donnée n’est pas accessible')
|
||||||
|
|
||||||
|
|
||||||
@app.delete('/user/<username>')
|
@app.delete('/user/<username>')
|
||||||
@ -387,19 +388,19 @@ def delete_user (username):
|
|||||||
user = login(request)
|
user = login(request)
|
||||||
if user['_privilege'] > 0:
|
if user['_privilege'] > 0:
|
||||||
response.status = 400
|
response.status = 400
|
||||||
return 'Privilèges insufisants'
|
return resp('error', 'Privilèges insufisants')
|
||||||
try:
|
try:
|
||||||
mongodb_database['users'].find({'username': username})[0]
|
mongodb_database['users'].find({'username': username})[0]
|
||||||
mongodb_database['users'].delete_one({
|
mongodb_database['users'].delete_one({
|
||||||
'username': username,
|
'username': username,
|
||||||
})
|
})
|
||||||
return 'Supprimé ' + username
|
return resp('success', 'Supprimé ' + username)
|
||||||
except IndexError as e:
|
except IndexError as e:
|
||||||
response.status = 400
|
response.status = 400
|
||||||
return 'L’utilisateur n’existe pas'
|
return resp('error', 'L’utilisateur n’existe pas')
|
||||||
except pymongo.errors.ServerSelectionTimeoutError as e:
|
except pymongo.errors.ServerSelectionTimeoutError as e:
|
||||||
response.status = 500
|
response.status = 500
|
||||||
return 'La base de donnée n’est pas accessible'
|
return resp('error', 'La base de donnée n’est pas accessible')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,11 +7,15 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="contact-mailer-message"></div>
|
<div id="contact-mailer-message"></div>
|
||||||
<form action="http://localhost:8080/submit" method="POST" id="contact-mailer-form">
|
<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>
|
<div>
|
||||||
<label for="nom">Votre nom :</label>
|
<label for="nom">Votre nom :</label>
|
||||||
<input type="text" name="nom" required="required"/>
|
<input type="text" name="nom" required="required"/>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="nom">Votre prénom :</label>
|
||||||
|
<input type="text" name="prenom"/>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="mail">Adresse mail :</label>
|
<label for="mail">Adresse mail :</label>
|
||||||
<input type="email" name="mail" required="required"/>
|
<input type="email" name="mail" required="required"/>
|
||||||
|
Loading…
Reference in New Issue
Block a user