Compare commits

...

3 Commits

Author SHA1 Message Date
e2465e2874 fix field control 2020-10-08 20:17:48 +02:00
8f88cd6d2c tested timer field 2020-09-17 16:19:39 +02:00
e66ac2e8bd First work on timer field against bots 2020-09-17 16:08:46 +02:00
3 changed files with 43 additions and 16 deletions

View File

@ -74,6 +74,14 @@ function jeanCloudContactFormIntercept (formId, notifier) {
loadingText.classList.add("contact-mailer-sending"); loadingText.classList.add("contact-mailer-sending");
loadingText.textContent = 'Envoi en cours…' loadingText.textContent = 'Envoi en cours…'
submitButton.after(loadingText) submitButton.after(loadingText)
/* Add the filling timer in seconds */
const timerField = document.createElement('input')
timerField.value = Math.round((Date.now() - contactMailerPageLoadedTime) / 1000)
timerField.name = 'timerfield'
timerField.hidden = 'hidden'
formElem.appendChild(timerField)
/* XHR */ /* XHR */
fetch(formElem.action, { fetch(formElem.action, {
method: formElem.method, method: formElem.method,
@ -98,6 +106,9 @@ function jeanCloudContactFormIntercept (formId, notifier) {
loadingText.parentNode.removeChild(loadingText) loadingText.parentNode.removeChild(loadingText)
notifier.error('Impossible denvoyer le formulaire. Vérifiez votre connexion internet ou réessayez plus tard.') notifier.error('Impossible denvoyer le formulaire. Vérifiez votre connexion internet ou réessayez plus tard.')
}) })
/* Remove timer field after xhr. So we can try again. */
formElem.removeChild(timerField)
} }
} }
@ -115,3 +126,5 @@ function jeanCloudContactFormIntercept (formId, notifier) {
// cat style.css | openssl dgst -sha384 -binary | openssl base64 -A // cat style.css | openssl dgst -sha384 -binary | openssl base64 -A
document.head.appendChild(link); document.head.appendChild(link);
})() })()
var contactMailerPageLoadedTime = Date.now()

37
main.py
View File

@ -46,7 +46,6 @@ class EnableCors(object):
return _enable_cors return _enable_cors
app = application = bottle.Bottle(catchall=False) app = application = bottle.Bottle(catchall=False)
#app.install(EnableCors())
##################################################### Configuration ############################################ ##################################################### Configuration ############################################
@ -134,16 +133,24 @@ def submission ():
response.status = 500 response.status = 500
return resp('error', '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']))
# Did the bot filled the honeypot field? # Did the bot filled the honeypot field?
if 'honeypotfield' in form and form['honeypotfield'] in request.forms and request.forms.get(form['honeypotfield']) != '': if 'honeypotfield' in form and form['honeypotfield'] in request.forms and request.forms.get(form['honeypotfield']) != '':
response.status = 400
print('honeypotfield')
return resp('error', 'We identified you as a bot. If this is an error, try to contact us via another way.')
# Is the js timer enabled?
if 'timerdelay' in form:
# Did it work?
if 'timerfield' not in request.forms or int(request.forms.get('timerfield')) < int(form['timerdelay']):
print('timer : {}/{}'.format(request.forms.get('timerfield'), form['timerdelay']))
response.status = 400 response.status = 400
return resp('error', 'We identified you as a bot. If this is an error, try to contact us via another way.') return resp('error', 'We identified you as a bot. If this is an error, try to contact us via another way.')
try:
subject_fields = fill_fields(request, get_fields(form['subject']))
content_fields = fill_fields(request, get_fields(form['content']))
except MissingParameterException as e: except MissingParameterException as e:
response.status = 404 response.status = 400
return resp('error', 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)
@ -184,9 +191,13 @@ def fill_fields(request, fields):
"""Look for fields in request and fill fields dict with values or let default ones. If the value is required, throw exception.""" """Look for fields in request and fill fields dict with values or let default ones. If the value is required, throw exception."""
for field in fields: for field in fields:
if field in request.forms: if field in request.forms:
if request.forms.get(field).strip() == '' and fields[field] is None: # If empty and mandatory
raise MissingParameterException("Le champs {} doit être rempli".format(field))
fields[field] = request.forms.getunicode(field) fields[field] = request.forms.getunicode(field)
if fields[field] is None: # if unicode failed if fields[field] is None: # if unicode failed
fields[field] = request.forms.get(field) fields[field] = request.forms.get(field)
if fields[field] is None: # if get failed too
raise Exception("Error, field '{}' not gettable".format(field))
elif fields[field] is None: elif fields[field] is None:
raise MissingParameterException("Le champs {} est obligatoire".format(field)) raise MissingParameterException("Le champs {} est obligatoire".format(field))
return fields return fields
@ -264,10 +275,6 @@ def create_form ():
response.status = 400 response.status = 400
return resp('error', '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:
@ -284,14 +291,19 @@ def create_form ():
# 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))
try: try:
inserted = mongodb_database['forms'].insert_one({ newEntry = {
'mail': mail, 'mail': mail,
'content': content, 'content': content,
'subject': subject, 'subject': subject,
'user_id': user['_id'], 'user_id': user['_id'],
'token': token, 'token': token,
'honeypotfield': honeypotfield, }
}) if 'honeypotfield' in request.forms:
newEntry['honeypotfield'] = request.forms.getunicode('honeypotfield')
if 'timerdelay' in request.forms:
newEntry['timerdelay'] = request.forms.getunicode('timerdelay')
inserted = mongodb_database['forms'].insert_one(newEntry)
except pymongo.errors.ServerSelectionTimeoutError as e: except pymongo.errors.ServerSelectionTimeoutError as e:
response.status = 500 response.status = 500
return resp('error', 'La base de donnée nest pas accessible') return resp('error', 'La base de donnée nest pas accessible')
@ -411,6 +423,7 @@ def delete_user (username):
##################################################### app startup ############################################ ##################################################### app startup ############################################
if __name__ == '__main__': if __name__ == '__main__':
app.install(EnableCors())
bottle.run(app=StripPathMiddleware(app), host=listen_address, port=listen_port, debug=True) bottle.run(app=StripPathMiddleware(app), host=listen_address, port=listen_port, debug=True)
else: else:
prod_app = StripPathMiddleware(app) prod_app = StripPathMiddleware(app)

View File

@ -7,7 +7,8 @@
<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="sYMXDz5UKuRF38LbQl20ikrmp7nhHcxTCgGZodqAaBtSvPOV4f" /> <noscript>Les protections anti-spam, nécéssitent lutilisation de javascript. Rien dintrusif normalement.</noscript>
<input type="hidden" name="token" value="PK8gQHDx9VoJ7yuEhbj5iCZkcUOAqTYlRSN14XFtdfr3LBs0zn" />
<div> <div>
<label for="nom">Votre nom&nbsp;:</label> <label for="nom">Votre nom&nbsp;:</label>
<input type="text" name="nom" required="required"/> <input type="text" name="nom" required="required"/>