diff --git a/webapp/api.php b/webapp/api.php index b1006d5..79cb95a 100644 --- a/webapp/api.php +++ b/webapp/api.php @@ -289,6 +289,10 @@ try { handleLicenseUsage($db); break; + case 'license_generate': + handleLicenseGenerate($db); + break; + default: jsonResponse(['error' => 'Invalid action'], 400); } @@ -3717,6 +3721,49 @@ function handleLicenseUsage($db) { ]); } +/** + * Generate sample license keys (admin only, for testing) + */ +function handleLicenseGenerate($db) { + require_once __DIR__ . '/includes/auth.php'; + + if (!isAdmin()) { + jsonResponse(['error' => 'Admin access required'], 403); + return; + } + + if ($_SERVER['REQUEST_METHOD'] !== 'GET') { + jsonResponse(['error' => 'GET method required'], 405); + return; + } + + $tier = $_GET['tier'] ?? 'trial'; + + // Map tier name to constant + $tierMap = [ + 'trial' => LICENSE_TRIAL, + 'basic' => LICENSE_BASIC, + 'professional' => LICENSE_PROFESSIONAL, + 'enterprise' => LICENSE_ENTERPRISE + ]; + + $tierConst = $tierMap[$tier] ?? LICENSE_TRIAL; + $key = generateLicenseKey($tierConst); + + global $LICENSE_LIMITS; + $limits = $LICENSE_LIMITS[$tierConst]; + + jsonResponse([ + 'success' => true, + 'license_key' => $key, + 'tier' => $tier, + 'tier_name' => $limits['name'], + 'max_entries' => $limits['max_entries'], + 'max_users' => $limits['max_users'], + 'features' => $limits['features'] + ]); +} + /** * Export full database backup as JSON */ diff --git a/webapp/includes/license.php b/webapp/includes/license.php index 5f214e2..a1d733f 100644 --- a/webapp/includes/license.php +++ b/webapp/includes/license.php @@ -43,34 +43,37 @@ $LICENSE_LIMITS = [ ]; /** - * Generate a new license key - * Format: IPMAN-XXXX-XXXX-XXXX-XXXX + * Tier prefix mapping for license keys + * First segment encodes the tier: Txxxx, Bxxxx, Pxxxx, Exxxx */ -function generateLicenseKey() { - $segments = []; - for ($i = 0; $i < 4; $i++) { +define('LICENSE_TIER_PREFIXES', [ + LICENSE_TRIAL => 'T', + LICENSE_BASIC => 'B', + LICENSE_PROFESSIONAL => 'P', + LICENSE_ENTERPRISE => 'E' +]); + +/** + * Generate a new license key for a given tier + * Format: IPMAN-TYYY-XXXX-XXXX-XXXX + * Where T is the tier prefix (T=Trial, B=Basic, P=Professional, E=Enterprise) + * and the rest are random hex characters + */ +function generateLicenseKey($tier = LICENSE_TRIAL) { + $prefixes = LICENSE_TIER_PREFIXES; + $prefix = $prefixes[$tier] ?? 'T'; + + // First segment: tier prefix + 3 random hex chars + $seg1 = $prefix . strtoupper(bin2hex(random_bytes(1))) . strtoupper(dechex(random_int(0, 15))); + + // Remaining segments: random hex + $segments = [$seg1]; + for ($i = 0; $i < 3; $i++) { $segments[] = strtoupper(bin2hex(random_bytes(2))); } return 'IPMAN-' . implode('-', $segments); } -/** - * Create a signed license key with embedded data - */ -function createSignedLicense($licenseeEmail, $licenseType, $expiresAt = null) { - $data = [ - 'e' => $licenseeEmail, - 't' => $licenseType, - 'i' => time(), - 'x' => $expiresAt ? strtotime($expiresAt) : null - ]; - - $payload = base64_encode(json_encode($data)); - $signature = hash_hmac('sha256', $payload, getLicenseSecret()); - - return 'IPMAN-' . substr($signature, 0, 8) . '-' . chunk_split(strtoupper(bin2hex(random_bytes(6))), 4, '-'); -} - /** * Get license secret (from environment or generate) */ @@ -84,9 +87,25 @@ function getLicenseSecret() { /** * Validate a license key format + * Accepts: IPMAN-XXXX-XXXX-XXXX-XXXX where X is alphanumeric hex */ function isValidLicenseFormat($key) { - return preg_match('/^IPMAN-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}$/i', $key); + return preg_match('/^IPMAN-[A-Z0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}$/i', $key); +} + +/** + * Detect license tier from a license key + */ +function detectLicenseTier($key) { + if (!isValidLicenseFormat($key)) { + return LICENSE_TRIAL; + } + + // First char of first segment after IPMAN- + $tierChar = strtoupper(substr($key, 6, 1)); + + $prefixes = array_flip(LICENSE_TIER_PREFIXES); + return $prefixes[$tierChar] ?? LICENSE_TRIAL; } /** @@ -287,31 +306,26 @@ function canAddUser($db = null) { */ function activateLicense($db, $licenseKey, $licenseeName, $licenseeEmail) { if (!isValidLicenseFormat($licenseKey)) { - return ['success' => false, 'error' => 'Invalid license key format']; + return ['success' => false, 'error' => 'Invalid license key format. Expected: IPMAN-XXXX-XXXX-XXXX-XXXX']; } - // For now, accept any valid format key as a trial - // In production, you would validate against a license server - $licenseType = LICENSE_TRIAL; - $expiresAt = date('Y-m-d H:i:s', strtotime('+14 days')); + // Detect tier from key prefix + $licenseType = detectLicenseTier($licenseKey); - // Check if key starts with specific prefixes for different tiers - if (preg_match('/^IPMAN-[0-9A-F]{4}/', $licenseKey)) { - $prefix = substr($licenseKey, 6, 1); - switch ($prefix) { - case 'B': - $licenseType = LICENSE_BASIC; - $expiresAt = date('Y-m-d H:i:s', strtotime('+1 year')); - break; - case 'P': - $licenseType = LICENSE_PROFESSIONAL; - $expiresAt = date('Y-m-d H:i:s', strtotime('+1 year')); - break; - case 'E': - $licenseType = LICENSE_ENTERPRISE; - $expiresAt = null; // No expiry - break; - } + // Set expiry based on tier + switch ($licenseType) { + case LICENSE_BASIC: + $expiresAt = date('Y-m-d H:i:s', strtotime('+1 year')); + break; + case LICENSE_PROFESSIONAL: + $expiresAt = date('Y-m-d H:i:s', strtotime('+1 year')); + break; + case LICENSE_ENTERPRISE: + $expiresAt = null; // No expiry + break; + default: // Trial + $expiresAt = date('Y-m-d H:i:s', strtotime('+14 days')); + break; } global $LICENSE_LIMITS; diff --git a/webapp/settings.php b/webapp/settings.php index a4c205c..7333607 100644 --- a/webapp/settings.php +++ b/webapp/settings.php @@ -612,6 +612,44 @@ require_once __DIR__ . '/includes/header.php'; + +
Generate sample license keys for testing. Keys start with T (Trial), B (Basic), P (Professional), or E (Enterprise).
+ +