Skip to content
Snippets Groups Projects
Commit 9e5c2e2d authored by Václav Bartoš's avatar Václav Bartoš
Browse files

improved error handling

- gui now works even without SMTP configured (cert-access URL is printed to the page instead of being sent to the user)
- several fixes in other error handling
parent 134bc228
Branches
Tags
No related merge requests found
......@@ -239,6 +239,10 @@ def send_token(user: 'UserAccount') -> Tuple[bool, Optional[str]]:
# Print URL to console (for debugging or when email sending doesn't work)
print(f"Certificate access URL for '{user.username}': {access_url}")
if not config['smtp']:
# SMTP not configured, show URL to the admin (as part of error message) instead
return False, f"Can't send email to the user. URL to download the certificate: {access_url}"
# Send the token via email
name = f"{user.firstname} {user.lastname}".strip() or user.username
try:
......
#!/usr/bin/env python3
import os.path
import sys
from datetime import datetime, timezone
from typing import List, Dict, Optional, Union, Tuple
......@@ -17,44 +18,54 @@ from config import config # module serving as a global namespace for configurati
# Load and check configuration - paths, URLs, api keys, etc.
invalid_configuration_error = None
invalid_smtp = None
try:
config.update(yaml.safe_load(open("config.yml", "r")))
errors = []
smtp_errors = []
# Check presence of all mandatory configuration parameters
for attr in ('soctoolsproxy', 'ca_cert_file', 'easyrsa_bin', 'easyrsa_ca_dir', 'token_file',
'mgmt_user_name', 'mgmt_user_cert_path', 'mgmt_user_key_path', 'user_mgmt_base_url',
'keycloak_base_url', 'keycloak_users_url', 'keycloak_admin_password',
'misp_api_key', 'thehive_api_key', 'thehive_org_name', 'cortex_api_key', 'cortex_org_name',):
if not config[attr]:
if attr not in config or not config[attr]:
errors.append(f'Missing mandatory parameter "{attr}".')
config[attr] = None # to avoid attr-access errors in some imported modules
# Check soctoolsproxy
if not re.match('[a-zA-Z0-9.:-]+', config['soctoolsproxy']):
if not re.fullmatch('[a-zA-Z0-9.:-]+', config['soctoolsproxy'] or 'a'):
errors.append("'soctoolsproxy' is not a valid hostname or IP address.")
# Check smtp params, set defaults
if not isinstance(config.get('smtp'), dict):
errors.append('Missing mandatory parameter "smtp" or it is not dict.')
if not re.match('[a-zA-Z0-9.:-]+', config['smtp'].get('host')):
errors.append('Missing mandatory parameter "smtp.host" or is not a valid hostname or IP address.')
if not config['smtp'].get('sender'):
errors.append('Missing mandatory parameter "smtp.sender".')
if not config['smtp'].get('port'):
config['smtp']['port'] = 465
smtp_errors.append('Parameter "smtp" is missing or it is not a dict.')
else:
if not re.match('[a-zA-Z0-9.:-]+', config['smtp'].get('host')):
smtp_errors.append('Parameter "smtp.host" is missing or it is not a valid hostname or IP address.')
if not config['smtp'].get('sender'):
smtp_errors.append('Parameter "smtp.sender" is missing.')
if not config['smtp'].get('port'):
config['smtp']['port'] = 465
if errors:
invalid_configuration_error = "Configuration error(s):\n" + "\n".join(errors)
invalid_configuration_error = "Configuration error(s):\n" + "\n".join(errors + smtp_errors)
if smtp_errors:
invalid_smtp = f"WARNING: SMTP not properly configured, it is not possible to send emails ({' '.join(smtp_errors)})"
config['smtp'] = None
except Exception as e:
invalid_configuration_error = f"Can't load configuration: {e}"
print(f"Config loaded:\nsoctoolsproxy={config['soctoolsproxy']}\nkeycloak_base_url={config['keycloak_base_url']}\n"
f"keycloak_admin_password={config['keycloak_admin_password'][:3]}...{config['keycloak_admin_password'][-4:]}\n"
f"misp_api_key={config['misp_api_key'][:3]}...{config['misp_api_key'][-4:]}\n"
f"thehive_api_key={config['thehive_api_key'][:3]}...{config['thehive_api_key'][-4:]}\n"
f"cortex_api_key={config['cortex_api_key'][:3]}...{config['cortex_api_key'][-4:]}\n"
f"thehive_org_name={config['thehive_org_name']}\n")
if invalid_configuration_error:
print("ERROR: " + invalid_configuration_error)
else:
print(f"Config loaded:\nsoctoolsproxy={config['soctoolsproxy']}\nkeycloak_base_url={config['keycloak_base_url']}\n"
f"keycloak_admin_password={config['keycloak_admin_password'][:3]}...{config['keycloak_admin_password'][-4:]}\n"
f"misp_api_key={config['misp_api_key'][:3]}...{config['misp_api_key'][-4:]}\n"
f"thehive_api_key={config['thehive_api_key'][:3]}...{config['thehive_api_key'][-4:]}\n"
f"cortex_api_key={config['cortex_api_key'][:3]}...{config['cortex_api_key'][-4:]}\n"
f"thehive_org_name={config['thehive_org_name']}\n")
import certificates
......@@ -69,10 +80,12 @@ app.secret_key = "ASDF1234 - CHANGE ME!" # TODO: set dynamically to something ra
# If there is config error, report it instead of trying to render a page
@app.before_request
def config_check():
# in case of fatal config error, return error 500 with an error message
if invalid_configuration_error is not None:
return make_response(f"500 Internal Server Error\n{'='*25}\n\n{invalid_configuration_error}\n\n"
"Fix the configuration file and restart the user-mgmt-ui service "
"('systemctl restart user-mgmt-ui')", 500, {'Content-Type': 'text/plain'})
f"Fix the configuration file ('{os.path.abspath('config.yml')}') and restart"
" the user-mgmt-ui service ('systemctl restart user-mgmt-ui')",
500, {'Content-Type': 'text/plain'})
def redirect_to_main_page():
......@@ -289,6 +302,11 @@ class AddUserForm(FlaskForm):
@app.route("/")
def main():
# In case of invalid SMTP configuration, show warning message
if invalid_smtp is not None and request.endpoint != "export_certificate":
flash(invalid_smtp + f" To fix it, edit the configuration file ('{os.path.abspath('config.yml')}') and restart"
" the user-mgmt-ui service ('systemctl restart user-mgmt-ui').", "warning")
# Load existing users from Keycloak
try:
users = kc_get_users()
......@@ -642,15 +660,13 @@ def send_token(username: str):
# TODO:
# - revoke and delete certificate when user is deleted
# - send tokens via email
# - authentication/authorization to this GUI
@app.route("/test_cert/<func>")
def test_cert_endpoint(func):
# run any function from "certificates" module
result = str(getattr(certificates, func)(**request.args))
return make_response(result)
# @app.route("/test_cert/<func>")
# def test_cert_endpoint(func):
# # run any function from "certificates" module
# result = str(getattr(certificates, func)(**request.args))
# return make_response(result)
# When the script is run directly, run the application on a local development server.
# Optionally pass two parameters, 'host' (IP to listen on) and 'port',
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment