better password management

This commit is contained in:
2025-06-04 13:44:49 +02:00
parent 88c3bc1b5b
commit 41cdd5ec7f
24 changed files with 246 additions and 10 deletions

View File

@@ -1,6 +1,6 @@
from flask import Blueprint, render_template, redirect, url_for, flash, request, jsonify
from flask_login import login_required, current_user
from models import db, User, Notif
from models import db, User, Notif, PasswordSetupToken
from forms import UserForm
from flask import abort
from sqlalchemy import or_
@@ -9,7 +9,9 @@ from utils import log_event, create_notification, get_unread_count
import json
import os
from werkzeug.utils import secure_filename
from datetime import datetime
from datetime import datetime, timedelta
import secrets
import string
contacts_bp = Blueprint('contacts', __name__, url_prefix='/contacts')
@@ -113,6 +115,10 @@ def new_contact():
file.save(file_path)
profile_picture = filename
# Generate a random password
alphabet = string.ascii_letters + string.digits + string.punctuation
random_password = ''.join(secrets.choice(alphabet) for _ in range(32))
# Create new user account
user = User(
username=form.first_name.data,
@@ -126,10 +132,20 @@ def new_contact():
is_admin=form.is_admin.data,
profile_picture=profile_picture
)
user.set_password('changeme') # Set default password
user.set_password(random_password) # Set random password
db.session.add(user)
db.session.commit()
# Create password setup token
token = secrets.token_urlsafe(32)
setup_token = PasswordSetupToken(
user_id=user.id,
token=token,
expires_at=datetime.utcnow() + timedelta(hours=24)
)
db.session.add(setup_token)
db.session.commit()
# Create notification for the new user
create_notification(
notif_type='account_created',
@@ -140,7 +156,8 @@ def new_contact():
'username': user.username,
'email': user.email,
'created_by': f"{current_user.username} {current_user.last_name}",
'timestamp': datetime.utcnow().isoformat()
'timestamp': datetime.utcnow().isoformat(),
'setup_link': url_for('auth.setup_password', token=token, _external=True)
}
)
@@ -159,7 +176,7 @@ def new_contact():
)
db.session.commit()
flash('User created successfully! They will need to change their password on first login.', 'success')
flash('User created successfully! They will receive an email with a link to set up their password.', 'success')
return redirect(url_for('contacts.contacts_list'))
return render_template('contacts/form.html', form=form, title='New User', total_admins=total_admins)
@@ -446,4 +463,54 @@ def toggle_active(id):
db.session.commit()
flash(f'User marked as {"active" if user.is_active else "inactive"}!', 'success')
return redirect(url_for('contacts.contacts_list'))
@contacts_bp.route('/<int:id>/resend-setup', methods=['POST'])
@login_required
@require_password_change
def resend_setup_link(id):
result = admin_required()
if result: return result
user = User.query.get_or_404(id)
# Create new password setup token
token = secrets.token_urlsafe(32)
setup_token = PasswordSetupToken(
user_id=user.id,
token=token,
expires_at=datetime.utcnow() + timedelta(hours=24)
)
db.session.add(setup_token)
# Create notification for the user
create_notification(
notif_type='account_created',
user_id=user.id,
sender_id=current_user.id,
details={
'message': 'A new password setup link has been sent to you.',
'username': user.username,
'email': user.email,
'created_by': f"{current_user.username} {current_user.last_name}",
'timestamp': datetime.utcnow().isoformat(),
'setup_link': url_for('auth.setup_password', token=token, _external=True)
}
)
# Log the event
log_event(
event_type='user_update',
details={
'user_id': user.id,
'user_name': f"{user.username} {user.last_name}",
'updated_by': current_user.id,
'updated_by_name': f"{current_user.username} {current_user.last_name}",
'update_type': 'password_setup_link_resend'
}
)
db.session.commit()
flash('Password setup link has been resent to the user.', 'success')
return redirect(url_for('contacts.contacts_list'))