22 KiB
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