Skip to content

Commit 14cd19b

Browse files
Merge branch 'main' into dependabot/pip/cryptography-45.0.5
2 parents 138d58f + 8ee3778 commit 14cd19b

File tree

7 files changed

+31
-22
lines changed

7 files changed

+31
-22
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM python:3.11-slim
1+
FROM python:3.13-slim
22

33
# Default UID and GID (can be overridden)
44
ARG USER_UID=1000

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# DirectAdmin Email Forwarder Manager
22

33
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4-
[![Python 3.11](https://img.shields.io/badge/python-3.11-blue.svg)](https://www.python.org/downloads/)
4+
[![Python 3.11](https://img.shields.io/badge/python-3.13-blue.svg)](https://www.python.org/downloads/)
55

66
A Dockerized secure web application for managing email forwarders through the DirectAdmin API. Features a clean web interface with authentication, 2FA support, user management options.
77

app/auth.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import io
77
import base64
88
from datetime import datetime
9-
9+
from urllib.parse import urlparse
1010
auth_bp = Blueprint('auth', __name__)
1111

1212
@auth_bp.route('/login', methods=['GET', 'POST'])
@@ -32,7 +32,11 @@ def login():
3232
login_user(user)
3333

3434
next_page = request.args.get('next')
35-
return redirect(next_page) if next_page else redirect(url_for('index'))
35+
if next_page:
36+
safe_next = next_page.replace('\\', '')
37+
if not urlparse(safe_next).netloc and not urlparse(safe_next).scheme:
38+
return redirect(safe_next)
39+
return redirect(url_for('index'))
3640
else:
3741
flash('Invalid 2FA code', 'error')
3842
# Show 2FA form again with username preserved
@@ -54,7 +58,11 @@ def login():
5458
login_user(user)
5559

5660
next_page = request.args.get('next')
57-
return redirect(next_page) if next_page else redirect(url_for('index'))
61+
if next_page:
62+
safe_next = next_page.replace('\\', '')
63+
if not urlparse(safe_next).netloc and not urlparse(safe_next).scheme:
64+
return redirect(safe_next)
65+
return redirect(url_for('index'))
5866
else:
5967
flash('Invalid username or password', 'error')
6068

app/directadmin_api.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,10 @@ def test_connection(self):
167167
return False, "Failed to connect. Please check your credentials."
168168

169169
except Exception as e:
170-
return False, f"Connection error: {str(e)}"
170+
import traceback
171+
print(f"Connection error: {str(e)}")
172+
traceback.print_exc()
173+
return False, "Connection error: Unable to connect to DirectAdmin."
171174

172175
def get_email_accounts(self):
173176
"""Get all email accounts for the domain"""
@@ -439,7 +442,7 @@ def create_forwarder(self, address, destination):
439442
print(f"Error creating forwarder: {e}")
440443
import traceback
441444
traceback.print_exc()
442-
return False, str(e)
445+
return False, "An error occurred while creating the forwarder"
443446

444447
def delete_forwarder(self, address):
445448
"""Delete an email forwarder"""
@@ -492,7 +495,7 @@ def delete_forwarder(self, address):
492495

493496
except Exception as e:
494497
print(f"Error deleting forwarder: {e}")
495-
return False, str(e)
498+
return False, "An error occurred while deleting the forwarder"
496499

497500
def validate_email(self, email):
498501
"""Basic email validation"""

app/main.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ def get_email_accounts():
9393
traceback.print_exc()
9494
return jsonify({
9595
'error': 'Failed to fetch email accounts',
96-
'details': str(e),
9796
'accounts': []
9897
}), 500
9998

@@ -135,7 +134,6 @@ def get_forwarders():
135134
traceback.print_exc()
136135
return jsonify({
137136
'error': 'Failed to fetch forwarders',
138-
'details': str(e),
139137
'forwarders': []
140138
}), 500
141139

@@ -180,15 +178,14 @@ def create_forwarder():
180178
})
181179
else:
182180
return jsonify({
183-
'error': message
181+
'error': 'Failed to create forwarder'
184182
}), 400
185183

186184
except Exception as e:
187185
print(f"Error creating forwarder: {str(e)}")
188186
traceback.print_exc()
189187
return jsonify({
190-
'error': 'Failed to create forwarder',
191-
'details': str(e)
188+
'error': 'Failed to create forwarder'
192189
}), 500
193190

194191
@app.route('/api/forwarders', methods=['DELETE'])
@@ -234,8 +231,7 @@ def delete_forwarder():
234231
print(f"Error deleting forwarder: {str(e)}")
235232
traceback.print_exc()
236233
return jsonify({
237-
'error': 'Failed to delete forwarder',
238-
'details': str(e)
234+
'error': 'Failed to delete forwarder'
239235
}), 500
240236

241237
# ===== Error Handlers =====
@@ -360,4 +356,4 @@ def create_admin():
360356
app = create_app()
361357

362358
if __name__ == '__main__':
363-
app.run(host='0.0.0.0', port=5000, debug=True)
359+
app.run(host='0.0.0.0', port=5000)

app/settings.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ def get_da_config():
2525
})
2626
except Exception as e:
2727
print(f"Error in GET da-config: {e}")
28-
return jsonify({'error': str(e)}), 500
28+
print(traceback.format_exc())
29+
return jsonify({'error': 'An internal error has occurred. Please try again later.'}), 500
2930

3031
@settings_bp.route('/api/da-config', methods=['POST'])
3132
@login_required
@@ -86,7 +87,7 @@ def update_da_config():
8687
print(f"Error in POST da-config: {str(e)}")
8788
print(traceback.format_exc())
8889
db.session.rollback()
89-
return jsonify({'error': str(e)}), 500
90+
return jsonify({'error': 'An internal error has occurred. Please try again later.'}), 500
9091

9192
# Keep test-connection separate
9293
@settings_bp.route('/api/test-connection', methods=['POST'])
@@ -119,7 +120,8 @@ def test_connection():
119120

120121
except Exception as e:
121122
print(f"Test connection error: {str(e)}")
122-
return jsonify({'error': str(e), 'success': False}), 200
123+
print(traceback.format_exc())
124+
return jsonify({'error': 'An internal error has occurred.', 'success': False}), 200
123125

124126
# Debug route to check available routes
125127
@settings_bp.route('/api/debug-routes', methods=['GET'])

requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
Flask==2.3.2
1+
Flask==3.1.1
22
Flask-SQLAlchemy==3.1.1
33
Flask-Login==0.6.3
4-
Werkzeug==2.3.6
4+
Werkzeug==3.1.3
55
gunicorn==23.0.0
66
pyotp==2.9.0
77
qrcode==8.2
88
pillow==11.3.0
99
requests==2.32.4
10-
cryptography==45.0.5
10+
cryptography==45.0.5

0 commit comments

Comments
 (0)