diff --git a/assets/icons/sport-bike.svg b/assets/icons/sport-bike.svg new file mode 100644 index 0000000..8057744 --- /dev/null +++ b/assets/icons/sport-bike.svg @@ -0,0 +1,8 @@ + diff --git a/assets/icons/sport-dance.svg b/assets/icons/sport-dance.svg new file mode 100644 index 0000000..ce52a44 --- /dev/null +++ b/assets/icons/sport-dance.svg @@ -0,0 +1,8 @@ + diff --git a/assets/icons/sport-hiit.svg b/assets/icons/sport-hiit.svg new file mode 100644 index 0000000..72a7462 --- /dev/null +++ b/assets/icons/sport-hiit.svg @@ -0,0 +1,6 @@ + diff --git a/assets/icons/sport-hike.svg b/assets/icons/sport-hike.svg new file mode 100644 index 0000000..dac1be4 --- /dev/null +++ b/assets/icons/sport-hike.svg @@ -0,0 +1,8 @@ + diff --git a/assets/icons/sport-strength.svg b/assets/icons/sport-strength.svg new file mode 100644 index 0000000..d6a7fb2 --- /dev/null +++ b/assets/icons/sport-strength.svg @@ -0,0 +1,8 @@ + diff --git a/assets/icons/sport-swim.svg b/assets/icons/sport-swim.svg new file mode 100644 index 0000000..7704eef --- /dev/null +++ b/assets/icons/sport-swim.svg @@ -0,0 +1,7 @@ + diff --git a/assets/icons/sport-yoga.svg b/assets/icons/sport-yoga.svg new file mode 100644 index 0000000..8cfc39f --- /dev/null +++ b/assets/icons/sport-yoga.svg @@ -0,0 +1,8 @@ + diff --git a/src/App.php b/src/App.php index e6049b6..5e8de75 100644 --- a/src/App.php +++ b/src/App.php @@ -170,7 +170,9 @@ final class App redirect('/login'); } - if (!$this->auth->attempt($username, $password)) { + $remember = isset($_POST['remember_me']) && $_POST['remember_me'] === '1'; + + if (!$this->auth->attempt($username, $password, $remember)) { $this->throttle->hit($throttleKey); flash('error', 'Login fehlgeschlagen. Bitte prüfe Benutzername und Passwort.'); redirect('/login'); diff --git a/src/Support/Auth.php b/src/Support/Auth.php index a2c53aa..2574f98 100644 --- a/src/Support/Auth.php +++ b/src/Support/Auth.php @@ -16,7 +16,13 @@ final class Auth $username = $_SESSION['user']['username'] ?? null; - return is_string($username) && $username !== ''; + $valid = is_string($username) && $username !== ''; + + if ($valid && !empty($_SESSION['remember_me'])) { + setcookie(session_name(), session_id(), session_cookie_options_for(time() + remember_me_lifetime())); + } + + return $valid; } public function user(): ?array @@ -28,7 +34,7 @@ final class Auth return $_SESSION['user']; } - public function attempt(string $username, string $password): bool + public function attempt(string $username, string $password, bool $remember = false): bool { $user = $this->users->verify($username, $password); @@ -36,12 +42,12 @@ final class Auth return false; } - $this->login($user); + $this->login($user, $remember); return true; } - public function login(array $user): void + public function login(array $user, bool $remember = false): void { if (!isset($user['username']) || !is_string($user['username']) || $user['username'] === '') { throw new RuntimeException('Der Benutzer konnte nicht angemeldet werden.'); @@ -53,11 +59,20 @@ final class Auth 'username' => $user['username'], 'is_admin' => (bool) ($user['is_admin'] ?? false), ]; + $_SESSION['remember_me'] = $remember; + + if ($remember) { + setcookie(session_name(), session_id(), session_cookie_options_for(time() + remember_me_lifetime())); + } else { + setcookie(session_name(), session_id(), session_cookie_options_for()); + } } public function logout(): void { unset($_SESSION['user']); + unset($_SESSION['remember_me']); + setcookie(session_name(), '', session_cookie_options_for(time() - 3600)); session_regenerate_id(true); } } diff --git a/src/Support/Defaults.php b/src/Support/Defaults.php index c154535..25bf8e0 100644 --- a/src/Support/Defaults.php +++ b/src/Support/Defaults.php @@ -17,22 +17,6 @@ final class Defaults ], ], 'sport_types' => [ - [ - 'id' => 'strength-home', - 'label' => 'Kraftsport (Keller)', - 'icon' => 'strength-home', - 'recovery_group' => 'kraftsport', - 'bonus_points' => 2, - 'allow_consecutive' => false, - ], - [ - 'id' => 'strength-gym', - 'label' => 'Kraftsport (Gym)', - 'icon' => 'strength-gym', - 'recovery_group' => 'kraftsport', - 'bonus_points' => 2, - 'allow_consecutive' => false, - ], [ 'id' => 'running', 'label' => 'Joggen', @@ -41,14 +25,70 @@ final class Defaults 'bonus_points' => 2, 'allow_consecutive' => false, ], + [ + 'id' => 'cycling', + 'label' => 'Radfahren', + 'icon' => 'bike', + 'recovery_group' => 'radfahren', + 'bonus_points' => 2, + 'allow_consecutive' => false, + ], + [ + 'id' => 'strength', + 'label' => 'Krafttraining', + 'icon' => 'strength', + 'recovery_group' => 'kraftsport', + 'bonus_points' => 2, + 'allow_consecutive' => false, + ], + [ + 'id' => 'hiking', + 'label' => 'Wandern', + 'icon' => 'hike', + 'recovery_group' => 'wandern', + 'bonus_points' => 2, + 'allow_consecutive' => false, + ], + [ + 'id' => 'swimming', + 'label' => 'Schwimmen', + 'icon' => 'swim', + 'recovery_group' => 'schwimmen', + 'bonus_points' => 2, + 'allow_consecutive' => false, + ], + [ + 'id' => 'yoga', + 'label' => 'Yoga', + 'icon' => 'yoga', + 'recovery_group' => 'yoga', + 'bonus_points' => 2, + 'allow_consecutive' => false, + ], + [ + 'id' => 'hiit-workout', + 'label' => 'HIIT / Workout', + 'icon' => 'hiit', + 'recovery_group' => 'hiit', + 'bonus_points' => 2, + 'allow_consecutive' => false, + ], [ 'id' => 'rowing', - 'label' => 'Rudergerät', + 'label' => 'Rudern', 'icon' => 'row', 'recovery_group' => 'rudern', 'bonus_points' => 2, 'allow_consecutive' => false, ], + [ + 'id' => 'dance', + 'label' => 'Tanzen', + 'icon' => 'dance', + 'recovery_group' => 'tanzen', + 'bonus_points' => 2, + 'allow_consecutive' => false, + ], [ 'id' => 'core', 'label' => 'Core', diff --git a/src/bootstrap.php b/src/bootstrap.php index f3e76c4..b205b46 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -15,26 +15,12 @@ require __DIR__ . '/App.php'; date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Berlin'); -$forwardedProto = strtolower((string) ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '')); -$forwardedSsl = strtolower((string) ($_SERVER['HTTP_X_FORWARDED_SSL'] ?? '')); -$isSecure = ( - (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') - || $forwardedProto === 'https' - || $forwardedSsl === 'on' -); - ini_set('session.use_only_cookies', '1'); ini_set('session.use_strict_mode', '1'); +ini_set('session.gc_maxlifetime', (string) remember_me_lifetime()); session_name('mood_session'); -session_set_cookie_params([ - 'lifetime' => 0, - 'path' => '/', - 'domain' => '', - 'secure' => $isSecure, - 'httponly' => true, - 'samesite' => 'Lax', -]); +session_set_cookie_params(session_cookie_params_for()); if (session_status() !== PHP_SESSION_ACTIVE) { session_start(); diff --git a/src/helpers.php b/src/helpers.php index 7f2c569..2a1e91a 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -176,6 +176,47 @@ function mood_icon_path(string $sentiment): string return icon_path('mood-' . $sentiment); } +function request_is_secure(): bool +{ + $forwardedProto = strtolower((string) ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '')); + $forwardedSsl = strtolower((string) ($_SERVER['HTTP_X_FORWARDED_SSL'] ?? '')); + + return ( + (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') + || $forwardedProto === 'https' + || $forwardedSsl === 'on' + ); +} + +function remember_me_lifetime(): int +{ + return 60 * 60 * 24 * 30; +} + +function session_cookie_params_for(int $lifetime = 0): array +{ + return [ + 'lifetime' => $lifetime, + 'path' => '/', + 'domain' => '', + 'secure' => request_is_secure(), + 'httponly' => true, + 'samesite' => 'Lax', + ]; +} + +function session_cookie_options_for(int $expires = 0): array +{ + return [ + 'expires' => $expires, + 'path' => '/', + 'domain' => '', + 'secure' => request_is_secure(), + 'httponly' => true, + 'samesite' => 'Lax', + ]; +} + function sport_icon_path(string $icon): string { return icon_path('sport-' . $icon); @@ -184,11 +225,18 @@ function sport_icon_path(string $icon): string function sport_icon_options(): array { return [ - 'strength-home' => 'Kraftsport daheim', - 'strength-gym' => 'Kraftsport im Gym', + 'strength' => 'Krafttraining', + 'bike' => 'Radfahren', 'run' => 'Joggen', + 'hike' => 'Wandern', + 'swim' => 'Schwimmen', + 'yoga' => 'Yoga', + 'hiit' => 'HIIT / Workout', 'row' => 'Rudergerät', + 'dance' => 'Tanzen', 'core' => 'Core', + 'strength-home' => 'Krafttraining Zuhause', + 'strength-gym' => 'Krafttraining Auswärts', ]; } diff --git a/templates/pages/login.php b/templates/pages/login.php index eafb55e..d3c0f84 100644 --- a/templates/pages/login.php +++ b/templates/pages/login.php @@ -16,6 +16,11 @@ + + diff --git a/templates/pages/options.php b/templates/pages/options.php index 9378de4..71ac527 100644 --- a/templates/pages/options.php +++ b/templates/pages/options.php @@ -64,7 +64,7 @@
Lege fest, welche Sportarten im Tracking auswählbar sind. Der Bonus gilt nur, wenn am Vortag keine gleiche Erholungsgruppe trainiert wurde.
+Lege fest, welche Sportarten nur für deinen eigenen Account im Tracking auswählbar sind. Wenn du lieber Varianten wie Krafttraining Zuhause oder Krafttraining Auswärts nutzen möchtest, kannst du sie hier einfach selbst anlegen.