Merge pull request #64 from the-luap/claude/investigate-issue-62-01GFcj5uDDgojgX5D8ytoF9W

Add option to ignore SSL/TLS certificate errors for email (Issue #53)
This commit is contained in:
Paul Nothaft
2025-11-25 21:31:22 +01:00
committed by GitHub
7 changed files with 97 additions and 12 deletions
@@ -0,0 +1,18 @@
const { addColumnIfNotExists } = require('../helpers');
exports.up = async function up(knex) {
// Add tls_reject_unauthorized column to email_configs table
// Default is true (validate certificates), false means ignore SSL/TLS certificate errors
await addColumnIfNotExists(knex, 'email_configs', 'tls_reject_unauthorized', (table) => {
table.boolean('tls_reject_unauthorized').defaultTo(true);
});
};
exports.down = async function down(knex) {
const hasColumn = await knex.schema.hasColumn('email_configs', 'tls_reject_unauthorized');
if (hasColumn) {
await knex.schema.alterTable('email_configs', (table) => {
table.dropColumn('tls_reject_unauthorized');
});
}
};
+9 -2
View File
@@ -18,7 +18,8 @@ router.get('/config', adminAuth, async (req, res) => {
smtp_user: '',
smtp_pass: '', // Don't send actual password
from_email: '',
from_name: ''
from_name: '',
tls_reject_unauthorized: true
});
}
@@ -53,7 +54,8 @@ router.post('/config', [
smtp_user,
smtp_pass,
from_email,
from_name
from_name,
tls_reject_unauthorized
} = req.body;
// Check if config exists
@@ -66,6 +68,7 @@ router.post('/config', [
smtp_user: smtp_user || '',
from_email,
from_name: from_name || 'Photo Sharing',
tls_reject_unauthorized: tls_reject_unauthorized !== false, // Default to true
updated_at: new Date()
};
@@ -137,6 +140,10 @@ router.post('/test', adminAuth, async (req, res) => {
user: config.smtp_user,
pass: config.smtp_pass
} : undefined,
tls: {
// Allow ignoring SSL certificate errors when tls_reject_unauthorized is false
rejectUnauthorized: config.tls_reject_unauthorized !== false
},
logger: process.env.NODE_ENV === 'development',
debug: process.env.NODE_ENV === 'development'
};
+6 -2
View File
@@ -9,7 +9,7 @@ let lastConfigHash = null;
// Generate hash from config for change detection
function generateConfigHash(config) {
const crypto = require('crypto');
const configString = `${config.smtp_host}:${config.smtp_port}:${config.smtp_user}:${config.smtp_pass}:${config.smtp_secure}`;
const configString = `${config.smtp_host}:${config.smtp_port}:${config.smtp_user}:${config.smtp_pass}:${config.smtp_secure}:${config.tls_reject_unauthorized}`;
return crypto.createHash('md5').update(configString).digest('hex');
}
@@ -40,7 +40,11 @@ async function initializeTransporter(forceReinit = false) {
auth: config.smtp_user ? {
user: config.smtp_user,
pass: config.smtp_pass
} : undefined
} : undefined,
tls: {
// Allow ignoring SSL certificate errors when tls_reject_unauthorized is false
rejectUnauthorized: config.tls_reject_unauthorized !== false
}
});
// Verify configuration
+28 -1
View File
@@ -1327,7 +1327,34 @@
"saveConfiguration": "Konfiguration speichern",
"emailTemplates": "E-Mail-Vorlagen",
"templateVariables": "Verfügbare Variablen",
"previewTemplate": "Vorlage anzeigen"
"previewTemplate": "Vorlage anzeigen",
"smtpSettings": "SMTP-Einstellungen",
"testEmailSuccess": "Test-E-Mail erfolgreich gesendet",
"saveSmtpSettings": "SMTP-Einstellungen speichern",
"testEmailSection": "E-Mail testen",
"beforeTesting": "Vor dem Testen:",
"saveSmtpFirst": "Speichern Sie zuerst Ihre SMTP-Einstellungen",
"ensureFirewall": "Stellen Sie sicher, dass Ihre Firewall ausgehende SMTP-Verbindungen erlaubt",
"gmailAppPassword": "Für Gmail verwenden Sie ein App-spezifisches Passwort",
"testEmailAddressLabel": "Test-E-Mail-Adresse",
"sendTestEmailButton": "Test-E-Mail senden",
"commonSmtpSettings": "Häufige SMTP-Einstellungen:",
"editTemplate": "Vorlage bearbeiten",
"templateName": "Vorlagenname",
"subjectLine": "Betreffzeile",
"emailBody": "E-Mail-Text",
"preview": "Vorschau",
"saveChanges": "Änderungen speichern",
"templates": "Vorlagen",
"variableHelp": "Verwenden Sie diese Variablen in Ihrer Vorlage. Sie werden beim Senden durch tatsächliche Werte ersetzt.",
"port": "Port",
"security": "Sicherheit",
"username": "Benutzername",
"password": "Passwort",
"enterPassword": "Passwort eingeben",
"required": "erforderlich",
"ignoreSslErrors": "SSL/TLS-Zertifikatfehler ignorieren",
"ignoreSslWarning": "Warnung: Das Deaktivieren der Zertifikatüberprüfung macht die Verbindung anfällig für Man-in-the-Middle-Angriffe. Aktivieren Sie dies nur, wenn Sie dem SMTP-Server vertrauen und die Sicherheitsrisiken verstehen."
},
"cms": {
"title": "CMS-Seiten",
+3 -1
View File
@@ -1072,7 +1072,9 @@
"enterPassword": "Enter password",
"fromEmail": "From Email",
"fromName": "From Name",
"required": "required"
"required": "required",
"ignoreSslErrors": "Ignore SSL/TLS certificate errors",
"ignoreSslWarning": "Warning: Disabling certificate verification makes the connection vulnerable to man-in-the-middle attacks. Only enable this if you trust the SMTP server and understand the security implications."
},
"cms": {
"title": "CMS Pages",
+32 -6
View File
@@ -1,8 +1,8 @@
import React, { useState } from 'react';
import {
Mail,
Save,
Send,
import {
Mail,
Save,
Send,
Server,
Lock,
User,
@@ -10,6 +10,7 @@ import {
CheckCircle,
Eye,
EyeOff,
ShieldAlert,
} from 'lucide-react';
import { toast } from 'react-toastify';
@@ -107,7 +108,8 @@ export const EmailConfigPage: React.FC = () => {
smtp_user: '',
smtp_pass: '',
from_email: '',
from_name: 'Photo Sharing'
from_name: 'Photo Sharing',
tls_reject_unauthorized: true
});
// Fetch SMTP config
@@ -342,7 +344,7 @@ export const EmailConfigPage: React.FC = () => {
placeholder="587"
/>
</div>
<div>
<label className="block text-sm font-medium text-neutral-700 mb-1">
{t('email.security')}
@@ -358,6 +360,30 @@ export const EmailConfigPage: React.FC = () => {
</div>
</div>
{/* Ignore SSL Certificate Errors */}
<div className="mt-2">
<label className="flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
checked={!smtpConfig.tls_reject_unauthorized}
onChange={(e) => setSmtpConfig(prev => ({ ...prev, tls_reject_unauthorized: !e.target.checked }))}
className="w-4 h-4 text-primary-600 border-neutral-300 rounded focus:ring-primary-500"
/>
<span className="text-sm font-medium text-neutral-700">
{t('email.ignoreSslErrors')}
</span>
</label>
{!smtpConfig.tls_reject_unauthorized && (
<div className="mt-2 p-3 bg-amber-50 border border-amber-200 rounded-lg">
<div className="flex items-start gap-2">
<ShieldAlert className="w-4 h-4 text-amber-600 flex-shrink-0 mt-0.5" />
<p className="text-xs text-amber-800">
{t('email.ignoreSslWarning')}
</p>
</div>
</div>
)}</div>
<div>
<label className="block text-sm font-medium text-neutral-700 mb-1">
{t('email.username')}
+1
View File
@@ -8,6 +8,7 @@ export interface EmailConfig {
smtp_pass: string;
from_email: string;
from_name: string;
tls_reject_unauthorized: boolean;
}
export interface EmailTemplate {