From 9757830bc402a0b6c907b2159c764c05ce6ad84a Mon Sep 17 00:00:00 2001
From: Josako
Date: Wed, 21 Aug 2024 14:59:56 +0200
Subject: [PATCH] Correct functions for creating new users, confirming email,
resetting password and forgot password.
---
common/extensions.py | 3 +-
common/models/user.py | 2 +-
common/utils/security_utils.py | 56 +++++++++++++-
config/config.py | 24 +++---
docker/compose_dev.yaml | 4 +-
docker/compose_stackhero.yaml | 4 +-
eveai_app/__init__.py | 1 +
.../templates/email/forgot_password.html | 13 ++++
.../security/email/reset_instructions.html | 13 ++++
.../templates/security/forgot_password.html | 12 ++-
eveai_app/templates/security/login_user.html | 5 +-
.../templates/security/reset_password.html | 6 +-
eveai_app/templates/user/view_users.html | 1 +
eveai_app/views/security_forms.py | 6 +-
eveai_app/views/security_views.py | 2 +
eveai_app/views/user_views.py | 33 ++++-----
logs/eveai_app.log | 3 +
...ed_fs_uniquifier_required_password_not_.py | 74 +++++++++++++++++++
..._uniquifier_not_required_when_creating_.py | 68 +++++++++++++++++
nginx/nginx.conf | 4 +
20 files changed, 291 insertions(+), 43 deletions(-)
create mode 100644 eveai_app/templates/email/forgot_password.html
create mode 100644 eveai_app/templates/security/email/reset_instructions.html
create mode 100644 migrations/public/versions/229774547fed_fs_uniquifier_required_password_not_.py
create mode 100644 migrations/public/versions/a39d2e378ccf_uniquifier_not_required_when_creating_.py
diff --git a/common/extensions.py b/common/extensions.py
index f36134b..deeafdd 100644
--- a/common/extensions.py
+++ b/common/extensions.py
@@ -10,10 +10,11 @@ from flask_jwt_extended import JWTManager
from flask_session import Session
from flask_wtf import CSRFProtect
-# from .utils.key_encryption import JosKMSClient
+from .utils.nginx_utils import prefixed_url_for
from .utils.simple_encryption import SimpleEncryption
from .utils.minio_utils import MinioClient
+
# Create extensions
db = SQLAlchemy()
migrate = Migrate()
diff --git a/common/models/user.py b/common/models/user.py
index 69e17f2..c700613 100644
--- a/common/models/user.py
+++ b/common/models/user.py
@@ -126,7 +126,7 @@ class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
user_name = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(255), unique=True, nullable=False)
- password = db.Column(db.String(255), nullable=False)
+ password = db.Column(db.String(255), nullable=True)
first_name = db.Column(db.String(80), nullable=False)
last_name = db.Column(db.String(80), nullable=False)
active = db.Column(db.Boolean)
diff --git a/common/utils/security_utils.py b/common/utils/security_utils.py
index 4a3a897..f86bad1 100644
--- a/common/utils/security_utils.py
+++ b/common/utils/security_utils.py
@@ -1,6 +1,7 @@
from flask import current_app, render_template
from flask_mailman import EmailMessage
from itsdangerous import URLSafeTimedSerializer
+import socket
from common.utils.nginx_utils import prefixed_url_for
@@ -35,15 +36,66 @@ def generate_confirmation_token(email):
def send_confirmation_email(user):
current_app.logger.debug(f'Sending confirmation email to {user.email}')
+
+ if not test_smtp_connection():
+ raise Exception("Failed to connect to SMTP server")
+
token = generate_confirmation_token(user.email)
confirm_url = prefixed_url_for('security_bp.confirm_email', token=token, _external=True)
current_app.logger.debug(f'Confirmation URL: {confirm_url}')
+
html = render_template('email/activate.html', confirm_url=confirm_url)
- send_email(user.email, "Confirm your email", html)
+ subject = "Please confirm your email"
+
+ try:
+ send_email(user.email, "Confirm your email", html)
+ current_app.logger.info(f'Confirmation email sent to {user.email}')
+ except Exception as e:
+ current_app.logger.error(f'Failed to send confirmation email to {user.email}. Error: {str(e)}')
+ raise
def send_reset_email(user):
+ current_app.logger.debug(f'Sending reset email to {user.email}')
token = generate_reset_token(user.email)
reset_url = prefixed_url_for('security_bp.reset_password', token=token, _external=True)
+ current_app.logger.debug(f'Reset URL: {reset_url}')
+
html = render_template('email/reset_password.html', reset_url=reset_url)
- send_email(user.email, "Reset Your Password", html)
+ subject = "Reset Your Password"
+
+ try:
+ send_email(user.email, "Reset Your Password", html)
+ current_app.logger.info(f'Reset email sent to {user.email}')
+ except Exception as e:
+ current_app.logger.error(f'Failed to send reset email to {user.email}. Error: {str(e)}')
+ raise
+
+
+def test_smtp_connection():
+ try:
+ current_app.logger.info(f"Attempting to resolve google.com...")
+ google_ip = socket.gethostbyname('google.com')
+ current_app.logger.info(f"Successfully resolved google.com to {google_ip}")
+ except Exception as e:
+ current_app.logger.error(f"Failed to resolve google.com: {str(e)}")
+
+ try:
+ smtp_server = current_app.config['MAIL_SERVER']
+ current_app.logger.info(f"Attempting to resolve {smtp_server}...")
+ smtp_ip = socket.gethostbyname(smtp_server)
+ current_app.logger.info(f"Successfully resolved {smtp_server} to {smtp_ip}")
+ except Exception as e:
+ current_app.logger.error(f"Failed to resolve {smtp_server}: {str(e)}")
+
+ try:
+ smtp_server = current_app.config['MAIL_SERVER']
+ smtp_port = current_app.config['MAIL_PORT']
+ sock = socket.create_connection((smtp_server, smtp_port), timeout=10)
+ sock.close()
+ current_app.logger.info(f"Successfully connected to SMTP server {smtp_server}:{smtp_port}")
+ return True
+ except Exception as e:
+ current_app.logger.error(f"Failed to connect to SMTP server: {str(e)}")
+ return False
+
diff --git a/config/config.py b/config/config.py
index d37db88..50a90a3 100644
--- a/config/config.py
+++ b/config/config.py
@@ -3,6 +3,7 @@ from datetime import timedelta
import redis
from common.utils.prompt_loader import load_prompt_templates
+from eveai_app.views.security_forms import ResetPasswordForm
basedir = path.abspath(path.dirname(__file__))
@@ -41,6 +42,11 @@ class Config(object):
SECURITY_POST_LOGIN_VIEW = '/user/tenant_overview'
SECURITY_RECOVERABLE = True
SECURITY_EMAIL_SENDER = "eveai_super@flow-it.net"
+ SECURITY_EMAIL_SUBJECT_PASSWORD_RESET = 'Reset Your Password'
+ SECURITY_EMAIL_SUBJECT_PASSWORD_NOTICE = 'Your Password Has Been Reset'
+ SECURITY_EMAIL_PLAINTEXT = False
+ SECURITY_EMAIL_HTML = True
+ SECURITY_RESET_PASSWORD_FORM = ResetPasswordForm
# Ensure Flask-Security-Too is handling CSRF tokens when behind a proxy
SECURITY_CSRF_PROTECT_MECHANISMS = ['session']
@@ -123,6 +129,15 @@ class Config(object):
"LLM": {"name": "LLM", "description": "Algorithm using information integrated in the used LLM"}
}
+ # flask-mailman settings
+ MAIL_SERVER = environ.get('MAIL_SERVER')
+ MAIL_PORT = int(environ.get('MAIL_PORT', 465))
+ MAIL_USE_TLS = False
+ MAIL_USE_SSL = True
+ MAIL_USERNAME = environ.get('MAIL_USERNAME')
+ MAIL_PASSWORD = environ.get('MAIL_PASSWORD')
+ MAIL_DEFAULT_SENDER = ('eveAI Admin', MAIL_USERNAME)
+
class DevConfig(Config):
DEVELOPMENT = True
@@ -138,15 +153,6 @@ class DevConfig(Config):
SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASS}@{DB_HOST}:5432/{DB_NAME}'
SQLALCHEMY_BINDS = {'public': SQLALCHEMY_DATABASE_URI}
- # flask-mailman settings
- MAIL_SERVER = 'mail.flow-it.net'
- MAIL_PORT = 587
- MAIL_USE_TLS = True
- MAIL_USE_SSL = False
- MAIL_DEFAULT_SENDER = ('eveAI Admin', 'eveai_admin@flow-it.net')
- MAIL_USERNAME = environ.get('MAIL_USERNAME')
- MAIL_PASSWORD = environ.get('MAIL_PASSWORD')
-
# Define the nginx prefix used for the specific apps
EVEAI_APP_LOCATION_PREFIX = '/admin'
EVEAI_CHAT_LOCATION_PREFIX = '/chat'
diff --git a/docker/compose_dev.yaml b/docker/compose_dev.yaml
index 3e2b28f..e2ef793 100644
--- a/docker/compose_dev.yaml
+++ b/docker/compose_dev.yaml
@@ -19,7 +19,9 @@ x-common-variables: &common-variables
SECRET_KEY: '97867c1491bea5ee6a8e8436eb11bf2ba6a69ff53ab1b17ecba450d0f2e572e1'
SECURITY_PASSWORD_SALT: '228614859439123264035565568761433607235'
MAIL_USERNAME: eveai_super@flow-it.net
- MAIL_PASSWORD: '$6xsWGbNtx$CFMQZqc*'
+ MAIL_PASSWORD: '$$6xsWGbNtx$$CFMQZqc*'
+ MAIL_SERVER: mail.flow-it.net
+ MAIL_PORT: 465
OPENAI_API_KEY: 'sk-proj-8R0jWzwjL7PeoPyMhJTZT3BlbkFJLb6HfRB2Hr9cEVFWEhU7'
GROQ_API_KEY: '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'
ANTHROPIC_API_KEY: 'sk-ant-api03-c2TmkzbReeGhXBO5JxNH6BJNylRDonc9GmZd0eRbrvyekec2'
diff --git a/docker/compose_stackhero.yaml b/docker/compose_stackhero.yaml
index 0f4ac01..009407b 100644
--- a/docker/compose_stackhero.yaml
+++ b/docker/compose_stackhero.yaml
@@ -11,7 +11,7 @@
x-common-variables: &common-variables
DB_HOST: bswnz4.stackhero-network.com
DB_USER: luke_skywalker
- DB_PASS: 2MK&1rHmWEydE2rFuJLq*ls%tdkPAk2
+ DB_PASS: '2MK&1rHmWEydE2rFuJLq*ls%tdkPAk2'
DB_NAME: eveai
DB_PORT: '5945'
FLASK_ENV: production
@@ -20,6 +20,8 @@ x-common-variables: &common-variables
SECURITY_PASSWORD_SALT: '166448071751628781809462050022558634074'
MAIL_USERNAME: 'evie_admin@askeveai.com'
MAIL_PASSWORD: 's5D%R#y^v!s&6Z^i0k&'
+ MAIL_SERVER: mail.askeveai.com
+ MAIL_PORT: 465
REDIS_USER: eveai
REDIS_PASS: 'jHliZwGD36sONgbm0fc6SOpzLbknqq4RNF8K'
REDIS_URL: 8bciqc.stackhero-network.com
diff --git a/eveai_app/__init__.py b/eveai_app/__init__.py
index 630f8d9..e43ba8f 100644
--- a/eveai_app/__init__.py
+++ b/eveai_app/__init__.py
@@ -10,6 +10,7 @@ from common.extensions import (db, migrate, bootstrap, security, mail, login_man
minio_client, simple_encryption)
from common.models.user import User, Role, Tenant, TenantDomain
import common.models.interaction
+from common.utils.nginx_utils import prefixed_url_for
from config.logging_config import LOGGING
from common.utils.security import set_tenant_session_data
from .errors import register_error_handlers
diff --git a/eveai_app/templates/email/forgot_password.html b/eveai_app/templates/email/forgot_password.html
new file mode 100644
index 0000000..7838c41
--- /dev/null
+++ b/eveai_app/templates/email/forgot_password.html
@@ -0,0 +1,13 @@
+
+
+
+ Reset Your Password
+
+
+ Hi,
+ You requested a password reset for your EveAI account. Click the link below to reset your password:
+ Reset Password
+ If you did not request a password reset, please ignore this email.
+ Thanks,
The EveAI Team
+
+
\ No newline at end of file
diff --git a/eveai_app/templates/security/email/reset_instructions.html b/eveai_app/templates/security/email/reset_instructions.html
new file mode 100644
index 0000000..8ef3aab
--- /dev/null
+++ b/eveai_app/templates/security/email/reset_instructions.html
@@ -0,0 +1,13 @@
+
+
+
+ Reset Your Password
+
+
+ Hi,
+ You requested a password reset for your EveAI account. Click the link below to reset your password:
+ Reset Password
+ If you did not request a password reset, please ignore this email.
+ Thanks,
The EveAI Team
+
+
\ No newline at end of file
diff --git a/eveai_app/templates/security/forgot_password.html b/eveai_app/templates/security/forgot_password.html
index eaa91dd..0fd4243 100644
--- a/eveai_app/templates/security/forgot_password.html
+++ b/eveai_app/templates/security/forgot_password.html
@@ -9,10 +9,16 @@
{% include "security/_messages.html" %}
- {% include "security/_menu.html" %}
+
{% endblock content %}
diff --git a/eveai_app/templates/security/login_user.html b/eveai_app/templates/security/login_user.html
index cf9e117..6e08bf3 100644
--- a/eveai_app/templates/security/login_user.html
+++ b/eveai_app/templates/security/login_user.html
@@ -14,10 +14,7 @@
{{ login_user_form.password.label }}
{{ login_user_form.password(size=80) }}
-{# #}
-{# {{ login_user_form.remember_me }}#}
-{# {{ login_user_form.remember_me.label }}#}
-{#
#}
+
{{ login_user_form.submit() }}
diff --git a/eveai_app/templates/security/reset_password.html b/eveai_app/templates/security/reset_password.html
index e8c59f2..5d89d04 100644
--- a/eveai_app/templates/security/reset_password.html
+++ b/eveai_app/templates/security/reset_password.html
@@ -1,12 +1,12 @@
{% extends "security/base.html" %}
{% from "macros.html" import render_field %}
-{% block title %} {{ _fsdomain('Reset password') }} {% endblock %}
-{% block content_title %} {{ _fsdomain('Reset password') }} {% endblock %}
+{% block title %} {{ _fsdomain('Reset AskEveAI password') }} {% endblock %}
+{% block content_title %} {{ _fsdomain('Reset AskEveAI password') }} {% endblock %}
{% block content_description %}An email will be sent to you with instructions.{% endblock %}
{% block content %}
{# {% include "security/_messages.html" %}#}
-{#