From 78dc2545c9af29dc5a62ca9631b96c30a6eaa130 Mon Sep 17 00:00:00 2001 From: Splunk Git Pusher Date: Tue, 24 Feb 2026 21:09:28 +0100 Subject: [PATCH] App_clean_with_obfuscation_js Pushed by: admin License: TA9O64YS7EPT (Professional) Timestamp: 2026-02-24T21:09:27.749365 --- .../appserver/static/license_validation.js | 443 +----- .../static/license_validation.js_old | 1291 +++++++++++++++++ .../license_validator.cpython-39.pyc | Bin 9715 -> 9715 bytes .../bin/credentials_manager.py | 0 apps/pusher_app_prem/bin/git_pusher.pid | 2 +- apps/pusher_app_prem/bin/git_pusher.py | 0 apps/pusher_app_prem/bin/license_endpoints.py | 0 apps/pusher_app_prem/bin/license_validator.py | 0 apps/pusher_app_prem/local/.usage_stats | 6 - .../local/certs/server.crt.bak | 29 - .../local/certs/server.key.bak | 52 - apps/pusher_app_prem/local/usage_stats.json | 1 - 12 files changed, 1354 insertions(+), 470 deletions(-) create mode 100644 apps/pusher_app_prem/appserver/static/license_validation.js_old mode change 100755 => 100644 apps/pusher_app_prem/bin/credentials_manager.py mode change 100755 => 100644 apps/pusher_app_prem/bin/git_pusher.py mode change 100755 => 100644 apps/pusher_app_prem/bin/license_endpoints.py mode change 100755 => 100644 apps/pusher_app_prem/bin/license_validator.py delete mode 100644 apps/pusher_app_prem/local/.usage_stats delete mode 100644 apps/pusher_app_prem/local/certs/server.crt.bak delete mode 100644 apps/pusher_app_prem/local/certs/server.key.bak delete mode 100644 apps/pusher_app_prem/local/usage_stats.json diff --git a/apps/pusher_app_prem/appserver/static/license_validation.js b/apps/pusher_app_prem/appserver/static/license_validation.js index 3be600dc..a3fe2f17 100644 --- a/apps/pusher_app_prem/appserver/static/license_validation.js +++ b/apps/pusher_app_prem/appserver/static/license_validation.js @@ -1,9 +1,66 @@ -// ============================================ -// GIT PUSHER - LICENSE VALIDATION (RSA) -// Version 2.1 - 100% Client-Side Validation -// ============================================ -// Configuration par défaut +// Git Pusher License Module v2.1 +// (c) 2026 - Proprietary Software + +function _d(a,k){return a.map(function(c){return String.fromCharCode(c^k)}).join('')} + +var _MiUBIgGA='jF6eQXaxas4w3DbTBbjHZiQ9pKBGtnCQ'; +var _uKlfWBIg='4haWQeriLYkoyXjRgm6X6UHBz45iRbnH'; +var _vbjxOVJJ='NFf8EIHOXxItNBiBn1nSiOVI39nWY4Wo'; +var _NifpYSQF='jetKGSDo6z5wRmmho5pl4ShoEDLXkVTg'; +var _CSSdPwWe='s2M1c6dDT80YSEJOglkKzFWva728EKKd'; +var _VtTpaXDJ='djV609Ci8cH0QhX1OOHpP2HXFkbTwAPW'; +var _RgWpxUDG='GzNPrJtG62D1lXxQqQMqERzrIh6t6qVg'; +var _ZwgnBtAr='6XCIgzQVyOlGYGhIBEaGDoK54C5iw8mb'; +var _EcTaZDmf='zwJaPfiZyz4GZjG4yTqMhme6xYdJmiZY'; +var _QVrhuHpJ='HiWpaGdVoKTfYjVonGJv8QTbS6s2mvQd'; + +function _wxAacnbb(a,b){ +var c=a^b;var d=c.toString(16); +return d.split('').reverse().join(''); +} + + +function _IYCAxJyI(a,b){ +var c=a^b;var d=c.toString(16); +return d.split('').reverse().join(''); +} + + +function _gquAYfDh(a,b){ +var c=a^b;var d=c.toString(16); +return d.split('').reverse().join(''); +} + + +function _XjtRsyrV(a,b){ +var c=a^b;var d=c.toString(16); +return d.split('').reverse().join(''); +} + + +function _nBWCFFkY(a,b){ +var c=a^b;var d=c.toString(16); +return d.split('').reverse().join(''); +} + +var _ZKiGcoJO=_d([43,43,43,43,43,68,67,65,79,72,38,86,83,68,74,79,69,38,77,67,95,43,43,43,43,43,12],6); +var _nytIOASn=_d([196,192,192,202,192,227,200,199,203,238,226,248,225,226,224,206,176,254,185,203,200,216,204,207,200,200,198,202,200,238,177,200,196,192,192,202,202,238,194,202,200,238,204,200,190,193,177,255,187,189,186,217,194,249,240,189,211,234,206,220,192],137); +var _yCXhseWK=_d([237,135,134,155,183,159,132,191,149,245,136,169,137,141,154,167,169,157,169,156,145,147,145,148,156,159,235,173,178,137,177,172,142,233,230,189,147,155,238,188,151,174,182,172,144,140,168,138,178,159,231,166,174,171,177,238,191,245,230,136,245],222); +var _MALrYRWj=_d([73,89,82,125,44,52,74,104,38,94,119,91,107,106,85,84,77,86,90,120,104,85,118,103,114,48,114,116,84,124,125,117,104,110,76,87,79,114,113,102,93,93,87,79,93,93,71,40,115,80,48,110,45,104,108,90,71,47,102,44,80],31); +var _FUmiltpU=_d([92,61,6,25,49,10,2,16,64,23,37,0,49,67,37,37,27,62,53,57,30,20,10,33,64,23,37,10,69,41,34,58,39,20,54,6,92,61,52,0,74,5,92,25,38,16,70,58,39,66,42,9,37,30,60,48,16,63,75,49,41],115); +var _HZJjkoYo=_d([130,154,156,183,153,182,192,177,168,149,163,195,223,165,200,155,152,129,199,135,181,136,197,191,153,156,152,168,179,199,153,200,135,198,133,130,148,201,147,196,180,154,154,151,197,200,195,167,131,183,132,180,187,155,192,145,170,198,136,134,158],240); +var _ZhewgHyM=_d([178,141,164,185,179,178,128,174,181,146,157,166,129,191,135,181,178,128,172,182,183,142,229,188,228,154,231,184,154,237,153,150,162,181,152,182,179,149,161,189,225,134,179,176,184,182,158,184,182,147,167,179,184,231,161,149,181,237,134,237,172],212); +var _eqxIyaxT=_d([249,200,136,209,210,247,203,155,245,225,239,245,210,136,232,244,150,234,194,149,194,250,236,245,215,198,209,246,197,145,203,217,140,203,246,200,237,201,238,155,241,204,207,213,151,140,144,243,242,251,147,206,228,214,149,197,194,151,197,212,204],163); +var _tzrcGgRV=_d([194,215,208,214,233,239,255,236,194,236,252,210,141,238,222,249,255,149,237,242,208,137,209,251,245,195,216,224,226,237,240,212,205,201,149,145,145,207,200,203,211,208,234,143,233,215,227,194,195,249,219,236,214,227,251,213,207,206,222,237,215],186); +var _vlzAAMhH=_d([94,21,80,112,86,119,124,107,76,19,16,85,86,75,81,22,83,82,23,103,28,96,73,75,28,71,71,114,94,74,101,76,64,76,108,71,114,87,19,105,119,72,29,117,70,93,83,21,66,87,77,21,21,21,19,115,77,67,113,99,79],36); +var _URxOonCc=_d([124,105,25,111,84,70,64,94,64,20,105,79,110,125,75,31,107,25,84,21,29,105,126,21,25,102,97,99,28,127,70,85,68,104,78,67,94,120,7,67,97,93,21,21,24,27,118,96,31,25,122,64,64,86,71,103,78,110,105,96,78],44); +var _YpSnAdLq=_d([39,11,33,32,40,23,34,11,2,61,6,124,6,37,126,26,13,34,12,59,28,0,21,53,0,3,39,54,7,11,9,34,8,41,122,14,13,126,3,24,45,55,44,40,61,53,0,2,44,27,3,32,14,7,43,58,46,11,46,35,21],79); +var _vdQnAYzc=_d([162,155,140,182,213,182,133,183,213,210,208,210,162,144,132,128,150,164,135,144,171,183,139,217,163,171,202,209,183,164,167,133,131,179,165,138,213,187,155,169,145,174,128,166,160,148,162,171,139,175,210,160,148,153,174,217,162,160,150,164,160,160,176,220,220],225); +var _WCjuyzqV=_d([219,252,252,252,252,252,148,159,149,241,129,132,147,157,152,146,241,154,148,136,252,252,252,252,252],209); +var _gkubzYFM=_ZKiGcoJO+_nytIOASn+_yCXhseWK+_MALrYRWj+_FUmiltpU+_HZJjkoYo+_ZhewgHyM+_eqxIyaxT+_tzrcGgRV+_vlzAAMhH+_URxOonCc+_YpSnAdLq+_vdQnAYzc+_WCjuyzqV; +var PUBLIC_KEY_PEM=_gkubzYFM; + const DEFAULT_APP_CONFIG = { api: { url: '', @@ -11,8 +68,6 @@ const DEFAULT_APP_CONFIG = { useProxy: true } }; - -// Charger la configuration function loadAppConfigForLicense() { try { const stored = localStorage.getItem('git_pusher_config'); @@ -24,75 +79,34 @@ function loadAppConfigForLicense() { } return DEFAULT_APP_CONFIG; } - -// Déterminer l'URL du serveur API function getLicenseServerUrl() { const config = loadAppConfigForLicense(); const hostname = window.location.hostname; const protocol = window.location.protocol; - - // Si une URL est configurée, l'utiliser if (config.api && config.api.url) { let url = config.api.url; - // Ajouter le port si pas de proxy if (!config.api.useProxy && config.api.port) { url = url.replace(/\/$/, '') + ':' + config.api.port; } return url; } - - // Auto-détection basée sur le hostname if (hostname === 'myprivspldev.jp-engineering.fr') { return protocol + '//myprivspldev-api.jp-engineering.fr'; } - if (hostname === 'myprivspldev-api.jp-engineering.fr') { return protocol + '//' + hostname; } - if (/^(\d{1,3}\.){3}\d{1,3}$/.test(hostname) || hostname === 'localhost') { return protocol + '//' + hostname + ':9999'; } - return protocol + '//' + hostname + ':9999'; } - -// Configuration const LICENSE_CONFIG = { storageKey: 'git_pusher_license', usageKey: 'git_pusher_usage', version: '2.1.0', serverUrl: getLicenseServerUrl() }; - -// ============================================ -// CLÉ PUBLIQUE RSA -// ============================================ -// Cette clé est générée par le vendeur avec license_generator_rsa.py -// Commande: python3 license_generator_rsa.py export-key - -const PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7H8v243PKpy4ZcGUI3YX -EiAZaK+VwWSDywCwBOMOJBA5slWorP78cME0bIphrNRvTlA9xpuo0a+8V+VFMb3+ -Uw9AhDtuJKRIEgwJixm/mkKcbjwqSHPmnyBBHPBBX7lO/q2wsEX0y3O/NujByqc3 -dVsB0VVhMFJmgyR3dVy6ZQITgEu/NGs9v/jUc5IT1YzVmOCcL8BZrjlGiF0AXeS3 -/U8khq7wEx5OilhXC7i8w6urd9c4Djjg583WsGtDKk0aZ6xvnfYpmgfTzaFIrUkS -afTxbcZ1h0N3lN9MBvaLbgAui5RgdlbJlbGsgl3uAa9R9xZk+rqTh8VBLVq+KW5I -a6aYOVterUf2hz/hUkNjM8Rolv4/3PQX0mGu6fa4fwoxmjlSUEVxVFh7TdCE/WHj -3kAOybZXWJnws/++urqijP5SmYxyCaVlYAoutdWmz1tTrSXOh74qrou2wv3C8Dmo -8ccVznAhdhHcVs7MSl9Qbyw1fsi1117WigUGkPE5Cxjlrl8EcBQg3G5x91ER95JM -O0SjyhDborT+oMq9947ZL35VllzkKbBELbhDnogXmDMrI3Ij1UBmCtSOZzOLhyHD -FmGf5AB1LWbxcgrzOMcTLoAHduaDalZCzmW4WdV4313CqeawEfqJVj8BJ+0VEFdb -RDk4ZzHpOaGAuCJjN3AuxO8CAwEAAQ== ------END PUBLIC KEY-----`; - -// ============================================ -// UTILITAIRES CRYPTO -// ============================================ - -/** - * Convertir une chaîne PEM en ArrayBuffer - */ function pemToArrayBuffer(pem) { const b64 = pem .replace(/-----BEGIN PUBLIC KEY-----/, '') @@ -105,10 +119,6 @@ function pemToArrayBuffer(pem) { } return bytes.buffer; } - -/** - * Importer la clé publique RSA - */ async function importPublicKey() { try { const keyData = pemToArrayBuffer(PUBLIC_KEY_PEM); @@ -128,36 +138,22 @@ async function importPublicKey() { return null; } } - -/** - * Vérifier la signature RSA PKCS#1 v1.5 - */ async function verifySignature(data, signatureB64, publicKey) { try { const signature = Uint8Array.from(atob(signatureB64), c => c.charCodeAt(0)); const dataBuffer = new TextEncoder().encode(data); - const isValid = await crypto.subtle.verify( 'RSASSA-PKCS1-v1_5', publicKey, signature, dataBuffer ); - return isValid; } catch (error) { console.error('Erreur vérification signature:', error); return false; } } - -// ============================================ -// FONCTIONS UTILITAIRES -// ============================================ - -/** - * Décoder Base64 - */ function base64Decode(str) { try { return decodeURIComponent(atob(str).split('').map(function(c) { @@ -167,20 +163,11 @@ function base64Decode(str) { return atob(str); } } - -/** - * Obtenir le hostname actuel (depuis l'URL) - */ function getCurrentHostname() { return window.location.hostname.toLowerCase(); } - -/** - * Obtenir le vrai hostname Splunk via l'API REST - */ async function getSplunkHostname() { try { - // Méthode 1: Via server/info const response = await fetch('/en-US/splunkd/__raw/services/server/info?output_mode=json', { credentials: 'include' }); @@ -195,9 +182,7 @@ async function getSplunkHostname() { } catch (e) { console.log('Méthode server/info échouée:', e); } - try { - // Méthode 2: Via server/settings const response2 = await fetch('/en-US/splunkd/__raw/services/server/settings?output_mode=json', { credentials: 'include' }); @@ -212,34 +197,19 @@ async function getSplunkHostname() { } catch (e) { console.log('Méthode server/settings échouée:', e); } - - // Fallback: hostname de l'URL (pas idéal) console.warn('Impossible de récupérer le hostname Splunk, utilisation URL:', getCurrentHostname()); return getCurrentHostname(); } - -/** - * Calculer les jours restants - */ function daysRemaining(expiryDate) { const expiry = new Date(expiryDate); const now = new Date(); const diff = expiry - now; return Math.ceil(diff / (1000 * 60 * 60 * 24)); } - -// ============================================ -// PARSING DE LICENCE -// ============================================ - -/** - * Parser le contenu d'un fichier .lic - */ function parseLicenseFile(content) { try { const lines = content.trim().split('\n'); let payloadB64 = null; - for (const line of lines) { const trimmed = line.trim(); if (trimmed && !trimmed.startsWith('#')) { @@ -247,23 +217,16 @@ function parseLicenseFile(content) { break; } } - if (!payloadB64) { return { error: 'Payload non trouvé dans le fichier' }; } - - // Décoder le payload const payloadJson = base64Decode(payloadB64); const payload = JSON.parse(payloadJson); - if (!payload.license || !payload.signature) { return { error: 'Format de licence invalide' }; } - - // Décoder les données de licence const licenseJson = base64Decode(payload.license); const licenseData = JSON.parse(licenseJson); - return { success: true, licenseJson: licenseJson, @@ -276,17 +239,8 @@ function parseLicenseFile(content) { return { error: 'Erreur de lecture du fichier de licence' }; } } - -// ============================================ -// VALIDATION DE LICENCE -// ============================================ - -/** - * Valider une licence complète (signature RSA + hostname + expiration) - */ async function validateLicense(licenseContent = null) { try { - // Si pas de contenu fourni, charger depuis le localStorage let parsed; if (licenseContent) { parsed = parseLicenseFile(licenseContent); @@ -301,7 +255,6 @@ async function validateLicense(licenseContent = null) { } parsed = JSON.parse(stored); } - if (parsed.error) { return { valid: false, @@ -309,20 +262,14 @@ async function validateLicense(licenseContent = null) { error_code: 'PARSE_ERROR' }; } - const { licenseJson, licenseData, signatureB64 } = parsed; - - // DEBUG: Afficher les données console.log('=== DEBUG VALIDATION LICENCE ==='); console.log('License JSON:', licenseJson); console.log('License JSON length:', licenseJson.length); console.log('Signature B64:', signatureB64.substring(0, 50) + '...'); console.log('Signature B64 length:', signatureB64.length); - - // 1. Vérifier la signature RSA console.log('Vérification de la signature RSA...'); const publicKey = await importPublicKey(); - if (!publicKey) { console.error('Échec import clé publique'); return { @@ -331,13 +278,9 @@ async function validateLicense(licenseContent = null) { error_code: 'KEY_ERROR' }; } - console.log('Clé publique importée avec succès'); - const signatureValid = await verifySignature(licenseJson, signatureB64, publicKey); - console.log('Résultat vérification signature:', signatureValid); - if (!signatureValid) { return { valid: false, @@ -345,18 +288,11 @@ async function validateLicense(licenseContent = null) { error_code: 'INVALID_SIGNATURE' }; } - console.log('✓ Signature RSA valide'); - - // 2. Vérifier le hostname const expectedHostname = (licenseData.hostname || '').toLowerCase(); const currentHostname = await getSplunkHostname(); - console.log(`Hostname attendu: "${expectedHostname}", actuel: "${currentHostname}"`); - - // Permettre une correspondance partielle ou exacte if (expectedHostname && expectedHostname !== currentHostname) { - // Vérifier si c'est une correspondance partielle (le hostname peut être un FQDN) if (!currentHostname.includes(expectedHostname) && !expectedHostname.includes(currentHostname)) { return { valid: false, @@ -370,12 +306,9 @@ async function validateLicense(licenseContent = null) { } else { console.log('✓ Hostname valide (exact)'); } - - // 3. Vérifier la date d'expiration const expiryDate = licenseData.expires; if (expiryDate) { const days = daysRemaining(expiryDate); - if (days < 0) { return { valid: false, @@ -384,11 +317,8 @@ async function validateLicense(licenseContent = null) { expires: expiryDate }; } - console.log(`✓ Licence valide (${days} jours restants)`); } - - // Licence valide ! return { valid: true, license_id: licenseData.license_id, @@ -402,7 +332,6 @@ async function validateLicense(licenseContent = null) { limits: licenseData.limits || {}, features: licenseData.features || [] }; - } catch (error) { console.error('Erreur validation licence:', error); return { @@ -412,38 +341,21 @@ async function validateLicense(licenseContent = null) { }; } } - -/** - * Vérifier si une fonctionnalité est disponible - */ async function hasFeature(featureName) { const validation = await validateLicense(); if (!validation.valid) return false; return validation.features.includes(featureName); } - -// ============================================ -// GESTION DU STOCKAGE -// ============================================ - -/** - * Sauvegarder une licence validée (localStorage + serveur) - */ async function saveLicense(licenseContent) { try { - // Parser le fichier const parsed = parseLicenseFile(licenseContent); - if (parsed.error) { return { success: false, error: parsed.error }; } - - // Valider la licence avant de sauvegarder const validation = await validateLicense(licenseContent); - if (!validation.valid) { return { success: false, @@ -451,12 +363,8 @@ async function saveLicense(licenseContent) { error_code: validation.error_code }; } - - // 1. Sauvegarder dans localStorage localStorage.setItem(LICENSE_CONFIG.storageKey, JSON.stringify(parsed)); console.log('✓ Licence sauvegardée dans localStorage'); - - // 2. Sauvegarder sur le serveur (pour persistance après vidage cache) try { const response = await fetch(`${LICENSE_CONFIG.serverUrl}/license/save`, { method: 'POST', @@ -467,25 +375,19 @@ async function saveLicense(licenseContent) { license_content: licenseContent }) }); - const serverResult = await response.json(); - if (serverResult.success) { console.log('✓ Licence sauvegardée sur le serveur'); } else { console.warn('⚠ Échec sauvegarde serveur:', serverResult.error); - // On continue quand même car localStorage fonctionne } } catch (serverError) { console.warn('⚠ Impossible de sauvegarder sur le serveur:', serverError); - // On continue quand même car localStorage fonctionne } - return { success: true, license: validation }; - } catch (error) { console.error('Erreur sauvegarde licence:', error); return { @@ -494,10 +396,6 @@ async function saveLicense(licenseContent) { }; } } - -/** - * Charger la licence depuis le serveur (si localStorage vide) - */ async function loadLicenseFromServer() { try { const response = await fetch(`${LICENSE_CONFIG.serverUrl}/license/file`, { @@ -506,25 +404,16 @@ async function loadLicenseFromServer() { 'Content-Type': 'application/json' } }); - const result = await response.json(); - if (result.success && result.content) { console.log('Licence trouvée sur le serveur, validation en cours...'); - - // Parser et valider la licence const parsed = parseLicenseFile(result.content); - if (parsed.error) { console.warn('Erreur parsing licence serveur:', parsed.error); return false; } - - // Valider la signature const validation = await validateLicense(result.content); - if (validation.valid) { - // Sauvegarder dans localStorage pour les prochaines fois localStorage.setItem(LICENSE_CONFIG.storageKey, JSON.stringify(parsed)); console.log('✓ Licence chargée depuis le serveur et stockée localement'); return true; @@ -541,16 +430,9 @@ async function loadLicenseFromServer() { return false; } } - -/** - * Supprimer la licence (localStorage + serveur) - */ async function removeLicense() { - // Supprimer du localStorage localStorage.removeItem(LICENSE_CONFIG.storageKey); localStorage.removeItem(LICENSE_CONFIG.usageKey); - - // Supprimer du serveur try { await fetch(`${LICENSE_CONFIG.serverUrl}/license/delete`, { method: 'POST' @@ -559,32 +441,18 @@ async function removeLicense() { } catch (e) { console.warn('Impossible de supprimer la licence du serveur:', e); } - console.log('Licence supprimée'); } - -/** - * Récupérer les infos de licence (sans revalider la signature) - */ function getLicenseInfo() { try { const stored = localStorage.getItem(LICENSE_CONFIG.storageKey); if (!stored) return null; - const parsed = JSON.parse(stored); return parsed.licenseData; } catch { return null; } } - -// ============================================ -// GESTION DES LIMITES D'UTILISATION -// ============================================ - -/** - * Obtenir les stats d'utilisation - */ function getUsageStats() { try { const stored = localStorage.getItem(LICENSE_CONFIG.usageKey); @@ -592,40 +460,26 @@ function getUsageStats() { return JSON.parse(stored); } } catch {} - return { total_pushes: 0, pushes_today: 0, last_push_date: null }; } - -/** - * Incrémenter le compteur d'utilisation - */ function incrementUsage() { const stats = getUsageStats(); const today = new Date().toISOString().split('T')[0]; - - // Reset si nouveau jour if (stats.last_push_date !== today) { stats.pushes_today = 0; stats.last_push_date = today; } - stats.total_pushes = (stats.total_pushes || 0) + 1; stats.pushes_today = (stats.pushes_today || 0) + 1; - localStorage.setItem(LICENSE_CONFIG.usageKey, JSON.stringify(stats)); return stats; } - -/** - * Vérifier les limites avant un push - */ async function checkLimits() { const validation = await validateLicense(); - if (!validation.valid) { return { allowed: false, @@ -633,20 +487,15 @@ async function checkLimits() { error_code: validation.error_code }; } - const limits = validation.limits || {}; const maxPushes = limits.max_pushes_per_day || -1; - if (maxPushes > 0) { const stats = getUsageStats(); const today = new Date().toISOString().split('T')[0]; - - // Reset si nouveau jour let pushesToday = stats.pushes_today || 0; if (stats.last_push_date !== today) { pushesToday = 0; } - if (pushesToday >= maxPushes) { return { allowed: false, @@ -655,34 +504,21 @@ async function checkLimits() { }; } } - return { allowed: true, license_type: validation.type_name, remaining_today: maxPushes > 0 ? maxPushes - getUsageStats().pushes_today : -1 }; } - -// ============================================ -// INTERFACE UTILISATEUR -// ============================================ - -/** - * Afficher le badge de licence - */ async function updateLicenseBadge() { const container = document.getElementById('license-badge-container'); if (!container) return; - const validation = await validateLicense(); - let badgeHtml = ''; - if (validation.valid) { const daysLeft = validation.days_remaining; let badgeClass = 'license-badge-valid'; let icon = '✓'; - if (daysLeft <= 7) { badgeClass = 'license-badge-expiring'; icon = '⚠️'; @@ -690,7 +526,6 @@ async function updateLicenseBadge() { badgeClass = 'license-badge-warning'; icon = '⏳'; } - badgeHtml = `
${icon} @@ -706,37 +541,26 @@ async function updateLicenseBadge() {
`; } - container.innerHTML = badgeHtml; } - -/** - * Afficher le modal de licence - */ function showLicenseModal(message = null, errorCode = null) { - // Supprimer l'ancien modal s'il existe const existingModal = document.getElementById('license-modal'); if (existingModal) existingModal.remove(); - const modal = document.createElement('div'); modal.id = 'license-modal'; modal.className = 'license-modal-overlay'; - let errorMessage = ''; if (message) { errorMessage = `
${message}
`; } - modal.innerHTML = `

🔐 Activation de Licence

-
${errorMessage} -
📄
@@ -745,84 +569,56 @@ function showLicenseModal(message = null, errorCode = null) {
-
Hostname du serveur: Chargement...
Communiquez ce hostname pour obtenir votre licence
-
-
`; - document.body.appendChild(modal); - - // Afficher le hostname getSplunkHostname().then(hostname => { const hostnameDisplay = document.getElementById('current-hostname-display'); if (hostnameDisplay) { hostnameDisplay.textContent = hostname; } }); - - // Configurer le drag & drop setupLicenseUpload(); } - -/** - * Configurer l'upload de licence - */ function setupLicenseUpload() { const dropZone = document.getElementById('license-upload-zone'); const fileInput = document.getElementById('license-file-input'); - if (!dropZone || !fileInput) return; - - // Clic pour sélectionner dropZone.addEventListener('click', () => fileInput.click()); - - // Drag & drop dropZone.addEventListener('dragover', (e) => { e.preventDefault(); dropZone.classList.add('dragover'); }); - dropZone.addEventListener('dragleave', () => { dropZone.classList.remove('dragover'); }); - dropZone.addEventListener('drop', (e) => { e.preventDefault(); dropZone.classList.remove('dragover'); - const files = e.dataTransfer.files; if (files.length > 0) { handleLicenseFile(files[0]); } }); - - // Sélection de fichier fileInput.addEventListener('change', (e) => { if (e.target.files.length > 0) { handleLicenseFile(e.target.files[0]); } }); } - -/** - * Traiter le fichier de licence uploadé - */ async function handleLicenseFile(file) { const resultDiv = document.getElementById('license-validation-result'); if (!resultDiv) return; - - // Vérifier l'extension if (!file.name.endsWith('.lic')) { resultDiv.innerHTML = `
@@ -831,20 +627,14 @@ async function handleLicenseFile(file) { `; return; } - resultDiv.innerHTML = `
⏳ Validation en cours...
`; - try { - // Lire le fichier const content = await file.text(); - - // Sauvegarder et valider const result = await saveLicense(content); - if (result.success) { const license = result.license; resultDiv.innerHTML = ` @@ -858,15 +648,10 @@ async function handleLicenseFile(file) {
`; - - // Mettre à jour le badge updateLicenseBadge(); - - // Fermer le modal après 3 secondes setTimeout(() => { closeLicenseModal(); }, 3000); - } else { resultDiv.innerHTML = `
@@ -874,7 +659,6 @@ async function handleLicenseFile(file) {
`; } - } catch (error) { resultDiv.innerHTML = `
@@ -883,48 +667,32 @@ async function handleLicenseFile(file) { `; } } - -/** - * Fermer le modal de licence - */ function closeLicenseModal() { const modal = document.getElementById('license-modal'); if (modal) modal.remove(); } - -/** - * Afficher les détails de la licence - */ async function showLicenseDetails() { const validation = await validateLicense(); - if (!validation.valid) { showLicenseModal(validation.error, validation.error_code); return; } - - // Supprimer l'ancien modal s'il existe const existingModal = document.getElementById('license-details-modal'); if (existingModal) existingModal.remove(); - const modal = document.createElement('div'); modal.id = 'license-details-modal'; modal.className = 'license-modal-overlay'; - const features = validation.features.map(f => `${f}`).join(' '); const limits = validation.limits; const maxApps = limits.max_apps === -1 ? '∞' : limits.max_apps; const maxPushes = limits.max_pushes_per_day === -1 ? '∞' : limits.max_pushes_per_day; - const usage = getUsageStats(); - modal.innerHTML = `

📋 Détails de la Licence

-
@@ -938,67 +706,42 @@ async function showLicenseDetails() {
ID${validation.license_id}
Apps max${maxApps}
Pushes/jour${maxPushes}
-
Fonctionnalités:
${features}
-
Utilisation:
Total pushes: ${usage.total_pushes} | Aujourd'hui: ${usage.pushes_today}
-
`; - document.body.appendChild(modal); } - -/** - * Confirmer la suppression de licence - */ function confirmRemoveLicense() { if (confirm('Êtes-vous sûr de vouloir supprimer cette licence ?')) { removeLicense(); - - // Fermer le modal des détails const detailsModal = document.getElementById('license-details-modal'); if (detailsModal) detailsModal.remove(); - - // Mettre à jour le badge updateLicenseBadge(); - alert('Licence supprimée.'); } } - -/** - * Vérifier la licence avant un push (appelé par git_pusher.js) - */ async function checkLicenseBeforePush() { const result = await checkLimits(); - if (!result.allowed) { showLicenseModal(result.error, result.error_code); return false; } - return true; } - -// ============================================ -// STYLES CSS -// ============================================ - const licenseStyles = ` `; - -// ============================================ -// INITIALISATION -// ============================================ - -/** - * Initialiser le système de licence - */ async function initializeLicense() { console.log('Git Pusher License System v' + LICENSE_CONFIG.version + ' (RSA)'); - - // Injecter les styles if (!document.getElementById('license-styles')) { const styleEl = document.createElement('div'); styleEl.id = 'license-styles'; styleEl.innerHTML = licenseStyles; document.head.appendChild(styleEl); } - - // Vérifier si une licence existe dans localStorage const stored = localStorage.getItem(LICENSE_CONFIG.storageKey); - if (!stored) { console.log('Aucune licence en cache, tentative de chargement depuis le serveur...'); - - // Essayer de charger depuis le serveur const loadedFromServer = await loadLicenseFromServer(); - if (loadedFromServer) { console.log('✓ Licence restaurée depuis le serveur'); } else { @@ -1259,12 +946,8 @@ async function initializeLicense() { } else { console.log('Licence trouvée dans le cache local'); } - - // Mettre à jour le badge updateLicenseBadge(); } - -// Exposer les fonctions globalement window.initializeLicense = initializeLicense; window.validateLicense = validateLicense; window.saveLicense = saveLicense; @@ -1280,8 +963,6 @@ window.hasFeature = hasFeature; window.incrementUsage = incrementUsage; window.getUsageStats = getUsageStats; window.getLicenseInfo = getLicenseInfo; - -// Auto-initialiser après le chargement if (document.readyState === 'complete') { setTimeout(initializeLicense, 100); } else { diff --git a/apps/pusher_app_prem/appserver/static/license_validation.js_old b/apps/pusher_app_prem/appserver/static/license_validation.js_old new file mode 100644 index 00000000..3be600dc --- /dev/null +++ b/apps/pusher_app_prem/appserver/static/license_validation.js_old @@ -0,0 +1,1291 @@ +// ============================================ +// GIT PUSHER - LICENSE VALIDATION (RSA) +// Version 2.1 - 100% Client-Side Validation +// ============================================ + +// Configuration par défaut +const DEFAULT_APP_CONFIG = { + api: { + url: '', + port: 9999, + useProxy: true + } +}; + +// Charger la configuration +function loadAppConfigForLicense() { + try { + const stored = localStorage.getItem('git_pusher_config'); + if (stored) { + return JSON.parse(stored); + } + } catch (e) { + console.warn('Erreur chargement config localStorage:', e); + } + return DEFAULT_APP_CONFIG; +} + +// Déterminer l'URL du serveur API +function getLicenseServerUrl() { + const config = loadAppConfigForLicense(); + const hostname = window.location.hostname; + const protocol = window.location.protocol; + + // Si une URL est configurée, l'utiliser + if (config.api && config.api.url) { + let url = config.api.url; + // Ajouter le port si pas de proxy + if (!config.api.useProxy && config.api.port) { + url = url.replace(/\/$/, '') + ':' + config.api.port; + } + return url; + } + + // Auto-détection basée sur le hostname + if (hostname === 'myprivspldev.jp-engineering.fr') { + return protocol + '//myprivspldev-api.jp-engineering.fr'; + } + + if (hostname === 'myprivspldev-api.jp-engineering.fr') { + return protocol + '//' + hostname; + } + + if (/^(\d{1,3}\.){3}\d{1,3}$/.test(hostname) || hostname === 'localhost') { + return protocol + '//' + hostname + ':9999'; + } + + return protocol + '//' + hostname + ':9999'; +} + +// Configuration +const LICENSE_CONFIG = { + storageKey: 'git_pusher_license', + usageKey: 'git_pusher_usage', + version: '2.1.0', + serverUrl: getLicenseServerUrl() +}; + +// ============================================ +// CLÉ PUBLIQUE RSA +// ============================================ +// Cette clé est générée par le vendeur avec license_generator_rsa.py +// Commande: python3 license_generator_rsa.py export-key + +const PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7H8v243PKpy4ZcGUI3YX +EiAZaK+VwWSDywCwBOMOJBA5slWorP78cME0bIphrNRvTlA9xpuo0a+8V+VFMb3+ +Uw9AhDtuJKRIEgwJixm/mkKcbjwqSHPmnyBBHPBBX7lO/q2wsEX0y3O/NujByqc3 +dVsB0VVhMFJmgyR3dVy6ZQITgEu/NGs9v/jUc5IT1YzVmOCcL8BZrjlGiF0AXeS3 +/U8khq7wEx5OilhXC7i8w6urd9c4Djjg583WsGtDKk0aZ6xvnfYpmgfTzaFIrUkS +afTxbcZ1h0N3lN9MBvaLbgAui5RgdlbJlbGsgl3uAa9R9xZk+rqTh8VBLVq+KW5I +a6aYOVterUf2hz/hUkNjM8Rolv4/3PQX0mGu6fa4fwoxmjlSUEVxVFh7TdCE/WHj +3kAOybZXWJnws/++urqijP5SmYxyCaVlYAoutdWmz1tTrSXOh74qrou2wv3C8Dmo +8ccVznAhdhHcVs7MSl9Qbyw1fsi1117WigUGkPE5Cxjlrl8EcBQg3G5x91ER95JM +O0SjyhDborT+oMq9947ZL35VllzkKbBELbhDnogXmDMrI3Ij1UBmCtSOZzOLhyHD +FmGf5AB1LWbxcgrzOMcTLoAHduaDalZCzmW4WdV4313CqeawEfqJVj8BJ+0VEFdb +RDk4ZzHpOaGAuCJjN3AuxO8CAwEAAQ== +-----END PUBLIC KEY-----`; + +// ============================================ +// UTILITAIRES CRYPTO +// ============================================ + +/** + * Convertir une chaîne PEM en ArrayBuffer + */ +function pemToArrayBuffer(pem) { + const b64 = pem + .replace(/-----BEGIN PUBLIC KEY-----/, '') + .replace(/-----END PUBLIC KEY-----/, '') + .replace(/\s/g, ''); + const binary = atob(b64); + const bytes = new Uint8Array(binary.length); + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return bytes.buffer; +} + +/** + * Importer la clé publique RSA + */ +async function importPublicKey() { + try { + const keyData = pemToArrayBuffer(PUBLIC_KEY_PEM); + const key = await crypto.subtle.importKey( + 'spki', + keyData, + { + name: 'RSASSA-PKCS1-v1_5', + hash: 'SHA-256' + }, + false, + ['verify'] + ); + return key; + } catch (error) { + console.error('Erreur import clé publique:', error); + return null; + } +} + +/** + * Vérifier la signature RSA PKCS#1 v1.5 + */ +async function verifySignature(data, signatureB64, publicKey) { + try { + const signature = Uint8Array.from(atob(signatureB64), c => c.charCodeAt(0)); + const dataBuffer = new TextEncoder().encode(data); + + const isValid = await crypto.subtle.verify( + 'RSASSA-PKCS1-v1_5', + publicKey, + signature, + dataBuffer + ); + + return isValid; + } catch (error) { + console.error('Erreur vérification signature:', error); + return false; + } +} + +// ============================================ +// FONCTIONS UTILITAIRES +// ============================================ + +/** + * Décoder Base64 + */ +function base64Decode(str) { + try { + return decodeURIComponent(atob(str).split('').map(function(c) { + return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); + }).join('')); + } catch (e) { + return atob(str); + } +} + +/** + * Obtenir le hostname actuel (depuis l'URL) + */ +function getCurrentHostname() { + return window.location.hostname.toLowerCase(); +} + +/** + * Obtenir le vrai hostname Splunk via l'API REST + */ +async function getSplunkHostname() { + try { + // Méthode 1: Via server/info + const response = await fetch('/en-US/splunkd/__raw/services/server/info?output_mode=json', { + credentials: 'include' + }); + if (response.ok) { + const data = await response.json(); + const serverName = data.entry?.[0]?.content?.serverName; + if (serverName) { + console.log('Hostname Splunk (server/info):', serverName); + return serverName.toLowerCase(); + } + } + } catch (e) { + console.log('Méthode server/info échouée:', e); + } + + try { + // Méthode 2: Via server/settings + const response2 = await fetch('/en-US/splunkd/__raw/services/server/settings?output_mode=json', { + credentials: 'include' + }); + if (response2.ok) { + const data2 = await response2.json(); + const serverName = data2.entry?.[0]?.content?.serverName; + if (serverName) { + console.log('Hostname Splunk (server/settings):', serverName); + return serverName.toLowerCase(); + } + } + } catch (e) { + console.log('Méthode server/settings échouée:', e); + } + + // Fallback: hostname de l'URL (pas idéal) + console.warn('Impossible de récupérer le hostname Splunk, utilisation URL:', getCurrentHostname()); + return getCurrentHostname(); +} + +/** + * Calculer les jours restants + */ +function daysRemaining(expiryDate) { + const expiry = new Date(expiryDate); + const now = new Date(); + const diff = expiry - now; + return Math.ceil(diff / (1000 * 60 * 60 * 24)); +} + +// ============================================ +// PARSING DE LICENCE +// ============================================ + +/** + * Parser le contenu d'un fichier .lic + */ +function parseLicenseFile(content) { + try { + const lines = content.trim().split('\n'); + let payloadB64 = null; + + for (const line of lines) { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith('#')) { + payloadB64 = trimmed; + break; + } + } + + if (!payloadB64) { + return { error: 'Payload non trouvé dans le fichier' }; + } + + // Décoder le payload + const payloadJson = base64Decode(payloadB64); + const payload = JSON.parse(payloadJson); + + if (!payload.license || !payload.signature) { + return { error: 'Format de licence invalide' }; + } + + // Décoder les données de licence + const licenseJson = base64Decode(payload.license); + const licenseData = JSON.parse(licenseJson); + + return { + success: true, + licenseJson: licenseJson, + licenseData: licenseData, + signatureB64: payload.signature, + rawPayload: payloadB64 + }; + } catch (error) { + console.error('Erreur parsing licence:', error); + return { error: 'Erreur de lecture du fichier de licence' }; + } +} + +// ============================================ +// VALIDATION DE LICENCE +// ============================================ + +/** + * Valider une licence complète (signature RSA + hostname + expiration) + */ +async function validateLicense(licenseContent = null) { + try { + // Si pas de contenu fourni, charger depuis le localStorage + let parsed; + if (licenseContent) { + parsed = parseLicenseFile(licenseContent); + } else { + const stored = localStorage.getItem(LICENSE_CONFIG.storageKey); + if (!stored) { + return { + valid: false, + error: 'Aucune licence installée', + error_code: 'NO_LICENSE' + }; + } + parsed = JSON.parse(stored); + } + + if (parsed.error) { + return { + valid: false, + error: parsed.error, + error_code: 'PARSE_ERROR' + }; + } + + const { licenseJson, licenseData, signatureB64 } = parsed; + + // DEBUG: Afficher les données + console.log('=== DEBUG VALIDATION LICENCE ==='); + console.log('License JSON:', licenseJson); + console.log('License JSON length:', licenseJson.length); + console.log('Signature B64:', signatureB64.substring(0, 50) + '...'); + console.log('Signature B64 length:', signatureB64.length); + + // 1. Vérifier la signature RSA + console.log('Vérification de la signature RSA...'); + const publicKey = await importPublicKey(); + + if (!publicKey) { + console.error('Échec import clé publique'); + return { + valid: false, + error: 'Impossible de charger la clé publique', + error_code: 'KEY_ERROR' + }; + } + + console.log('Clé publique importée avec succès'); + + const signatureValid = await verifySignature(licenseJson, signatureB64, publicKey); + + console.log('Résultat vérification signature:', signatureValid); + + if (!signatureValid) { + return { + valid: false, + error: 'Signature de licence invalide', + error_code: 'INVALID_SIGNATURE' + }; + } + + console.log('✓ Signature RSA valide'); + + // 2. Vérifier le hostname + const expectedHostname = (licenseData.hostname || '').toLowerCase(); + const currentHostname = await getSplunkHostname(); + + console.log(`Hostname attendu: "${expectedHostname}", actuel: "${currentHostname}"`); + + // Permettre une correspondance partielle ou exacte + if (expectedHostname && expectedHostname !== currentHostname) { + // Vérifier si c'est une correspondance partielle (le hostname peut être un FQDN) + if (!currentHostname.includes(expectedHostname) && !expectedHostname.includes(currentHostname)) { + return { + valid: false, + error: `Licence non valide pour ce serveur. Attendu: ${expectedHostname}, Actuel: ${currentHostname}`, + error_code: 'HOSTNAME_MISMATCH', + expected_hostname: expectedHostname, + current_hostname: currentHostname + }; + } + console.log('✓ Hostname valide (correspondance partielle)'); + } else { + console.log('✓ Hostname valide (exact)'); + } + + // 3. Vérifier la date d'expiration + const expiryDate = licenseData.expires; + if (expiryDate) { + const days = daysRemaining(expiryDate); + + if (days < 0) { + return { + valid: false, + error: `Licence expirée le ${expiryDate}`, + error_code: 'LICENSE_EXPIRED', + expires: expiryDate + }; + } + + console.log(`✓ Licence valide (${days} jours restants)`); + } + + // Licence valide ! + return { + valid: true, + license_id: licenseData.license_id, + type: licenseData.type, + type_name: licenseData.type_name, + customer: licenseData.customer, + hostname: expectedHostname, + issued: licenseData.issued, + expires: expiryDate, + days_remaining: daysRemaining(expiryDate), + limits: licenseData.limits || {}, + features: licenseData.features || [] + }; + + } catch (error) { + console.error('Erreur validation licence:', error); + return { + valid: false, + error: error.message, + error_code: 'VALIDATION_ERROR' + }; + } +} + +/** + * Vérifier si une fonctionnalité est disponible + */ +async function hasFeature(featureName) { + const validation = await validateLicense(); + if (!validation.valid) return false; + return validation.features.includes(featureName); +} + +// ============================================ +// GESTION DU STOCKAGE +// ============================================ + +/** + * Sauvegarder une licence validée (localStorage + serveur) + */ +async function saveLicense(licenseContent) { + try { + // Parser le fichier + const parsed = parseLicenseFile(licenseContent); + + if (parsed.error) { + return { + success: false, + error: parsed.error + }; + } + + // Valider la licence avant de sauvegarder + const validation = await validateLicense(licenseContent); + + if (!validation.valid) { + return { + success: false, + error: validation.error, + error_code: validation.error_code + }; + } + + // 1. Sauvegarder dans localStorage + localStorage.setItem(LICENSE_CONFIG.storageKey, JSON.stringify(parsed)); + console.log('✓ Licence sauvegardée dans localStorage'); + + // 2. Sauvegarder sur le serveur (pour persistance après vidage cache) + try { + const response = await fetch(`${LICENSE_CONFIG.serverUrl}/license/save`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + license_content: licenseContent + }) + }); + + const serverResult = await response.json(); + + if (serverResult.success) { + console.log('✓ Licence sauvegardée sur le serveur'); + } else { + console.warn('⚠ Échec sauvegarde serveur:', serverResult.error); + // On continue quand même car localStorage fonctionne + } + } catch (serverError) { + console.warn('⚠ Impossible de sauvegarder sur le serveur:', serverError); + // On continue quand même car localStorage fonctionne + } + + return { + success: true, + license: validation + }; + + } catch (error) { + console.error('Erreur sauvegarde licence:', error); + return { + success: false, + error: error.message + }; + } +} + +/** + * Charger la licence depuis le serveur (si localStorage vide) + */ +async function loadLicenseFromServer() { + try { + const response = await fetch(`${LICENSE_CONFIG.serverUrl}/license/file`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }); + + const result = await response.json(); + + if (result.success && result.content) { + console.log('Licence trouvée sur le serveur, validation en cours...'); + + // Parser et valider la licence + const parsed = parseLicenseFile(result.content); + + if (parsed.error) { + console.warn('Erreur parsing licence serveur:', parsed.error); + return false; + } + + // Valider la signature + const validation = await validateLicense(result.content); + + if (validation.valid) { + // Sauvegarder dans localStorage pour les prochaines fois + localStorage.setItem(LICENSE_CONFIG.storageKey, JSON.stringify(parsed)); + console.log('✓ Licence chargée depuis le serveur et stockée localement'); + return true; + } else { + console.warn('Licence serveur invalide:', validation.error); + return false; + } + } else { + console.log('Aucune licence sur le serveur'); + return false; + } + } catch (error) { + console.log('Impossible de charger la licence depuis le serveur:', error.message); + return false; + } +} + +/** + * Supprimer la licence (localStorage + serveur) + */ +async function removeLicense() { + // Supprimer du localStorage + localStorage.removeItem(LICENSE_CONFIG.storageKey); + localStorage.removeItem(LICENSE_CONFIG.usageKey); + + // Supprimer du serveur + try { + await fetch(`${LICENSE_CONFIG.serverUrl}/license/delete`, { + method: 'POST' + }); + console.log('Licence supprimée du serveur'); + } catch (e) { + console.warn('Impossible de supprimer la licence du serveur:', e); + } + + console.log('Licence supprimée'); +} + +/** + * Récupérer les infos de licence (sans revalider la signature) + */ +function getLicenseInfo() { + try { + const stored = localStorage.getItem(LICENSE_CONFIG.storageKey); + if (!stored) return null; + + const parsed = JSON.parse(stored); + return parsed.licenseData; + } catch { + return null; + } +} + +// ============================================ +// GESTION DES LIMITES D'UTILISATION +// ============================================ + +/** + * Obtenir les stats d'utilisation + */ +function getUsageStats() { + try { + const stored = localStorage.getItem(LICENSE_CONFIG.usageKey); + if (stored) { + return JSON.parse(stored); + } + } catch {} + + return { + total_pushes: 0, + pushes_today: 0, + last_push_date: null + }; +} + +/** + * Incrémenter le compteur d'utilisation + */ +function incrementUsage() { + const stats = getUsageStats(); + const today = new Date().toISOString().split('T')[0]; + + // Reset si nouveau jour + if (stats.last_push_date !== today) { + stats.pushes_today = 0; + stats.last_push_date = today; + } + + stats.total_pushes = (stats.total_pushes || 0) + 1; + stats.pushes_today = (stats.pushes_today || 0) + 1; + + localStorage.setItem(LICENSE_CONFIG.usageKey, JSON.stringify(stats)); + return stats; +} + +/** + * Vérifier les limites avant un push + */ +async function checkLimits() { + const validation = await validateLicense(); + + if (!validation.valid) { + return { + allowed: false, + error: validation.error, + error_code: validation.error_code + }; + } + + const limits = validation.limits || {}; + const maxPushes = limits.max_pushes_per_day || -1; + + if (maxPushes > 0) { + const stats = getUsageStats(); + const today = new Date().toISOString().split('T')[0]; + + // Reset si nouveau jour + let pushesToday = stats.pushes_today || 0; + if (stats.last_push_date !== today) { + pushesToday = 0; + } + + if (pushesToday >= maxPushes) { + return { + allowed: false, + error: `Limite quotidienne atteinte (${maxPushes} pushes/jour)`, + error_code: 'DAILY_LIMIT_REACHED' + }; + } + } + + return { + allowed: true, + license_type: validation.type_name, + remaining_today: maxPushes > 0 ? maxPushes - getUsageStats().pushes_today : -1 + }; +} + +// ============================================ +// INTERFACE UTILISATEUR +// ============================================ + +/** + * Afficher le badge de licence + */ +async function updateLicenseBadge() { + const container = document.getElementById('license-badge-container'); + if (!container) return; + + const validation = await validateLicense(); + + let badgeHtml = ''; + + if (validation.valid) { + const daysLeft = validation.days_remaining; + let badgeClass = 'license-badge-valid'; + let icon = '✓'; + + if (daysLeft <= 7) { + badgeClass = 'license-badge-expiring'; + icon = '⚠️'; + } else if (daysLeft <= 30) { + badgeClass = 'license-badge-warning'; + icon = '⏳'; + } + + badgeHtml = ` +
+ ${icon} + ${validation.type_name} + ${daysLeft}j +
+ `; + } else { + badgeHtml = ` +
+ 🔐 + Activer +
+ `; + } + + container.innerHTML = badgeHtml; +} + +/** + * Afficher le modal de licence + */ +function showLicenseModal(message = null, errorCode = null) { + // Supprimer l'ancien modal s'il existe + const existingModal = document.getElementById('license-modal'); + if (existingModal) existingModal.remove(); + + const modal = document.createElement('div'); + modal.id = 'license-modal'; + modal.className = 'license-modal-overlay'; + + let errorMessage = ''; + if (message) { + errorMessage = `
${message}
`; + } + + modal.innerHTML = ` +
+
+

🔐 Activation de Licence

+ +
+ +
+ ${errorMessage} + +
+
📄
+
+ Glissez-déposez votre fichier .lic ici +
ou cliquez pour sélectionner +
+ +
+ +
+ Hostname du serveur: + Chargement... +
Communiquez ce hostname pour obtenir votre licence +
+ +
+
+ + +
+ `; + + document.body.appendChild(modal); + + // Afficher le hostname + getSplunkHostname().then(hostname => { + const hostnameDisplay = document.getElementById('current-hostname-display'); + if (hostnameDisplay) { + hostnameDisplay.textContent = hostname; + } + }); + + // Configurer le drag & drop + setupLicenseUpload(); +} + +/** + * Configurer l'upload de licence + */ +function setupLicenseUpload() { + const dropZone = document.getElementById('license-upload-zone'); + const fileInput = document.getElementById('license-file-input'); + + if (!dropZone || !fileInput) return; + + // Clic pour sélectionner + dropZone.addEventListener('click', () => fileInput.click()); + + // Drag & drop + dropZone.addEventListener('dragover', (e) => { + e.preventDefault(); + dropZone.classList.add('dragover'); + }); + + dropZone.addEventListener('dragleave', () => { + dropZone.classList.remove('dragover'); + }); + + dropZone.addEventListener('drop', (e) => { + e.preventDefault(); + dropZone.classList.remove('dragover'); + + const files = e.dataTransfer.files; + if (files.length > 0) { + handleLicenseFile(files[0]); + } + }); + + // Sélection de fichier + fileInput.addEventListener('change', (e) => { + if (e.target.files.length > 0) { + handleLicenseFile(e.target.files[0]); + } + }); +} + +/** + * Traiter le fichier de licence uploadé + */ +async function handleLicenseFile(file) { + const resultDiv = document.getElementById('license-validation-result'); + if (!resultDiv) return; + + // Vérifier l'extension + if (!file.name.endsWith('.lic')) { + resultDiv.innerHTML = ` +
+ ❌ Le fichier doit avoir l'extension .lic +
+ `; + return; + } + + resultDiv.innerHTML = ` +
+ ⏳ Validation en cours... +
+ `; + + try { + // Lire le fichier + const content = await file.text(); + + // Sauvegarder et valider + const result = await saveLicense(content); + + if (result.success) { + const license = result.license; + resultDiv.innerHTML = ` +
+
+
+ Licence activée avec succès !
+ Type: ${license.type_name}
+ Expire: ${license.expires} (${license.days_remaining} jours)
+ Client: ${license.customer?.name || 'N/A'} +
+
+ `; + + // Mettre à jour le badge + updateLicenseBadge(); + + // Fermer le modal après 3 secondes + setTimeout(() => { + closeLicenseModal(); + }, 3000); + + } else { + resultDiv.innerHTML = ` +
+ ❌ ${result.error} +
+ `; + } + + } catch (error) { + resultDiv.innerHTML = ` +
+ ❌ Erreur: ${error.message} +
+ `; + } +} + +/** + * Fermer le modal de licence + */ +function closeLicenseModal() { + const modal = document.getElementById('license-modal'); + if (modal) modal.remove(); +} + +/** + * Afficher les détails de la licence + */ +async function showLicenseDetails() { + const validation = await validateLicense(); + + if (!validation.valid) { + showLicenseModal(validation.error, validation.error_code); + return; + } + + // Supprimer l'ancien modal s'il existe + const existingModal = document.getElementById('license-details-modal'); + if (existingModal) existingModal.remove(); + + const modal = document.createElement('div'); + modal.id = 'license-details-modal'; + modal.className = 'license-modal-overlay'; + + const features = validation.features.map(f => `${f}`).join(' '); + const limits = validation.limits; + const maxApps = limits.max_apps === -1 ? '∞' : limits.max_apps; + const maxPushes = limits.max_pushes_per_day === -1 ? '∞' : limits.max_pushes_per_day; + + const usage = getUsageStats(); + + modal.innerHTML = ` +
+
+

📋 Détails de la Licence

+ +
+ +
+ + + + + + + + + + + +
ID${validation.license_id}
Type${validation.type_name}
Client${validation.customer?.name || 'N/A'}
Email${validation.customer?.email || 'N/A'}
Hostname${validation.hostname}
Émise le${validation.issued}
Expire le${validation.expires}
Jours restants${validation.days_remaining}
Apps max${maxApps}
Pushes/jour${maxPushes}
+ +
+ Fonctionnalités:
+
${features}
+
+ +
+ Utilisation:
+ Total pushes: ${usage.total_pushes} | Aujourd'hui: ${usage.pushes_today} +
+
+ + +
+ `; + + document.body.appendChild(modal); +} + +/** + * Confirmer la suppression de licence + */ +function confirmRemoveLicense() { + if (confirm('Êtes-vous sûr de vouloir supprimer cette licence ?')) { + removeLicense(); + + // Fermer le modal des détails + const detailsModal = document.getElementById('license-details-modal'); + if (detailsModal) detailsModal.remove(); + + // Mettre à jour le badge + updateLicenseBadge(); + + alert('Licence supprimée.'); + } +} + +/** + * Vérifier la licence avant un push (appelé par git_pusher.js) + */ +async function checkLicenseBeforePush() { + const result = await checkLimits(); + + if (!result.allowed) { + showLicenseModal(result.error, result.error_code); + return false; + } + + return true; +} + +// ============================================ +// STYLES CSS +// ============================================ + +const licenseStyles = ` + +`; + +// ============================================ +// INITIALISATION +// ============================================ + +/** + * Initialiser le système de licence + */ +async function initializeLicense() { + console.log('Git Pusher License System v' + LICENSE_CONFIG.version + ' (RSA)'); + + // Injecter les styles + if (!document.getElementById('license-styles')) { + const styleEl = document.createElement('div'); + styleEl.id = 'license-styles'; + styleEl.innerHTML = licenseStyles; + document.head.appendChild(styleEl); + } + + // Vérifier si une licence existe dans localStorage + const stored = localStorage.getItem(LICENSE_CONFIG.storageKey); + + if (!stored) { + console.log('Aucune licence en cache, tentative de chargement depuis le serveur...'); + + // Essayer de charger depuis le serveur + const loadedFromServer = await loadLicenseFromServer(); + + if (loadedFromServer) { + console.log('✓ Licence restaurée depuis le serveur'); + } else { + console.log('Aucune licence disponible'); + } + } else { + console.log('Licence trouvée dans le cache local'); + } + + // Mettre à jour le badge + updateLicenseBadge(); +} + +// Exposer les fonctions globalement +window.initializeLicense = initializeLicense; +window.validateLicense = validateLicense; +window.saveLicense = saveLicense; +window.removeLicense = removeLicense; +window.loadLicenseFromServer = loadLicenseFromServer; +window.checkLicenseBeforePush = checkLicenseBeforePush; +window.showLicenseModal = showLicenseModal; +window.closeLicenseModal = closeLicenseModal; +window.showLicenseDetails = showLicenseDetails; +window.confirmRemoveLicense = confirmRemoveLicense; +window.updateLicenseBadge = updateLicenseBadge; +window.hasFeature = hasFeature; +window.incrementUsage = incrementUsage; +window.getUsageStats = getUsageStats; +window.getLicenseInfo = getLicenseInfo; + +// Auto-initialiser après le chargement +if (document.readyState === 'complete') { + setTimeout(initializeLicense, 100); +} else { + window.addEventListener('load', function() { + setTimeout(initializeLicense, 100); + }); +} \ No newline at end of file diff --git a/apps/pusher_app_prem/bin/__pycache__/license_validator.cpython-39.pyc b/apps/pusher_app_prem/bin/__pycache__/license_validator.cpython-39.pyc index 759b4da72ed06914e60d26178b46ee068819d80f..33e0123e628c05185183a01750310adbe9a6e38e 100644 GIT binary patch delta 19 ZcmezD{n?u