feat: enhance email configuration and validation features
- Implemented mutual exclusivity for SSL and TLS options in the email configuration. - Added comprehensive validation for required fields, port range, email format, and password requirements. - Updated the backend to support reversible encryption for SMTP passwords, ensuring secure handling of sensitive data. - Introduced loading states and improved user feedback in the email configuration UI for better user experience.
This commit is contained in:
@@ -130,3 +130,106 @@ export function validarSenha(senha: string): boolean {
|
||||
return regex.test(senha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Criptografia reversível para senhas SMTP usando AES-GCM
|
||||
* NOTA: Esta função é usada apenas para senhas SMTP que precisam ser descriptografadas.
|
||||
* Para senhas de usuários, use hashPassword() que é unidirecional.
|
||||
*/
|
||||
|
||||
// Chave de criptografia derivada (em produção, deve vir de variável de ambiente)
|
||||
// Para desenvolvimento, usando uma chave fixa. Em produção, deve ser configurada via env var.
|
||||
const getEncryptionKey = async (): Promise<CryptoKey> => {
|
||||
// Chave base - em produção, isso deve vir de process.env.ENCRYPTION_KEY
|
||||
// Por enquanto, usando uma chave derivada de um valor fixo
|
||||
const keyMaterial = new TextEncoder().encode("SGSE-EMAIL-ENCRYPTION-KEY-2024");
|
||||
|
||||
// Deriva uma chave de 256 bits usando PBKDF2
|
||||
const key = await crypto.subtle.importKey(
|
||||
"raw",
|
||||
keyMaterial,
|
||||
{ name: "PBKDF2" },
|
||||
false,
|
||||
["deriveBits", "deriveKey"]
|
||||
);
|
||||
|
||||
return await crypto.subtle.deriveKey(
|
||||
{
|
||||
name: "PBKDF2",
|
||||
salt: new TextEncoder().encode("SGSE-SALT"),
|
||||
iterations: 100000,
|
||||
hash: "SHA-256",
|
||||
},
|
||||
key,
|
||||
{ name: "AES-GCM", length: 256 },
|
||||
false,
|
||||
["encrypt", "decrypt"]
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Criptografa uma senha SMTP usando AES-GCM
|
||||
*/
|
||||
export async function encryptSMTPPassword(password: string): Promise<string> {
|
||||
try {
|
||||
const key = await getEncryptionKey();
|
||||
const encoder = new TextEncoder();
|
||||
const data = encoder.encode(password);
|
||||
|
||||
// Gerar IV (Initialization Vector) aleatório
|
||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
||||
|
||||
// Criptografar
|
||||
const encrypted = await crypto.subtle.encrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv: iv,
|
||||
},
|
||||
key,
|
||||
data
|
||||
);
|
||||
|
||||
// Combinar IV + dados criptografados e converter para base64
|
||||
const combined = new Uint8Array(iv.length + encrypted.byteLength);
|
||||
combined.set(iv);
|
||||
combined.set(new Uint8Array(encrypted), iv.length);
|
||||
|
||||
return btoa(String.fromCharCode(...combined));
|
||||
} catch (error) {
|
||||
console.error("Erro ao criptografar senha SMTP:", error);
|
||||
throw new Error("Falha ao criptografar senha SMTP");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Descriptografa uma senha SMTP usando AES-GCM
|
||||
*/
|
||||
export async function decryptSMTPPassword(encryptedPassword: string): Promise<string> {
|
||||
try {
|
||||
const key = await getEncryptionKey();
|
||||
|
||||
// Decodificar base64
|
||||
const combined = Uint8Array.from(atob(encryptedPassword), (c) => c.charCodeAt(0));
|
||||
|
||||
// Extrair IV e dados criptografados
|
||||
const iv = combined.slice(0, 12);
|
||||
const encrypted = combined.slice(12);
|
||||
|
||||
// Descriptografar
|
||||
const decrypted = await crypto.subtle.decrypt(
|
||||
{
|
||||
name: "AES-GCM",
|
||||
iv: iv,
|
||||
},
|
||||
key,
|
||||
encrypted
|
||||
);
|
||||
|
||||
// Converter para string
|
||||
const decoder = new TextDecoder();
|
||||
return decoder.decode(decrypted);
|
||||
} catch (error) {
|
||||
console.error("Erro ao descriptografar senha SMTP:", error);
|
||||
throw new Error("Falha ao descriptografar senha SMTP");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user