diff --git a/main.py b/main.py index d3f9201e961f0722423ed53fa813561cd1c1ab4a..fda7df51825ac0c1243b5a834595b6dc022b25d0 100644 --- a/main.py +++ b/main.py @@ -3,11 +3,11 @@ import sys from datetime import datetime, timezone import os.path import re -from typing import List, Dict, Optional, Union +from typing import List, Dict, Optional, Union, Tuple from flask import Flask, render_template, request, make_response, redirect, flash, send_file from flask_wtf import FlaskForm -from wtforms import StringField +from wtforms import StringField, BooleanField from wtforms.validators import DataRequired, Email import requests @@ -246,6 +246,13 @@ class AddUserForm(FlaskForm): lastname = StringField("Last name", validators=[]) email = StringField("Email", validators=[DataRequired(), Email()]) # DN is constructed automatically from CN + send_email = BooleanField("Automatically send email with an URL allowing the user to download his/her certificate.", + default="checked", + description="A certificate, signed by an internal SOCtools CA, is generated for each user. It allows " + "to access all the SOCtools components. The user can download the certificate " + "(and matching private key) on a unique URL which will be sent to given email address. " + "Check to send it immediately after the user account is created. Or you can (re)send it" + "manually later.") @app.route("/") @@ -310,9 +317,8 @@ def add_user(): try: certificates.generate_certificate(user.cn) flash(f'Certificate for user "{user.username}" was successfully created.', "success") - #token = certificates.generate_access_token(user.cn) - #print(f"Certificate access token for '{user.cn}': {token}") - # TODO: send token to email automatically? + + except certificates.CertError as e: flash(str(e), "error") return redirect("/") # don't continue creating user accounts in services @@ -323,6 +329,7 @@ def add_user(): flash(f'User "{user.username}" successfully created in Keycloak.', "success") except Exception as e: flash(f'Error when creating user in Keycloak: {e}', "error") + return redirect("/") # don't continue creating user accounts in other services # NiFi try: @@ -342,6 +349,14 @@ def add_user(): except Exception as e: flash(f'Error when creating user in MISP: {e}', "error") + # Send email to the user + if form_user.send_email.data: + ok, err = _send_token(user.username, user.email) + if ok: + flash(f"Email successfully sent to '{user.email}'", "success") + else: + flash(f"ERROR: {err}", "error") + return redirect("/") # Success - go back to main page return render_template("add_edit_user.html", form_user=form_user, user=None) @@ -484,6 +499,7 @@ def send_token(username: str): """ Send an email with certificate access token to the user. + Endpoint called by a "send email" button from web gui. """ # Check that the user exists user_spec = kc_get_user_by_name(username) @@ -491,12 +507,25 @@ def send_token(username: str): flash(f"ERROR: No such user ('{username}')", "error") return redirect("/") + ok, err = _send_token(username, user_spec.email) + if ok: + flash(f"Email successfully sent to '{user_spec.email}'", "success") + else: + flash(f"ERROR: {err}", "error") + return redirect("/") + + +def _send_token(username: str, email: str) -> Tuple[bool, Optional[str]]: + """ + Generate token and send the email (internal function) + + :return tuple: success or not (bool), error message in case of failure + """ # Generate token try: token = certificates.generate_access_token(username) except Exception as e: - flash(f"ERROR: {e}", "error") - return redirect("/") + return False, str(e) access_url = f"{config.USER_MGMT_BASE_URL}/export_certificate?token={token}" print(f"Certificate access URL for '{username}': {access_url}") @@ -504,15 +533,14 @@ def send_token(username: str): # Send the token via email # TODO - flash(f"Email successfully sent to '{user_spec.email}'", "success") - return redirect("/") + return True, None # TODO: # (re)send cert-access token for existing user - DONE (on click in table) # automatically create certificate when creating new user (optionally automatically send email with token) - DONE # revoke and delete certificate when user is deleted -# make CN=username (co cert filename also matches the username (it's stored by CN)) +# make CN=username (so cert filename also matches the username (it's stored by CN)) # @app.route("/test_cert/<func>") diff --git a/templates/add_edit_user.html b/templates/add_edit_user.html index 11f273beb75c49a299aa67d48f3a72985e37ba17..8cf5158de49b8007dfc4b8c098b50ea158c609a1 100644 --- a/templates/add_edit_user.html +++ b/templates/add_edit_user.html @@ -24,7 +24,12 @@ {{ form_user.lastname.label }} {{ form_user.lastname(size=20) }}<br> {{ form_user.cn.label }} {{ form_user.cn(size=20) }}<br> {{ form_user.email.label }} {{ form_user.email(size=20) }}<br> - <input type="submit" value="{{"Update user" if user else "Add user"}}"> + {% if user %} + <input type="submit" value="Update user"> + {% else %} + {{ form_user.send_email(title=form_user.send_email.description) }}{{ form_user.send_email.label(title=form_user.send_email.description) }}<br> + <input type="submit" value="Add user"> + {% endif %} </form> {% endblock %}