phosphat-report-app/app/routes/test-encryption.tsx
2025-08-01 05:51:08 +03:00

168 lines
7.6 KiB
TypeScript

import type { LoaderFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { requireAuthLevel } from "~/utils/auth.server";
import DashboardLayout from "~/components/DashboardLayout";
import { testEncryption, encrypt, decrypt, migrateEncryption, isEncrypted, isLegacyEncrypted } from "~/utils/encryption.server";
export async function loader({ request }: LoaderFunctionArgs) {
// Require auth level 3 to access encryption test
const user = await requireAuthLevel(request, 3);
// Test encryption with sample data
const testResults = testEncryption("sample-smtp-password-123");
// Test with different types of data
const tests = [
{ name: "Simple Password", data: "mypassword123" },
{ name: "Complex Password", data: "P@ssw0rd!@#$%^&*()" },
{ name: "Email Password", data: "smtp.gmail.app.password" },
{ name: "Special Characters", data: "test!@#$%^&*()_+-=[]{}|;:,.<>?" }
];
const testResults2 = tests.map(test => {
try {
const encrypted = encrypt(test.data);
const decrypted = decrypt(encrypted);
const migrated = migrateEncryption(test.data);
const migratedDecrypted = decrypt(migrated);
return {
...test,
encrypted,
decrypted,
success: decrypted === test.data,
encryptedLength: encrypted.length,
migrationSuccess: migratedDecrypted === test.data,
isNewFormat: isEncrypted(encrypted),
isLegacyFormat: isLegacyEncrypted(test.data)
};
} catch (error) {
return {
...test,
error: error instanceof Error ? error.message : 'Unknown error',
success: false
};
}
});
return json({
user,
basicTest: testResults,
detailedTests: testResults2
});
}
export default function TestEncryption() {
const { user, basicTest, detailedTests } = useLoaderData<typeof loader>();
return (
<DashboardLayout user={user}>
<div className="max-w-4xl mx-auto p-6">
<h1 className="text-2xl font-bold mb-6">Encryption Test Results</h1>
{/* Basic Test */}
<div className="bg-white shadow rounded-lg p-6 mb-6">
<h2 className="text-lg font-semibold mb-4">Basic Encryption Test</h2>
<div className="space-y-2">
<div className="flex items-center">
<span className="font-medium w-20">Status:</span>
<span className={`px-2 py-1 rounded text-sm ${
basicTest.success ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
}`}>
{basicTest.success ? 'PASSED' : 'FAILED'}
</span>
</div>
{basicTest.success ? (
<>
<div><span className="font-medium">Original:</span> {basicTest.original}</div>
<div><span className="font-medium">Encrypted:</span> <code className="bg-gray-100 px-2 py-1 rounded text-sm">{basicTest.encrypted}</code></div>
<div><span className="font-medium">Decrypted:</span> {basicTest.decrypted}</div>
<div><span className="font-medium">Valid:</span> {basicTest.isValid ? '✅' : '❌'}</div>
</>
) : (
<div className="text-red-600">Error: {basicTest.error}</div>
)}
</div>
</div>
{/* Detailed Tests */}
<div className="bg-white shadow rounded-lg p-6">
<h2 className="text-lg font-semibold mb-4">Detailed Encryption Tests</h2>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Test Name</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Original</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Encrypted Length</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Match</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Migration</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{detailedTests.map((test, index) => (
<tr key={index}>
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
{test.name}
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`px-2 py-1 rounded text-xs ${
test.success ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
}`}>
{test.success ? 'PASS' : 'FAIL'}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<code className="bg-gray-100 px-2 py-1 rounded text-xs">
{test.data.length > 20 ? test.data.substring(0, 20) + '...' : test.data}
</code>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{test.encryptedLength || 'N/A'}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm">
{test.success ? '✅' : '❌'}
{test.error && <div className="text-red-500 text-xs mt-1">{test.error}</div>}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm">
{test.migrationSuccess ? '✅' : '❌'}
<div className="text-xs text-gray-500 mt-1">
Format: {test.isNewFormat ? 'New' : test.isLegacyFormat ? 'Legacy' : 'Plain'}
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
{/* Security Notes */}
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mt-6">
<h3 className="text-lg font-semibold text-yellow-800 mb-2">Security Notes</h3>
<ul className="text-sm text-yellow-700 space-y-1">
<li> Passwords are encrypted using AES-256-CBC algorithm</li>
<li> Each encryption uses a random IV (Initialization Vector)</li>
<li> Encrypted data format: IV:EncryptedData (hex)</li>
<li> Migration support for legacy encrypted data</li>
<li> Encryption key should be set via ENCRYPTION_KEY environment variable</li>
<li> This test page should only be accessible to Super Admins</li>
</ul>
</div>
{/* Node.js Version Info */}
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4 mt-4">
<h3 className="text-lg font-semibold text-blue-800 mb-2">Environment Info</h3>
<div className="text-sm text-blue-700 space-y-1">
<div> Node.js Version: {process.version}</div>
<div> Platform: {process.platform}</div>
<div> Architecture: {process.arch}</div>
<div> Encryption Format: New (createCipheriv/createDecipheriv)</div>
</div>
</div>
</div>
</DashboardLayout>
);
}