diff --git a/main.py b/main.py index 61bb192..529e193 100755 --- a/main.py +++ b/main.py @@ -32,66 +32,48 @@ class StripPathMiddleware(object): return self.a(e, h) -app = application = bottle.Bottle() +app = application = bottle.Bottle(catchall=False) ##################################################### Configuration ############################################$ +def get_env(var, default=None): + if var in os.environ: + return os.environ[var] + elif default is not None: + return default + else: + raise MissingParameterException("Environment variable {} is missing".format(var)) + + +# Token generation +token_chars = string.ascii_lowercase+string.ascii_uppercase+string.digits +token_len = 50 + +# form template regex +form_regex = '\{\{(\w+)(\|\w+)?\}\}' # Load file from .env file. load_dotenv(os.path.dirname(__file__) + '.env') -token_chars = string.ascii_lowercase+string.ascii_uppercase+string.digits -token_len = 50 - - # Get address and port from env -listen_address = os.environ['LISTEN_ADDRESS'] if 'LISTEN_ADDRESS' in os.environ else '0.0.0.0' -listen_port = os.environ['LISTEN_PORT'] if 'LISTEN_PORT' in os.environ else 8080 - -# Get mail related informations from env -mail_default_subject = os.environ['MAIL_DEFAULT_SUBJECT'] if 'MAIL_DEFAULT_SUBJECT' in os.environ else 'Nouveau message' -mail_subject_prefix = os.environ['MAIL_SUBJECT_PREFIX'] if 'MAIL_SUBJECT_PREFIX' in os.environ else '[Contact]' - -# Redirect info -success_redirect_default = os.environ['SUCCESS_REDIRECT_DEFAULT'] if 'SUCCESS_REDIRECT_DEFAULT' in os.environ else '/success' -failure_redirect_default = os.environ['FAILURE_REDIRECT_DEFAULT'] if 'FAILURE_REDIRECT_DEFAULT' in os.environ else '/fail' +listen_address = get_env('LISTEN_ADDRESS', '0.0.0.0') +listen_port = get_env('LISTEN_PORT', 8080) # Get SMTP infos from env -if 'SMTP_SERVER_ADDRESS' in os.environ: - smtp_server_address = os.environ['SMTP_SERVER_ADDRESS'] -else: - raise MissingParameterException("Environment variable SMTP_SERVER_ADDRESS is missing") - -if 'SMTP_SERVER_PORT' in os.environ: - smtp_server_port = os.environ['SMTP_SERVER_PORT'] -else: - raise MissingParameterException("Environment variable SMTP_SERVER_PORT is missing") - -if 'SMTP_SERVER_USERNAME' in os.environ: - smtp_server_username = os.environ['SMTP_SERVER_USERNAME'] -else: - raise MissingParameterException("Environment variable SMTP_SERVER_USERNAME is missing") - -if 'SMTP_SERVER_PASSWORD' in os.environ: - smtp_server_password = os.environ['SMTP_SERVER_PASSWORD'] -else: - raise MissingParameterException("return Environment variable SMTP_SERVER_PASSWORD is missing") - -if 'SMTP_SERVER_SENDER' in os.environ: - smtp_server_sender = os.environ['SMTP_SERVER_SENDER'] -else: - raise MissingParameterException("Environment variable SMTP_SERVER_SENDER is missing") +smtp_server_address = get_env('SMTP_SERVER_ADDRESS') +smtp_server_port = get_env('SMTP_SERVER_PORT') +smtp_server_username = get_env('SMTP_SERVER_USERNAME') +smtp_server_password = get_env('SMTP_SERVER_PASSWORD') +smtp_server_sender = get_env('SMTP_SERVER_SENDER') # Get mongodb connection -if 'MONGODB_HOST' in os.environ: - mongodb_host = os.environ['MONGODB_HOST'] -else: - raise MissingParameterException("Environment variable MONGODB_HOST is missing") - -mongodb_port = os.environ['MONGODB_PORT'] if 'MONGODB_PORT' in os.environ else '27017' -mongodb_dbname = os.environ['MONGODB_DBNAME'] if 'MONGODB_DBNAME' in os.environ else 'contact_mailer' +mongodb_host = get_env('MONGODB_HOST') +mongodb_port = get_env('MONGODB_PORT', '27017') +mongodb_dbname = get_env('MONGODB_DBNAME', 'contact_mailer') # Security +admin_password = get_env('ADMIN_PASSWORD') + if 'SMTP_SSL' in os.environ and os.environ['SMTP_SSL'] == 'true': security = 'ssl' elif 'SMTP_STARTTLS' in os.environ and os.onviron['SMTP_STARTTLS'] == 'true': @@ -99,25 +81,12 @@ elif 'SMTP_STARTTLS' in os.environ and os.onviron['SMTP_STARTTLS'] == 'true': else: raise MissingParameterException('No security env var (SMTP_SSL or SMTP_STARTTLS) have been defined. (Expected true or false)') -if 'ADMIN_PASSWORD' in os.environ: - admin_password = os.environ['ADMIN_PASSWORD'] -else: - raise MissingParameterException("Environment variable ADMIN_PASSWORD is missing") - - # mongodb initialization -mongodb_client = pymongo.MongoClient("mongodb://{}:{}/".format(mongodb_host, mongodb_port)) +mongodb_client = pymongo.MongoClient("mongodb://{}:{}/".format(mongodb_host, mongodb_port), connect=False, serverSelectionTimeoutMS=10000, connectTimeoutMS=10000) mongodb_database = mongodb_client[mongodb_dbname] -# form template regex -form_regex = '\{\{(\w+)(\|\w+)?\}\}' - -@app.post('/fail') -def fail (): - a = 2/0 - print('lol, failed', file=sys.stderr) - return 'failed' +##################################################### main route: mail submission ############################################$ @app.post('/submit') def submission (): @@ -140,6 +109,9 @@ def submission (): except IndexError as e: response.status = 400 return 'Le formulaire est introuvable' + except pymongo.errors.ServerSelectionTimeoutError as e: + response.status = 500 + return 'La base de donnée n’est pas accessible' try: subject_fields = fill_fields(request, get_fields(form['subject'])) @@ -151,9 +123,13 @@ def submission (): subject = re.sub(form_regex, r'{\1}', form['subject']).format(**subject_fields) content = re.sub(form_regex, r'{\1}', form['content']).format(**content_fields) - if not send_mail(from_address, form['mail'], subject, content): + try: + if not send_mail(from_address, form['mail'], subject, content): + response.status = 500 + return 'Le mail n’a pas pu être envoyé.' + except SMTPDataError as e: response.status = 500 - return 'Le mail n’a pas pu être envoyé.' + return 'Le mail a été refusé.' # Redirection #redirect(success_redirect_default) @@ -220,6 +196,9 @@ def login(request): return user except IndexError as e: pass + except pymongo.errors.ServerSelectionTimeoutError as e: + response.status = 500 + return 'La base de donnée n’est pas accessible' return {'_privilege': 1000} # anonymous @@ -258,27 +237,37 @@ def create_form (): # TODO limit the insertion rate token = ''.join(random.sample(token_chars, token_len)) - inserted = mongodb_database['forms'].insert_one({ - 'mail': mail, - 'content': content, - 'subject': subject, - 'user_id': user['_id'], - 'token': token, - }) + try: + inserted = mongodb_database['forms'].insert_one({ + 'mail': mail, + 'content': content, + 'subject': subject, + 'user_id': user['_id'], + 'token': token, + }) + except pymongo.errors.ServerSelectionTimeoutError as e: + response.status = 500 + return 'La base de donnée n’est pas accessible' return 'Créé : ' + token @app.post('/form/list') def list_forms (): - user = login(request) - if user['_privilege'] == 0: - filt = {} - elif user['_privilege'] == 1: - filt = {'user_id': user['_id']} - else: - response.status = 400 - return 'Privilèges insufisants' - return bottle.template("list.tpl", data=mongodb_database['forms'].find(filt)) + try: + user = login(request) + if user['_privilege'] == 0: + filt = {} + elif user['_privilege'] == 1: + filt = {'user_id': user['_id']} + else: + response.status = 400 + return 'Privilèges insufisants' + data = mongodb_database['forms'].find(filt) + return bottle.template("list.tpl", data=data) + except pymongo.errors.ServerSelectionTimeoutError as e: + response.status = 500 + return 'La base de donnée n’est pas accessible' + @app.delete('/form/') @@ -295,11 +284,18 @@ def delete_form(token): except IndexError as e: response.status = 400 return 'Le token n’est pas valide' + except pymongo.errors.ServerSelectionTimeoutError as e: + response.status = 500 + return 'La base de donnée n’est pas accessible' if user['_privilege'] == 0 or (form['user_id'] == user['_id']): - mongodb_database['forms'].delete_one({ - 'token': token, - }) + try: + mongodb_database['forms'].delete_one({ + 'token': token, + }) + except pymongo.errors.ServerSelectionTimeoutError as e: + response.status = 500 + return 'La base de donnée n’est pas accessible' return 'Supprimé ' + token response.status = 400 return 'Privilèges insufisants' @@ -313,7 +309,13 @@ def list_users (): if user['_privilege'] > 0: response.status = 400 return 'Privilèges insufisants' - return bottle.template("list.tpl", data=mongodb_database['users'].find()) + try: + data = mongodb_database['users'].find() + return bottle.template("list.tpl", data=data) + except pymongo.errors.ServerSelectionTimeoutError as e: + response.status = 500 + return 'La base de donnée n’est pas accessible' + @app.put('/user/') def create_user (username): @@ -325,11 +327,18 @@ def create_user (username): mongodb_database['users'].find({'username': username})[0] return 'L’utilisateur existe déjà' except IndexError as e: - inserted = mongodb_database['users'].insert_one({ - 'username': username, - 'token': ''.join(random.sample(token_chars, token_len)) - }) - return 'Créé : ' + username + try: + inserted = mongodb_database['users'].insert_one({ + 'username': username, + 'token': ''.join(random.sample(token_chars, token_len)) + }) + return 'Créé : ' + username + except pymongo.errors.ServerSelectionTimeoutError as e: + response.status = 500 + return 'La base de donnée n’est pas accessible' + except pymongo.errors.ServerSelectionTimeoutError as e: + response.status = 500 + return 'La base de donnée n’est pas accessible' @app.delete('/user/') @@ -340,15 +349,20 @@ def delete_user (username): return 'Privilèges insufisants' try: mongodb_database['users'].find({'username': username})[0] + mongodb_database['users'].delete_one({ + 'username': username, + }) + return 'Supprimé ' + username except IndexError as e: response.status = 400 return 'L’utilisateur n’existe pas' - mongodb_database['users'].delete_one({ - 'username': username, - }) - return 'Supprimé ' + username + except pymongo.errors.ServerSelectionTimeoutError as e: + response.status = 500 + return 'La base de donnée n’est pas accessible' + +##################################################### app startup ############################################$ if __name__ == '__main__': bottle.run(app=StripPathMiddleware(app), host=listen_address, port=listen_port, debug=True) else: