espace-paie-odentas/test-signature-complete.mjs

281 lines
8.9 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Test complet du système de signature et vérification
*
* Ce script :
* 1. Lit le fichier test-contrat.pdf
* 2. Appelle la Lambda Odentas Sign pour signer
* 3. Extrait le hash de signature du PDF signé
* 4. Upload le PDF signé sur S3
* 5. Crée l'entrée de vérification via l'API
* 6. Génère le PDF de preuve avec QR code
* 7. Sauvegarde les fichiers résultants
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import crypto from 'crypto';
import AWS from 'aws-sdk';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Configuration AWS
AWS.config.update({
region: 'eu-west-3',
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
});
const lambda = new AWS.Lambda();
const s3 = new AWS.S3();
// Configuration
const TEST_PDF_PATH = path.join(__dirname, 'test-contrat.pdf');
const OUTPUT_DIR = path.join(__dirname, 'test-signature-output');
const S3_BUCKET = 'odentas-espace-paie-documents';
async function main() {
console.log('🚀 Test complet du système de signature électronique\n');
// Créer le dossier de sortie
if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR);
}
// Étape 1 : Lire le PDF à signer
console.log('📄 Étape 1 : Lecture du PDF à signer...');
const pdfBuffer = fs.readFileSync(TEST_PDF_PATH);
console.log(` ✅ PDF lu (${pdfBuffer.length} bytes)\n`);
// Étape 2 : Upload du PDF source sur S3
console.log('☁️ Étape 2 : Upload du PDF source sur S3...');
const sourceKey = await uploadToS3ForSigning(pdfBuffer);
console.log(` ✅ S3 Key : ${sourceKey}\n`);
// Étape 3 : Signer le PDF avec Odentas Sign
console.log('✍️ Étape 3 : Signature du PDF avec Odentas Sign...');
const signedS3Key = await signPdfWithLambda(sourceKey);
console.log(` ✅ PDF signé : ${signedS3Key}\n`);
// Étape 4 : Télécharger le PDF signé
console.log('📥 Étape 4 : Téléchargement du PDF signé...');
const signedPdfBuffer = await downloadFromS3(signedS3Key);
const signedPdfPath = path.join(OUTPUT_DIR, 'test-contrat-signe.pdf');
fs.writeFileSync(signedPdfPath, signedPdfBuffer);
console.log(` ✅ PDF signé sauvegardé : ${signedPdfPath}`);
console.log(` 📊 Taille : ${signedPdfBuffer.length} bytes\n`);
// Étape 5 : Extraire le hash de signature
console.log('🔍 Étape 5 : Extraction du hash de signature...');
const signatureHash = extractSignatureHash(signedPdfBuffer);
console.log(` ✅ Hash SHA-256 : ${signatureHash}\n`);
// Étape 6 : Générer URL présignée
console.log('🔗 Étape 6 : Génération URL présignée...');
const s3Url = await getPresignedUrl(signedS3Key);
console.log(` ✅ URL S3 : ${s3Url}\n`);
// Étape 7 : Créer l'entrée de vérification
console.log('🔐 Étape 7 : Création de la preuve de signature...');
const verificationData = {
document_name: 'Test Contrat CDDU - Jean Dupont',
pdf_url: s3Url,
signer_name: 'Jean Dupont',
signer_email: 'jean.dupont@example.com',
signature_hash: signatureHash,
signature_hex: signedPdfBuffer.toString('hex').substring(0, 200), // Premiers 200 chars
certificate_info: {
issuer: 'CN=Odentas Media SAS, O=Odentas Media, C=FR',
subject: 'CN=Jean Dupont, O=Odentas Media SAS',
valid_from: new Date().toISOString(),
valid_until: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString(),
serial_number: crypto.randomBytes(8).toString('hex'),
},
timestamp: {
tsa_url: 'freetsa.org/tsr',
timestamp: new Date().toISOString(),
hash: signatureHash,
},
organization_id: '550e8400-e29b-41d4-a716-446655440000', // UUID test
};
console.log(' 📝 Données de vérification préparées\n');
// Étape 8 : Générer le PDF de preuve (simulation)
console.log('📄 Étape 8 : Génération du PDF de preuve...');
console.log(' Le PDF de preuve sera généré par l\'API Next.js avec le QR code');
console.log(' 📍 Emplacement S3 : s3://odentas-sign/evidence/proofs/');
console.log(` 📝 Nom du fichier : TEST-${Date.now()}.pdf`);
console.log(' ✅ Structure prête\n');
// Étape 9 : Afficher les résultats
console.log('✅ TEST COMPLET TERMINÉ !\n');
console.log('📦 Fichiers générés :');
console.log(` • PDF signé : ${signedPdfPath}`);
console.log(` • URL S3 : ${s3Url}`);
console.log(`\n🔗 Prochaines étapes :`);
console.log(' 1. Appliquer la migration Supabase');
console.log(' 2. Lancer le serveur dev : npm run dev');
console.log(' 3. Visiter : http://localhost:3000/test-signature-verification');
console.log(' 4. Coller ces données dans le formulaire pour créer la preuve\n');
// Sauvegarder les données de test
const testDataPath = path.join(OUTPUT_DIR, 'verification-data.json');
fs.writeFileSync(testDataPath, JSON.stringify(verificationData, null, 2));
console.log(` 📄 Données sauvegardées : ${testDataPath}\n`);
}
async function signPdfWithLambda(sourceKey) {
const params = {
FunctionName: 'odentas-pades-sign',
InvocationType: 'RequestResponse',
Payload: JSON.stringify({
sourceKey: sourceKey,
requestRef: `TEST-${Date.now()}`,
}),
};
console.log(' ⏳ Appel de la Lambda odentas-pades-sign...');
try {
const result = await lambda.invoke(params).promise();
if (result.FunctionError) {
const errorPayload = JSON.parse(result.Payload);
throw new Error(`Lambda error: ${errorPayload.errorMessage}`);
}
const response = JSON.parse(result.Payload);
if (response.statusCode !== 200) {
const body = JSON.parse(response.body);
throw new Error(`Lambda returned status ${response.statusCode}: ${JSON.stringify(body)}`);
}
const body = JSON.parse(response.body);
if (!body.signed_pdf_s3_key) {
throw new Error(`Missing signed_pdf_s3_key in response. Body: ${JSON.stringify(body)}`);
}
console.log(` 📊 SHA-256: ${body.sha256}`);
return body.signed_pdf_s3_key;
} catch (error) {
console.error(' ❌ Erreur lors de la signature:', error.message);
throw error;
}
}
function extractSignatureHash(pdfBuffer) {
// Extraire le contenu signé via ByteRange
const pdfString = pdfBuffer.toString('latin1');
// Trouver le ByteRange
const byteRangeMatch = pdfString.match(/\/ByteRange\s*\[\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*\]/);
if (!byteRangeMatch) {
console.warn(' ⚠️ ByteRange non trouvé, calcul du hash sur tout le PDF');
return crypto.createHash('sha256').update(pdfBuffer).digest('hex');
}
const [_, o1, l1, o2, l2] = byteRangeMatch.map(Number);
console.log(` 📍 ByteRange trouvé : [${o1}, ${l1}, ${o2}, ${l2}]`);
// Extraire les deux segments
const segment1 = pdfBuffer.slice(o1, o1 + l1);
const segment2 = pdfBuffer.slice(o2, o2 + l2);
// Calculer le hash
const hash = crypto.createHash('sha256');
hash.update(segment1);
hash.update(segment2);
return hash.digest('hex');
}
async function uploadToS3ForSigning(buffer) {
const filename = `source/test-contrat-${Date.now()}.pdf`;
const params = {
Bucket: 'odentas-sign', // Bucket de la Lambda
Key: filename,
Body: buffer,
ContentType: 'application/pdf',
};
console.log(` ⏳ Upload vers s3://odentas-sign/${filename}...`);
try {
await s3.putObject(params).promise();
return filename;
} catch (error) {
console.error(' ❌ Erreur lors de l\'upload S3:', error.message);
throw error;
}
}
async function downloadFromS3(key) {
const params = {
Bucket: 'odentas-sign',
Key: key,
};
console.log(` ⏳ Téléchargement depuis s3://odentas-sign/${key}...`);
try {
const data = await s3.getObject(params).promise();
return data.Body;
} catch (error) {
console.error(' ❌ Erreur lors du téléchargement S3:', error.message);
throw error;
}
}
async function getPresignedUrl(key) {
const params = {
Bucket: 'odentas-sign',
Key: key,
Expires: 7 * 24 * 60 * 60, // 7 jours
};
return s3.getSignedUrl('getObject', params);
}
async function uploadToS3(buffer, prefix) {
const filename = `${prefix}/test-contrat-${Date.now()}.pdf`;
const params = {
Bucket: S3_BUCKET,
Key: filename,
Body: buffer,
ContentType: 'application/pdf',
ACL: 'private',
};
console.log(` ⏳ Upload vers s3://${S3_BUCKET}/${filename}...`);
try {
await s3.putObject(params).promise();
// Générer une URL présignée (valide 7 jours)
const presignedUrl = s3.getSignedUrl('getObject', {
Bucket: S3_BUCKET,
Key: filename,
Expires: 7 * 24 * 60 * 60, // 7 jours
});
return presignedUrl;
} catch (error) {
console.error(' ❌ Erreur lors de l\'upload S3:', error.message);
throw error;
}
}
// Exécuter
main().catch(error => {
console.error('\n❌ ERREUR FATALE:', error);
process.exit(1);
});