GitGram — install.php — GitGram
PassGram / main / v6.00 / install.php14,641 B↓ Raw
<?php
/**
 * PassGram v3.0 - Installation Script
 *
 * This script initializes PassGram by:
 * 1. Generating the Master Application Key (MAK)
 * 2. Creating the first admin user
 * 3. Creating the first group
 * 4. Initializing the database
 * 5. Generating an invite code for additional users
 */

require_once __DIR__ . '/autoload.php';

use PassGram\Core\Config;
use PassGram\Core\Database;
use PassGram\Security\Encryption;
use PassGram\Models\User;
use PassGram\Models\Group;
use PassGram\Models\Invite;
use PassGram\Helpers\Validator;

// ---------------------------------------------------------------------------
// Generate missing config files before Config is loaded.
// This lets the installer run on a completely fresh deployment where only
// autoload.php and install.php exist in the project root.
// ---------------------------------------------------------------------------
$configDir = __DIR__ . '/config';

if (!file_exists($configDir . '/config.php')) {
    file_put_contents($configDir . '/config.php', <<<'CFGPHP'
<?php
/**
 * PassGram v3.0 - Application Configuration
 *
 * Generated during installation.
 * Edit base_url and set cookie_secure to true before deploying to production.
 */

return [

    'name'     => 'PassGram',
    'version'  => '3.0',
    'base_url' => '',        // e.g. https://your-domain.com  (no trailing slash)
    'debug'    => false,

    // Session settings
    'session' => [
        'timeout'         => 3600,              // seconds (60 minutes)
        'name'            => 'passgram_session',
        'cookie_secure'   => false,             // set true when serving over HTTPS
        'cookie_httponly' => true,
        'cookie_samesite' => 'Strict',
    ],

    // Input validation / security rules
    'security' => [
        'password_min_length' => 12,
        'username_min_length' => 3,
        'username_max_length' => 32,
        'max_login_attempts'  => 5,
        'lockout_duration'    => 900,           // seconds (15 minutes)
        'invite_expiry_days'  => 7,
    ],

    // Filesystem paths used at runtime
    'paths' => [
        'pgp' => dirname(__DIR__) . '/data/pgp',
    ],

];
CFGPHP);
}

if (!file_exists($configDir . '/security.php')) {
    file_put_contents($configDir . '/security.php', <<<'SECPHP'
<?php
/**
 * PassGram v3.0 - Security Configuration
 *
 * Generated during installation. Keep this file backed up in a secure location.
 * If the master_application_key is ever lost, all encrypted data is unrecoverable.
 */

return [

    // Master Application Key (MAK) — set by install.php during first run
    'master_application_key' => 'REPLACE_WITH_GENERATED_KEY_DURING_INSTALLATION',

    // AES-256-GCM settings used by the Encryption class
    'encryption' => [
        'algorithm'  => 'aes-256-gcm',
        'key_length' => 32,     // bytes
        'iv_length'  => 12,     // bytes (96-bit nonce, recommended for GCM)
        'tag_length' => 16,     // bytes (128-bit authentication tag)
    ],

    // Argon2id parameters for key derivation (PGP private-key passphrase)
    'argon2' => [
        'memory_cost' => 65536,     // KB (64 MB)
        'time_cost'   => 4,         // iterations
        'threads'     => 2,
    ],

    // bcrypt cost factor for password hashing
    'bcrypt_cost' => 12,

];
SECPHP);
}

// Check if already installed
$config = Config::getInstance();
if ($config->isInstalled()) {
    die("PassGram is already installed. Delete config/security.php to reinstall.");
}

// Handle POST submission
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    try {
        // Get form data
        $adminUsername = trim($_POST['admin_username'] ?? '');
        $adminEmail = trim($_POST['admin_email'] ?? '');
        $adminPassword = $_POST['admin_password'] ?? '';
        $confirmPassword = $_POST['confirm_password'] ?? '';

        // Validate inputs
        $validator = new Validator($config);

        if (!$validator->username($adminUsername)) {
            throw new Exception($validator->getFirstError());
        }

        if (!$validator->email($adminEmail)) {
            throw new Exception($validator->getFirstError());
        }

        if (!$validator->password($adminPassword)) {
            throw new Exception($validator->getFirstError());
        }

        if ($adminPassword !== $confirmPassword) {
            throw new Exception('Passwords do not match');
        }

        // Generate Master Application Key
        $masterKey = Encryption::generateKey();
        $masterKeyHex = bin2hex($masterKey);

        // Write security.php with the generated MAK
        $generatedDate = date('Y-m-d H:i:s');
        file_put_contents($configDir . '/security.php', <<<SECPHP
<?php
/**
 * PassGram v3.0 - Security Configuration
 *
 * Generated: {$generatedDate}
 * Keep this file backed up in a secure location.
 * If the master_application_key is ever lost, all encrypted data is unrecoverable.
 */

return [

    // Master Application Key (MAK) — generated by install.php
    'master_application_key' => '{$masterKeyHex}',

    // AES-256-GCM settings used by the Encryption class
    'encryption' => [
        'algorithm'  => 'aes-256-gcm',
        'key_length' => 32,     // bytes
        'iv_length'  => 12,     // bytes (96-bit nonce, recommended for GCM)
        'tag_length' => 16,     // bytes (128-bit authentication tag)
    ],

    // Argon2id parameters for key derivation (PGP private-key passphrase)
    'argon2' => [
        'memory_cost' => 65536,     // KB (64 MB)
        'time_cost'   => 4,         // iterations
        'threads'     => 2,
    ],

    // bcrypt cost factor for password hashing
    'bcrypt_cost' => 12,

];
SECPHP);

        // Initialize database
        $encryption = new Encryption($masterKey, $config->get('security.encryption'));
        $db = new Database($encryption, $config->database());
        $db->initialize();

        // Create models
        $userModel = new User($db, $config, $validator);
        $groupModel = new Group($db, $validator);
        $inviteModel = new Invite($db, $config, $validator);

        // Create admin user
        $admin = $userModel->create([
            'username' => $adminUsername,
            'email' => $adminEmail,
            'password' => $adminPassword,
        ]);

        // Create default group
        $group = $groupModel->create([
            'name' => 'Default Group',
            'description' => 'Initial group for PassGram users',
            'created_by' => $admin['id'],
        ]);

        // Add admin to group
        $userModel->addToGroup($admin['id'], $group['id']);

        // Generate invite code for additional users
        $invite = $inviteModel->generate($group['id'], $admin['id']);

        // Success!
        $success = true;
        $inviteCode = $invite['code'];

    } catch (Exception $e) {
        $error = $e->getMessage();
    }
}
?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PassGram v3.0 - Installation</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Courier New', Courier, monospace;
            background: #008080;
            color: #000;
            padding: 20px;
        }

        .container {
            max-width: 600px;
            margin: 40px auto;
            background: #C0C0C0;
            border: 3px outset #fff;
            box-shadow: 3px 3px 0 #000;
        }

        .title-bar {
            background: #5D009D;
            color: #fff;
            padding: 4px 8px;
            font-weight: bold;
            border-bottom: 2px solid #000;
        }

        .content {
            padding: 20px;
        }

        h1 {
            font-size: 20px;
            margin-bottom: 20px;
            color: #000;
        }

        .form-group {
            margin-bottom: 15px;
        }

        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }

        input[type="text"],
        input[type="email"],
        input[type="password"] {
            width: 100%;
            padding: 4px;
            border: 2px inset #808080;
            background: #fff;
            font-family: 'Courier New', Courier, monospace;
            font-size: 14px;
        }

        .button {
            background: #C0C0C0;
            border: 2px outset #fff;
            padding: 6px 20px;
            cursor: pointer;
            font-family: 'Courier New', Courier, monospace;
            font-size: 14px;
            font-weight: bold;
        }

        .button:active {
            border-style: inset;
        }

        .error {
            background: #ff0000;
            color: #fff;
            padding: 10px;
            margin-bottom: 15px;
            border: 2px solid #800000;
        }

        .success {
            background: #00ff00;
            color: #000;
            padding: 20px;
            border: 2px solid #008000;
        }

        .info {
            background: #ffff00;
            padding: 10px;
            margin-bottom: 15px;
            border: 2px solid #808000;
        }

        .code {
            background: #000;
            color: #0f0;
            padding: 10px;
            font-family: 'Courier New', Courier, monospace;
            word-break: break-all;
            margin: 10px 0;
        }

        .requirements {
            background: #fff;
            border: 2px inset #808080;
            padding: 15px;
            margin-bottom: 20px;
        }

        .requirements h2 {
            font-size: 16px;
            margin-bottom: 10px;
        }

        .requirements ul {
            margin-left: 20px;
        }

        .requirements li {
            margin-bottom: 5px;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="title-bar">PassGram v3.0 - Installation</div>
        <div class="content">
            <?php if (isset($success) && $success): ?>
                <div class="success">
                    <h1>Installation Successful!</h1>
                    <p><strong>PassGram has been installed successfully.</strong></p>
                    <p style="margin-top: 15px;">Admin username: <strong><?php echo htmlspecialchars($adminUsername); ?></strong></p>
                    <p>Admin email: <strong><?php echo htmlspecialchars($adminEmail); ?></strong></p>

                    <p style="margin-top: 20px;"><strong>Invite Code for Additional Users:</strong></p>
                    <div class="code"><?php echo htmlspecialchars($inviteCode); ?></div>

                    <div class="info" style="margin-top: 20px;">
                        <strong>IMPORTANT SECURITY NOTES:</strong>
                        <ul style="margin-left: 20px; margin-top: 10px;">
                            <li>Delete this install.php file immediately</li>
                            <li>Backup config/security.php in a secure location</li>
                            <li>Save the invite code - you'll need it to register more users</li>
                            <li>Configure HTTPS on your server</li>
                            <li>Update config/config.php with your domain</li>
                        </ul>
                    </div>

                    <p style="margin-top: 20px;">
                        <a href="/login.php" class="button">Go to Login Page</a>
                    </p>
                </div>
            <?php else: ?>
                <h1>PassGram Installation</h1>

                <div class="requirements">
                    <h2>Server Requirements:</h2>
                    <ul>
                        <li>PHP <?php echo phpversion(); ?> <?php echo version_compare(phpversion(), '7.4.0', '>=') ? '✓' : '✗ (7.4+ required)'; ?></li>
                        <li>OpenSSL Extension <?php echo extension_loaded('openssl') ? '✓' : '✗ (required)'; ?></li>
                        <li>JSON Extension <?php echo extension_loaded('json') ? '✓' : '✗ (required)'; ?></li>
                        <li>Writable data/ directory <?php echo is_writable(__DIR__ . '/data') ? '✓' : '✗ (required)'; ?></li>
                        <li>Writable config/ directory <?php echo is_writable(__DIR__ . '/config') ? '✓' : '✗ (required)'; ?></li>
                    </ul>
                </div>

                <?php if (isset($error)): ?>
                    <div class="error">
                        <strong>Error:</strong> <?php echo htmlspecialchars($error); ?>
                    </div>
                <?php endif; ?>

                <div class="info">
                    <strong>Note:</strong> This will create the first admin user and generate a secure Master Application Key. Keep this key secure - if lost, all data will be unrecoverable!
                </div>

                <form method="POST">
                    <div class="form-group">
                        <label for="admin_username">Admin Username:</label>
                        <input type="text" id="admin_username" name="admin_username" required
                               value="<?php echo htmlspecialchars($_POST['admin_username'] ?? ''); ?>">
                        <small>3-32 characters, letters, numbers, underscore only</small>
                    </div>

                    <div class="form-group">
                        <label for="admin_email">Admin Email:</label>
                        <input type="email" id="admin_email" name="admin_email" required
                               value="<?php echo htmlspecialchars($_POST['admin_email'] ?? ''); ?>">
                    </div>

                    <div class="form-group">
                        <label for="admin_password">Admin Password:</label>
                        <input type="password" id="admin_password" name="admin_password" required>
                        <small>Minimum 12 characters, must include uppercase, lowercase, numbers, and special characters</small>
                    </div>

                    <div class="form-group">
                        <label for="confirm_password">Confirm Password:</label>
                        <input type="password" id="confirm_password" name="confirm_password" required>
                    </div>

                    <div class="form-group">
                        <button type="submit" class="button">Install PassGram</button>
                    </div>
                </form>
            <?php endif; ?>
        </div>
    </div>
</body>
</html>
Ready
GitGram