From 73c5eeb45685245fc66a9a92dc5e94e2bf4460a8 Mon Sep 17 00:00:00 2001
From: Jan-Hendrik Willms <tleilax+studip@gmail.com>
Date: Wed, 3 Jan 2024 10:25:56 +0000
Subject: [PATCH] fixes #3570

Closes #3570

Merge request studip/studip!2460
---
 lib/classes/TwoFactorAuth.php | 36 ++++++++++++++++++++++-------------
 1 file changed, 23 insertions(+), 13 deletions(-)

diff --git a/lib/classes/TwoFactorAuth.php b/lib/classes/TwoFactorAuth.php
index 70d05676b27..6c04b8e1ad8 100644
--- a/lib/classes/TwoFactorAuth.php
+++ b/lib/classes/TwoFactorAuth.php
@@ -16,6 +16,7 @@ final class TwoFactorAuth
     const SESSION_DATA          = 'tfa/data';
     const SESSION_CONFIRMATIONS = 'tfa/confirmations';
     const SESSION_FAILED        = 'tfa/failed';
+    const SESSION_TOKEN_SENT    = 'tfa/token-sent';
 
     const COOKIE_KEY = 'tfa/authentication';
 
@@ -178,10 +179,11 @@ final class TwoFactorAuth
      */
     public function confirm($action, $text, array $data = []): void
     {
-        if (isset($_SESSION[self::SESSION_CONFIRMATIONS])
+        if (
+            isset($_SESSION[self::SESSION_CONFIRMATIONS])
             && is_array($_SESSION[self::SESSION_CONFIRMATIONS])
-            && in_array($action, $_SESSION[self::SESSION_CONFIRMATIONS]))
-        {
+            && in_array($action, $_SESSION[self::SESSION_CONFIRMATIONS])
+        ) {
             $_SESSION[self::SESSION_CONFIRMATIONS] = array_diff(
                 $_SESSION[self::SESSION_CONFIRMATIONS],
                 [$action]
@@ -210,14 +212,21 @@ final class TwoFactorAuth
         ]);
 
         if ($this->secret->type === 'email') {
-            StudipMail::sendMessage(
-                $this->secret->user->email,
-                _('Ihr Zwei-Faktor-Token'),
-                sprintf(
-                    _('Bitte geben Sie dieses Token ein: %s'),
-                    $this->secret->getToken()
-                )
-            );
+            if (
+                !isset($_SESSION[self::SESSION_TOKEN_SENT])
+                || $_SESSION[self::SESSION_TOKEN_SENT] < time()
+            ) {
+                StudipMail::sendMessage(
+                    $this->secret->user->email,
+                    _('Ihr Zwei-Faktor-Token'),
+                    sprintf(
+                        _('Bitte geben Sie dieses Token ein: %s'),
+                        $this->secret->getToken()
+                    )
+                );
+
+                $_SESSION[self::SESSION_TOKEN_SENT] = time() + TFASecret::getValidationDuration('email');
+            }
         }
         PageLayout::setBodyElementId('tfa-confirmation-screen');
 
@@ -295,8 +304,9 @@ final class TwoFactorAuth
 
             if ($this->secret->validateToken($token)) {
                 $_SESSION[self::SESSION_FAILED] = [];
+                unset($_SESSION[self::SESSION_TOKEN_SENT]);
 
-                if ($data['global'] ?: false) {
+                if ($data['global'] ?? false) {
                     $this->registerSecretInSession();
 
                     if (Request::int('tfa-trusted')) {
@@ -304,7 +314,7 @@ final class TwoFactorAuth
                     }
                 }
 
-                if ($data['confirm'] ?: false) {
+                if ($data['confirm'] ?? false) {
                     if (!isset($_SESSION[self::SESSION_CONFIRMATIONS])) {
                         $_SESSION[self::SESSION_CONFIRMATIONS] = [];
                     }
-- 
GitLab