password reset online test

This commit is contained in:
2025-06-20 13:18:13 +02:00
parent e25c7660b0
commit c9d1d7416b
3 changed files with 513 additions and 65 deletions

View File

@@ -2,7 +2,7 @@ from flask import Blueprint, jsonify, request, current_app, make_response, flash
from functools import wraps
from models import (
KeyValueSettings, User, Room, Conversation, RoomFile,
SiteSettings, DocuPulseSettings, Event, Mail, ManagementAPIKey
SiteSettings, DocuPulseSettings, Event, Mail, ManagementAPIKey, PasswordSetupToken, PasswordResetToken
)
from extensions import db, csrf
from datetime import datetime, timedelta
@@ -526,4 +526,37 @@ def resend_setup_mail(current_user, user_id):
db.session.add(mail)
db.session.commit()
return jsonify({'message': 'Setup mail queued for resending'})
return jsonify({'message': 'Setup mail queued for resending'})
# Generate Password Reset Token
@admin_api.route('/generate-password-reset/<int:user_id>', methods=['POST'])
@csrf.exempt
@token_required
def generate_password_reset_token(current_user, user_id):
user = User.query.get(user_id)
if not user:
return jsonify({'message': 'User not found'}), 404
# Generate a secure token for password reset
token = secrets.token_urlsafe(32)
# Create password reset token
reset_token = PasswordResetToken(
user_id=user.id,
token=token,
expires_at=datetime.utcnow() + timedelta(hours=24), # 24 hour expiration
ip_address=request.remote_addr
)
db.session.add(reset_token)
db.session.commit()
# Return the token and reset URL
reset_url = f"{request.host_url.rstrip('/')}/reset-password/{token}"
return jsonify({
'message': 'Password reset token generated successfully',
'token': token,
'reset_url': reset_url,
'expires_at': reset_token.expires_at.isoformat(),
'user_email': user.email
})

View File

@@ -1101,40 +1101,11 @@ def update_admin_credentials():
if not jwt_token:
return jsonify({'error': 'No JWT token received'}), 400
# Generate a secure password
import secrets
import string
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
new_password = ''.join(secrets.choice(alphabet) for i in range(16))
# First, login with default credentials to get admin token
login_response = requests.post(
f"{instance_url.rstrip('/')}/api/admin/login",
headers={
'Content-Type': 'application/json',
'Accept': 'application/json'
},
json={
'email': 'administrator@docupulse.com',
'password': 'changeme'
},
timeout=10
)
if login_response.status_code != 200:
return jsonify({'error': f'Failed to login with default credentials: {login_response.text}'}), 400
login_data = login_response.json()
if login_data.get('status') != 'success' or not login_data.get('token'):
return jsonify({'error': 'Failed to get admin token'}), 400
admin_token = login_data.get('token')
# Get the admin user ID first
users_response = requests.get(
f"{instance_url.rstrip('/')}/api/admin/contacts",
headers={
'Authorization': f'Bearer {admin_token}',
'Authorization': f'Bearer {jwt_token}',
'Accept': 'application/json'
},
timeout=10
@@ -1148,7 +1119,7 @@ def update_admin_credentials():
# Find the administrator user
for user in users_data:
if user.get('email') == 'administrator@docupulse.com':
if user.get('email') == email:
admin_user = user
break
@@ -1157,37 +1128,359 @@ def update_admin_credentials():
admin_user_id = admin_user.get('id')
# Update the admin user with new email and password
update_response = requests.put(
f"{instance_url.rstrip('/')}/api/admin/contacts/{admin_user_id}",
# Try to login with default credentials first
try:
login_response = requests.post(
f"{instance_url.rstrip('/')}/api/admin/login",
headers={
'Content-Type': 'application/json',
'Accept': 'application/json'
},
json={
'email': 'administrator@docupulse.com',
'password': 'changeme'
},
timeout=10
)
# If login with default credentials succeeds, update the credentials
if login_response.status_code == 200:
login_data = login_response.json()
if login_data.get('status') == 'success' and login_data.get('token'):
admin_token = login_data.get('token')
# Generate a secure password
import secrets
import string
alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
new_password = ''.join(secrets.choice(alphabet) for i in range(16))
# Update the admin user with new email and password
update_response = requests.put(
f"{instance_url.rstrip('/')}/api/admin/contacts/{admin_user_id}",
headers={
'Authorization': f'Bearer {admin_token}',
'Content-Type': 'application/json',
'Accept': 'application/json'
},
json={
'email': email,
'password': new_password,
'username': 'administrator',
'last_name': 'Administrator',
'role': 'admin'
},
timeout=10
)
if update_response.status_code != 200:
return jsonify({'error': f'Failed to update admin credentials: {update_response.text}'}), 400
return jsonify({
'message': 'Admin credentials updated successfully',
'data': {
'email': email,
'password': new_password,
'username': 'administrator',
'instance_url': instance_url
}
})
# If login with default credentials fails, check if email is already updated
else:
# Check if the email is already the target email
if admin_user.get('email') == email:
return jsonify({
'message': 'Admin credentials already updated',
'data': {
'email': email,
'password': 'Already set',
'username': 'administrator',
'instance_url': instance_url,
'already_updated': True
}
})
else:
return jsonify({'error': 'Failed to login with default credentials and email not yet updated'}), 400
except Exception as login_error:
# If there's an error with login, check if email is already updated
if admin_user.get('email') == email:
return jsonify({
'message': 'Admin credentials already updated',
'data': {
'email': email,
'password': 'Already set',
'username': 'administrator',
'instance_url': instance_url,
'already_updated': True
}
})
else:
return jsonify({'error': f'Login error and email not yet updated: {str(login_error)}'}), 400
except Exception as e:
current_app.logger.error(f"Error updating admin credentials: {str(e)}")
return jsonify({'error': str(e)}), 500
@launch_api.route('/send-completion-email', methods=['POST'])
@csrf.exempt
def send_completion_email():
"""Send completion email to client with instance details and password reset link"""
try:
if not request.is_json:
return jsonify({'error': 'Request must be JSON'}), 400
data = request.get_json()
required_fields = ['instance_url', 'company_data', 'credentials_data']
if not all(field in data for field in required_fields):
missing_fields = [field for field in required_fields if field not in data]
return jsonify({'error': f'Missing required fields: {", ".join(missing_fields)}'}), 400
instance_url = data['instance_url']
company_data = data['company_data']
credentials_data = data['credentials_data']
# Get SMTP settings from master instance
smtp_settings = KeyValueSettings.get_value('smtp_settings')
if not smtp_settings:
return jsonify({'error': 'SMTP settings not configured'}), 400
# Get the instance from database to get the connection token
instance = Instance.query.filter_by(main_url=instance_url).first()
if not instance:
return jsonify({'error': 'Instance not found in database'}), 404
if not instance.connection_token:
return jsonify({'error': 'Instance not authenticated'}), 400
# Get JWT token from the launched instance using management API key
jwt_response = requests.post(
f"{instance_url.rstrip('/')}/api/admin/management-token",
headers={
'Authorization': f'Bearer {admin_token}',
'Content-Type': 'application/json',
'X-API-Key': instance.connection_token,
'Accept': 'application/json'
},
json={
'email': email,
'password': new_password,
'username': 'administrator',
'last_name': 'Administrator',
'role': 'admin'
},
timeout=10
)
if update_response.status_code != 200:
return jsonify({'error': f'Failed to update admin credentials: {update_response.text}'}), 400
if jwt_response.status_code != 200:
return jsonify({'error': f'Failed to get JWT token: {jwt_response.text}'}), 400
return jsonify({
'message': 'Admin credentials updated successfully',
'data': {
'email': email,
'password': new_password,
'username': 'administrator',
'instance_url': instance_url
}
})
jwt_data = jwt_response.json()
jwt_token = jwt_data.get('token')
if not jwt_token:
return jsonify({'error': 'No JWT token received'}), 400
# Get the admin user ID from the launched instance
users_response = requests.get(
f"{instance_url.rstrip('/')}/api/admin/contacts",
headers={
'Authorization': f'Bearer {jwt_token}',
'Accept': 'application/json'
},
timeout=10
)
if users_response.status_code != 200:
return jsonify({'error': f'Failed to get users: {users_response.text}'}), 400
users_data = users_response.json()
admin_user = None
# Find the administrator user by email
for user in users_data:
if user.get('email') == credentials_data.get('email'):
admin_user = user
break
if not admin_user:
return jsonify({'error': 'Administrator user not found'}), 404
admin_user_id = admin_user.get('id')
# Generate password reset token for the launched instance using management API
reset_response = requests.post(
f"{instance_url.rstrip('/')}/api/admin/generate-password-reset/{admin_user_id}",
headers={
'Authorization': f'Bearer {jwt_token}',
'Accept': 'application/json'
},
timeout=10
)
if reset_response.status_code != 200:
return jsonify({'error': f'Failed to generate password reset: {reset_response.text}'}), 400
reset_data = reset_response.json()
reset_url = reset_data.get('reset_url')
expires_at = reset_data.get('expires_at')
if not reset_url:
return jsonify({'error': 'No reset URL received from instance'}), 400
# Create email content
subject = "Your DocuPulse Instance is Ready!"
# Build HTML email content
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {{ font-family: Arial, sans-serif; line-height: 1.6; color: #333; }}
.container {{ max-width: 600px; margin: 0 auto; padding: 20px; }}
.header {{ background-color: #16767b; color: white; padding: 20px; text-align: center; border-radius: 5px 5px 0 0; }}
.content {{ background-color: #f9f9f9; padding: 20px; border-radius: 0 0 5px 5px; }}
.credentials {{ background-color: #e8f5e9; padding: 15px; border-radius: 5px; margin: 15px 0; }}
.button {{ display: inline-block; background-color: #16767b; color: white; padding: 12px 24px; text-decoration: none; border-radius: 5px; margin: 10px 0; }}
.footer {{ margin-top: 20px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 12px; color: #666; }}
.info-box {{ background-color: #e3f2fd; padding: 15px; border-radius: 5px; margin: 15px 0; }}
.security-notice {{ background-color: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 5px; margin: 15px 0; }}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🎉 Your DocuPulse Instance is Ready!</h1>
</div>
<div class="content">
<p>Dear {company_data.get('name', 'Valued Customer')},</p>
<p>Great news! Your DocuPulse instance has been successfully deployed and configured.
You can now access your secure document management platform.</p>
<div class="info-box">
<h3>📋 Instance Details</h3>
<p><strong>Instance URL:</strong> <a href="{instance_url}">{instance_url}</a></p>
<p><strong>Company Name:</strong> {company_data.get('name', 'Not set')}</p>
<p><strong>Industry:</strong> {company_data.get('industry', 'Not set')}</p>
<p><strong>Deployment Date:</strong> {datetime.utcnow().strftime('%B %d, %Y at %I:%M %p UTC')}</p>
</div>
<div class="credentials">
<h3>🔐 Account Access</h3>
<p><strong>Email Address:</strong> {credentials_data.get('email', 'Not set')}</p>
<p><strong>Username:</strong> {credentials_data.get('username', 'administrator')}</p>
<div class="security-notice">
<h4>🔒 Security Setup Required</h4>
<p>For your security, you need to set up your password. Click the button below to create your secure password.</p>
<p><strong>Password Reset Link Expires:</strong> {expires_at}</p>
</div>
</div>
<div style="text-align: center; margin: 30px 0;">
<a href="{reset_url}" class="button">🔐 Set Up Your Password</a>
<br><br>
<a href="{instance_url}" class="button">🚀 Access Your Instance</a>
</div>
<h3>✅ What's Been Configured</h3>
<ul>
<li>✅ Secure SSL certificate for HTTPS access</li>
<li>✅ Company information and branding</li>
<li>✅ Custom color scheme</li>
<li>✅ Admin account created</li>
<li>✅ Document management system ready</li>
</ul>
<h3>🎯 Next Steps</h3>
<ol>
<li>Click the "Set Up Your Password" button above</li>
<li>Create your secure password</li>
<li>Return to your instance and log in</li>
<li>Explore your new DocuPulse platform</li>
<li>Start uploading and organizing your documents</li>
<li>Invite team members to collaborate</li>
</ol>
<div class="footer">
<p>If you have any questions or need assistance, please don't hesitate to contact our support team.</p>
<p>Thank you for choosing DocuPulse!</p>
</div>
</div>
</div>
</body>
</html>
"""
# Build plain text version
text_content = f"""
Your DocuPulse Instance is Ready!
Dear {company_data.get('name', 'Valued Customer')},
Great news! Your DocuPulse instance has been successfully deployed and configured.
INSTANCE DETAILS:
- Instance URL: {instance_url}
- Company Name: {company_data.get('name', 'Not set')}
- Industry: {company_data.get('industry', 'Not set')}
- Deployment Date: {datetime.utcnow().strftime('%B %d, %Y at %I:%M %p UTC')}
ACCOUNT ACCESS:
- Email Address: {credentials_data.get('email', 'Not set')}
- Username: {credentials_data.get('username', 'administrator')}
SECURITY SETUP REQUIRED:
For your security, you need to set up your password.
Password Reset Link: {reset_url}
Password Reset Link Expires: {expires_at}
WHAT'S BEEN CONFIGURED:
✓ Secure SSL certificate for HTTPS access
✓ Company information and branding
✓ Custom color scheme
✓ Admin account created
✓ Document management system ready
NEXT STEPS:
1. Click the password reset link above
2. Create your secure password
3. Return to your instance and log in
4. Explore your new DocuPulse platform
5. Start uploading and organizing your documents
6. Invite team members to collaborate
If you have any questions or need assistance, please don't hesitate to contact our support team.
Thank you for choosing DocuPulse!
"""
# Send email using master instance's email system
from utils.email_templates import send_email
try:
send_email(
to_email=company_data.get('email'),
subject=subject,
html_content=html_content,
text_content=text_content
)
# Log the email sending
current_app.logger.info(f"Completion email sent to {company_data.get('email')} for instance {instance_url}")
return jsonify({
'message': 'Completion email sent successfully',
'data': {
'recipient': company_data.get('email'),
'subject': subject,
'instance_url': instance_url,
'password_reset_sent': True
}
})
except Exception as email_error:
current_app.logger.error(f"Failed to send completion email: {str(email_error)}")
return jsonify({'error': f'Failed to send email: {str(email_error)}'}), 500
except Exception as e:
current_app.logger.error(f"Error updating admin credentials: {str(e)}")
current_app.logger.error(f"Error sending completion email: {str(e)}")
return jsonify({'error': str(e)}), 500