telvero_whatson_talpa/plans/authentication-implementation-examples.md
2026-02-19 15:58:15 +01:00

22 KiB

Authentication System - Implementatie Voorbeelden

Code Voorbeelden

1. Login Pagina (auth/login.php)

<?php
session_start();
require_once __DIR__ . '/../vendor/autoload.php';

use Dotenv\Dotenv;
$dotenv = Dotenv::createImmutable(__DIR__ . '/..');
$dotenv->load();

$db = new PDO(
    "mysql:host={$_ENV['DB_HOST']};dbname={$_ENV['DB_NAME']}", 
    $_ENV['DB_USER'], 
    $_ENV['DB_PASS']
);

$error = '';
$redirect = $_GET['redirect'] ?? 'index.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = $_POST['username'] ?? '';
    $password = $_POST['password'] ?? '';
    
    // Check brute force
    $stmt = $db->prepare("
        SELECT COUNT(*) FROM login_attempts 
        WHERE username = ? AND success = 0 
        AND attempted_at > DATE_SUB(NOW(), INTERVAL 15 MINUTE)
    ");
    $stmt->execute([$username]);
    $failedAttempts = $stmt->fetchColumn();
    
    if ($failedAttempts >= 5) {
        $error = 'Te veel mislukte pogingen. Probeer het over 15 minuten opnieuw.';
    } else {
        // Verify credentials
        $stmt = $db->prepare("
            SELECT id, username, email, password_hash, role, is_active 
            FROM users 
            WHERE (username = ? OR email = ?) AND is_active = 1
        ");
        $stmt->execute([$username, $username]);
        $user = $stmt->fetch();
        
        if ($user && password_verify($password, $user['password_hash'])) {
            // Success - create session
            session_regenerate_id(true);
            $_SESSION['user_id'] = $user['id'];
            $_SESSION['user'] = [
                'id' => $user['id'],
                'username' => $user['username'],
                'email' => $user['email'],
                'role' => $user['role']
            ];
            
            // Log success
            $stmt = $db->prepare("
                INSERT INTO login_attempts (username, ip_address, success) 
                VALUES (?, ?, 1)
            ");
            $stmt->execute([$username, $_SERVER['REMOTE_ADDR']]);
            
            // Update last login
            $stmt = $db->prepare("UPDATE users SET last_login = NOW() WHERE id = ?");
            $stmt->execute([$user['id']]);
            
            header("Location: ../$redirect");
            exit;
        } else {
            // Failed login
            $error = 'Ongeldige gebruikersnaam of wachtwoord';
            
            $stmt = $db->prepare("
                INSERT INTO login_attempts (username, ip_address, success) 
                VALUES (?, ?, 0)
            ");
            $stmt->execute([$username, $_SERVER['REMOTE_ADDR']]);
        }
    }
}
?>
<!DOCTYPE html>
<html lang="nl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login - Telvero Talpa</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
    <style>
        body {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .login-card {
            max-width: 400px;
            width: 100%;
        }
    </style>
</head>
<body>
    <div class="login-card">
        <div class="card shadow-lg">
            <div class="card-body p-5">
                <div class="text-center mb-4">
                    <i class="bi bi-tv" style="font-size: 3rem; color: #667eea;"></i>
                    <h3 class="mt-3">Telvero Talpa</h3>
                    <p class="text-muted">Planning System</p>
                </div>
                
                <?php if ($error): ?>
                    <div class="alert alert-danger">
                        <i class="bi bi-exclamation-triangle"></i>
                        <?= htmlspecialchars($error) ?>
                    </div>
                <?php endif; ?>
                
                <form method="POST">
                    <div class="mb-3">
                        <label class="form-label">Gebruikersnaam of Email</label>
                        <div class="input-group">
                            <span class="input-group-text">
                                <i class="bi bi-person"></i>
                            </span>
                            <input type="text" 
                                   name="username" 
                                   class="form-control" 
                                   required 
                                   autofocus
                                   value="<?= htmlspecialchars($_POST['username'] ?? '') ?>">
                        </div>
                    </div>
                    
                    <div class="mb-3">
                        <label class="form-label">Wachtwoord</label>
                        <div class="input-group">
                            <span class="input-group-text">
                                <i class="bi bi-lock"></i>
                            </span>
                            <input type="password" 
                                   name="password" 
                                   class="form-control" 
                                   required>
                        </div>
                    </div>
                    
                    <div class="mb-3 form-check">
                        <input type="checkbox" class="form-check-input" id="remember" name="remember">
                        <label class="form-check-label" for="remember">
                            Onthoud mij
                        </label>
                    </div>
                    
                    <button type="submit" class="btn btn-primary w-100">
                        <i class="bi bi-box-arrow-in-right"></i> Inloggen
                    </button>
                </form>
                
                <div class="mt-4 text-center">
                    <small class="text-muted">
                        Telvero © <?= date('Y') ?>
                    </small>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

2. Authenticatie Functies (auth/auth_functions.php)

<?php
/**
 * Authentication Helper Functions
 */

if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

require_once __DIR__ . '/../vendor/autoload.php';

use Dotenv\Dotenv;
$dotenv = Dotenv::createImmutable(__DIR__ . '/..');
$dotenv->load();

/**
 * Get database connection
 */
function getAuthDb() {
    static $db = null;
    if ($db === null) {
        $db = new PDO(
            "mysql:host={$_ENV['DB_HOST']};dbname={$_ENV['DB_NAME']}", 
            $_ENV['DB_USER'], 
            $_ENV['DB_PASS']
        );
        $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }
    return $db;
}

/**
 * Check if user is logged in
 */
function isLoggedIn() {
    return isset($_SESSION['user_id']) && isset($_SESSION['user']);
}

/**
 * Get current logged in user
 */
function getCurrentUser() {
    return $_SESSION['user'] ?? null;
}

/**
 * Check if user has specific role
 */
function hasRole($role) {
    if (!isLoggedIn()) {
        return false;
    }
    return $_SESSION['user']['role'] === $role;
}

/**
 * Check if user is admin
 */
function isAdmin() {
    return hasRole('admin');
}

/**
 * Check if user is guest
 */
function isGuest() {
    return hasRole('guest');
}

/**
 * Require user to be logged in
 */
function requireLogin() {
    if (!isLoggedIn()) {
        $currentUrl = $_SERVER['REQUEST_URI'];
        $redirect = urlencode($currentUrl);
        header("Location: /auth/login.php?redirect=$redirect");
        exit;
    }
}

/**
 * Require user to have specific role
 */
function requireRole($role) {
    requireLogin();
    
    if (!hasRole($role)) {
        http_response_code(403);
        echo '<!DOCTYPE html>
        <html lang="nl">
        <head>
            <meta charset="UTF-8">
            <title>Geen Toegang</title>
            <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
        </head>
        <body class="bg-light">
            <div class="container mt-5">
                <div class="alert alert-danger">
                    <h4><i class="bi bi-exclamation-triangle"></i> Geen Toegang</h4>
                    <p>Je hebt geen toegang tot deze pagina.</p>
                    <a href="/index.php" class="btn btn-primary">Terug naar Dashboard</a>
                </div>
            </div>
        </body>
        </html>';
        exit;
    }
}

/**
 * Require admin role
 */
function requireAdmin() {
    requireRole('admin');
}

/**
 * Logout user
 */
function logout() {
    $_SESSION = [];
    
    if (isset($_COOKIE[session_name()])) {
        setcookie(session_name(), '', time() - 3600, '/');
    }
    
    session_destroy();
}

/**
 * Check if user can perform write operations
 */
function canWrite() {
    return isLoggedIn() && isAdmin();
}

/**
 * Check if user can only read
 */
function canOnlyRead() {
    return isLoggedIn() && isGuest();
}

3. Middleware (auth/middleware.php)

<?php
/**
 * Authorization Middleware
 */

require_once __DIR__ . '/auth_functions.php';

/**
 * Check if user can create
 */
function canCreate() {
    return isAdmin();
}

/**
 * Check if user can edit
 */
function canEdit() {
    return isAdmin();
}

/**
 * Check if user can delete
 */
function canDelete() {
    return isAdmin();
}

/**
 * Check if user can sync with Talpa
 */
function canSync() {
    return isAdmin();
}

/**
 * Render button only if user has permission
 */
function renderButton($permission, $html) {
    if ($permission) {
        echo $html;
    }
}

/**
 * Render form only if user has permission
 */
function renderForm($permission, $html) {
    if ($permission) {
        echo $html;
    }
}

/**
 * Get disabled attribute for guest users
 */
function getDisabledAttr() {
    return isGuest() ? 'disabled' : '';
}

/**
 * Get readonly attribute for guest users
 */
function getReadonlyAttr() {
    return isGuest() ? 'readonly' : '';
}

4. Aangepaste Navigatie

<!-- In alle pagina's: index.php, planner.php, calendar.php, etc. -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <div class="container-fluid">
        <a class="navbar-brand" href="index.php">
            <i class="bi bi-tv"></i> Telvero Talpa Planner
        </a>
        <div class="navbar-nav">
            <a class="nav-link" href="index.php">Dashboard</a>
            <a class="nav-link" href="planner.php">Planner</a>
            <a class="nav-link" href="calendar.php">Kalender</a>
            <a class="nav-link" href="blocks.php">Blokken</a>
            <a class="nav-link" href="infomercials.php">Infomercials</a>
            <?php if (isAdmin()): ?>
                <a class="nav-link" href="admin/users.php">Gebruikers</a>
            <?php endif; ?>
        </div>
        
        <!-- User Info & Logout -->
        <div class="navbar-nav ms-auto">
            <span class="nav-link">
                <i class="bi bi-person-circle"></i> 
                <?= htmlspecialchars(getCurrentUser()['username']) ?>
                <span class="badge bg-<?= isAdmin() ? 'danger' : 'secondary' ?>">
                    <?= isAdmin() ? 'Admin' : 'Guest' ?>
                </span>
            </span>
            <a class="nav-link" href="auth/logout.php">
                <i class="bi bi-box-arrow-right"></i> Uitloggen
            </a>
        </div>
    </div>
</nav>

5. Voorbeeld: Dashboard met Role-based UI

<?php
require_once __DIR__ . '/auth/auth_functions.php';
require_once __DIR__ . '/auth/middleware.php';

// Require login
requireLogin();

// Rest of existing code...
?>

<!-- In de HTML -->
<div class="container mt-4">
    <?php if (isGuest()): ?>
        <div class="alert alert-info">
            <i class="bi bi-info-circle"></i>
            Je bent ingelogd als <strong>Guest</strong>. Je kunt alleen informatie bekijken.
        </div>
    <?php endif; ?>
    
    <!-- Existing content -->
    
    <!-- Infomercial Registration Form - Only for Admins -->
    <?php if (canCreate()): ?>
        <div class="card p-3 shadow-sm">
            <h5>1. Infomercial Registreren</h5>
            <form method="POST">
                <input type="text" name="title" class="form-control mb-2" placeholder="Product Naam" required>
                <input type="text" name="duration" class="form-control mb-2" placeholder="Duur (HH:MM:SS)" required>
                <input type="hidden" name="season_id" value="<?= $_ENV['TV_SEASON_ID'] ?>">
                <button type="submit" name="add_commercial" class="btn btn-primary w-100">
                    Registreren bij Talpa
                </button>
            </form>
        </div>
    <?php endif; ?>
    
    <!-- Sync buttons - Only for Admins -->
    <?php if (canSync()): ?>
        <form method="POST" style="display:inline;">
            <input type="hidden" name="sync_id" value="<?= $tx['id'] ?>">
            <button type="submit" name="sync_item" class="btn-icon btn-icon-xs btn-icon-success">
                <i class="bi bi-cloud-upload"></i>
            </button>
        </form>
    <?php else: ?>
        <button type="button" 
                class="btn-icon btn-icon-xs btn-icon-secondary" 
                disabled
                title="Alleen admins kunnen synchroniseren">
            <i class="bi bi-cloud-upload"></i>
        </button>
    <?php endif; ?>
</div>

6. API Beveiliging Voorbeeld

<?php
// api/create_transmission.php
require_once __DIR__ . '/../auth/auth_functions.php';

// Check authentication
if (!isLoggedIn()) {
    http_response_code(401);
    echo json_encode([
        'success' => false, 
        'error' => 'Niet geautoriseerd. Log eerst in.'
    ]);
    exit;
}

// Check authorization (admin only for write operations)
if (!isAdmin()) {
    http_response_code(403);
    echo json_encode([
        'success' => false, 
        'error' => 'Geen toegang. Alleen admins kunnen uitzendingen aanmaken.'
    ]);
    exit;
}

// Continue with existing logic...

7. Gebruikersbeheer Interface (admin/users.php)

<?php
require_once __DIR__ . '/../auth/auth_functions.php';
require_once __DIR__ . '/../auth/middleware.php';

// Only admins can access
requireAdmin();

$db = getAuthDb();

// Handle user creation
if (isset($_POST['create_user'])) {
    $username = $_POST['username'];
    $email = $_POST['email'];
    $password = $_POST['password'];
    $role = $_POST['role'];
    
    $passwordHash = password_hash($password, PASSWORD_BCRYPT);
    
    $stmt = $db->prepare("
        INSERT INTO users (username, email, password_hash, role) 
        VALUES (?, ?, ?, ?)
    ");
    $stmt->execute([$username, $email, $passwordHash, $role]);
    
    header("Location: users.php?success=created");
    exit;
}

// Handle user update
if (isset($_POST['update_user'])) {
    $userId = $_POST['user_id'];
    $username = $_POST['username'];
    $email = $_POST['email'];
    $role = $_POST['role'];
    $isActive = isset($_POST['is_active']) ? 1 : 0;
    
    $stmt = $db->prepare("
        UPDATE users 
        SET username = ?, email = ?, role = ?, is_active = ? 
        WHERE id = ?
    ");
    $stmt->execute([$username, $email, $role, $isActive, $userId]);
    
    header("Location: users.php?success=updated");
    exit;
}

// Get all users
$users = $db->query("
    SELECT id, username, email, role, is_active, last_login, created_at 
    FROM users 
    ORDER BY created_at DESC
")->fetchAll();
?>

<!DOCTYPE html>
<html lang="nl">
<head>
    <meta charset="UTF-8">
    <title>Gebruikersbeheer - Telvero Talpa</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
</head>
<body class="bg-light">
    <!-- Navigation -->
    <?php include __DIR__ . '/../includes/nav.php'; ?>
    
    <div class="container mt-4">
        <h1><i class="bi bi-people"></i> Gebruikersbeheer</h1>
        
        <?php if (isset($_GET['success'])): ?>
            <div class="alert alert-success alert-dismissible fade show">
                Gebruiker succesvol <?= $_GET['success'] === 'created' ? 'aangemaakt' : 'bijgewerkt' ?>!
                <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
            </div>
        <?php endif; ?>
        
        <!-- Create User Button -->
        <button type="button" class="btn btn-primary mb-3" data-bs-toggle="modal" data-bs-target="#createUserModal">
            <i class="bi bi-plus-circle"></i> Nieuwe Gebruiker
        </button>
        
        <!-- Users Table -->
        <div class="card shadow-sm">
            <div class="card-body">
                <table class="table table-hover">
                    <thead>
                        <tr>
                            <th>Gebruikersnaam</th>
                            <th>Email</th>
                            <th>Rol</th>
                            <th>Status</th>
                            <th>Laatste Login</th>
                            <th>Acties</th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($users as $user): ?>
                            <tr>
                                <td><?= htmlspecialchars($user['username']) ?></td>
                                <td><?= htmlspecialchars($user['email']) ?></td>
                                <td>
                                    <span class="badge bg-<?= $user['role'] === 'admin' ? 'danger' : 'secondary' ?>">
                                        <?= ucfirst($user['role']) ?>
                                    </span>
                                </td>
                                <td>
                                    <span class="badge bg-<?= $user['is_active'] ? 'success' : 'warning' ?>">
                                        <?= $user['is_active'] ? 'Actief' : 'Inactief' ?>
                                    </span>
                                </td>
                                <td>
                                    <?= $user['last_login'] ? date('d-m-Y H:i', strtotime($user['last_login'])) : 'Nooit' ?>
                                </td>
                                <td>
                                    <button type="button" 
                                            class="btn btn-sm btn-primary"
                                            onclick="editUser(<?= htmlspecialchars(json_encode($user)) ?>)">
                                        <i class="bi bi-pencil"></i>
                                    </button>
                                </td>
                            </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
    
    <!-- Create User Modal -->
    <div class="modal fade" id="createUserModal" tabindex="-1">
        <div class="modal-dialog">
            <div class="modal-content">
                <form method="POST">
                    <div class="modal-header">
                        <h5 class="modal-title">Nieuwe Gebruiker</h5>
                        <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                    </div>
                    <div class="modal-body">
                        <div class="mb-3">
                            <label class="form-label">Gebruikersnaam</label>
                            <input type="text" name="username" class="form-control" required>
                        </div>
                        <div class="mb-3">
                            <label class="form-label">Email</label>
                            <input type="email" name="email" class="form-control" required>
                        </div>
                        <div class="mb-3">
                            <label class="form-label">Wachtwoord</label>
                            <input type="password" name="password" class="form-control" required minlength="8">
                        </div>
                        <div class="mb-3">
                            <label class="form-label">Rol</label>
                            <select name="role" class="form-select" required>
                                <option value="guest">Guest</option>
                                <option value="admin">Admin</option>
                            </select>
                        </div>
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuleren</button>
                        <button type="submit" name="create_user" class="btn btn-primary">Aanmaken</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

8. JavaScript voor Guest Mode (calendar-init.js aanpassing)

// In assets/js/calendar-init.js

// Check if user is guest
const isGuest = <?= json_encode(isGuest()) ?>;

// Disable drag and drop for guests
if (isGuest) {
    // Disable external events
    document.querySelectorAll('.infomercial-item').forEach(item => {
        item.style.cursor = 'not-allowed';
        item.style.opacity = '0.6';
        item.removeAttribute('draggable');
    });
    
    // Make calendar read-only
    calendar = new FullCalendar.Calendar(calendarEl, {
        // ... existing config
        editable: false,
        droppable: false,
        eventStartEditable: false,
        eventDurationEditable: false,
        eventResourceEditable: false
    });
    
    // Show tooltip on hover
    document.querySelectorAll('.infomercial-item').forEach(item => {
        item.title = 'Alleen admins kunnen infomercials slepen';
    });
}

Visuele Voorbeelden

Login Pagina

┌─────────────────────────────────────┐
│                                     │
│           📺 Telvero Talpa          │
│           Planning System