From 0a2b099e501fc433985728c59bf6fbb5cca1209d Mon Sep 17 00:00:00 2001
From: lukasmatusiewicz <lukas.matusiewicz@netknights.it>
Date: Wed, 6 Sep 2023 12:58:08 +0200
Subject: [PATCH] 41 feature forward headers (#42)

* forward headers implementation

* update tests

* update tests

* Update PrivacyIDEA.php
---
 src/PrivacyIDEA.php                | 76 +++++++++++++++++++++++-------
 test/EnrollTokenTest.php           |  3 +-
 test/PollTransactionTest.php       |  4 +-
 test/TriggerChallengeTest.php      |  2 +-
 test/ValidateCheckTest.php         |  2 +-
 test/ValidateCheckU2FTest.php      |  4 +-
 test/ValidateCheckWebauthnTest.php |  2 +-
 7 files changed, 69 insertions(+), 24 deletions(-)

diff --git a/src/PrivacyIDEA.php b/src/PrivacyIDEA.php
index a6063c9..bf929b0 100644
--- a/src/PrivacyIDEA.php
+++ b/src/PrivacyIDEA.php
@@ -62,10 +62,11 @@ class PrivacyIDEA
      * @param $username string
      * @param $pass string this can be the OTP, but also the PIN to trigger a token or PIN+OTP depending on the configuration of the server.
      * @param null $transactionID Optional transaction ID. Used to reference a challenge that was triggered beforehand.
+     * @param null $headers Optional headers array to forward to the server.
      * @return PIResponse|null null if response was empty or malformed, or parameter missing
      * @throws PIBadRequestException
      */
-    public function validateCheck($username, $pass, $transactionID = null)
+    public function validateCheck($username, $pass, $transactionID = null, $headers=null)
     {
         assert('string' === gettype($username));
         assert('string' === gettype($pass));
@@ -79,12 +80,16 @@ class PrivacyIDEA
                 // Add transaction ID in case of challenge response
                 $params["transaction_id"] = $transactionID;
             }
+            if (empty($headers))
+            {
+                $headers = array('');
+            }
             if ($this->realm)
             {
                 $params["realm"] = $this->realm;
             }
 
-            $response = $this->sendRequest($params, array(''), 'POST', '/validate/check');
+            $response = $this->sendRequest($params, $headers, 'POST', '/validate/check');
 
             $ret = PIResponse::fromJSON($response, $this);
             if ($ret == null)
@@ -105,17 +110,18 @@ class PrivacyIDEA
      * This function requires a service account to be set.
      *
      * @param string $username
+     * @param null $headers Optional headers array to forward to the server.
      * @return PIResponse|null null if response was empty or malformed, or parameter missing
      * @throws PIBadRequestException
      */
-    public function triggerChallenge($username)
+    public function triggerChallenge($username, $headers = null)
     {
         assert('string' === gettype($username));
 
         if ($username)
         {
             $authToken = $this->getAuthToken();
-            $header = array("authorization:" . $authToken);
+            $authTokenHeader = array("authorization:" . $authToken);
 
             $params = array("user" => $username);
 
@@ -124,7 +130,16 @@ class PrivacyIDEA
                 $params["realm"] = $this->realm;
             }
 
-            $response = $this->sendRequest($params, $header, 'POST', '/validate/triggerchallenge');
+            if (!empty($headers))
+            {
+                $headers = array_merge($headers, $authTokenHeader);
+            }
+            else
+            {
+                $headers = $authTokenHeader;
+            }
+
+            $response = $this->sendRequest($params, $headers, 'POST', '/validate/triggerchallenge');
 
             return PIResponse::fromJSON($response, $this);
         }
@@ -139,17 +154,22 @@ class PrivacyIDEA
      * Poll for the status of a transaction (challenge).
      *
      * @param $transactionID string transactionId of the push challenge that was triggered before
+     * @param null $headers Optional headers array to forward to the server.
      * @return bool true if the Push request has been accepted, false otherwise.
      * @throws PIBadRequestException
      */
-    public function pollTransaction($transactionID)
+    public function pollTransaction($transactionID, $headers = null)
     {
         assert('string' === gettype($transactionID));
 
         if (!empty($transactionID))
         {
             $params = array("transaction_id" => $transactionID);
-            $responseJSON = $this->sendRequest($params, array(''), 'GET', '/validate/polltransaction');
+            if (empty($headers))
+            {
+                $headers = array('');
+            }
+            $responseJSON = $this->sendRequest($params, $headers, 'GET', '/validate/polltransaction');
             $response = json_decode($responseJSON, true);
             return $response['result']['value'];
         }
@@ -167,10 +187,11 @@ class PrivacyIDEA
      * @param string $genkey
      * @param string $type
      * @param string $description
+     * @param null $headers Optional headers array to forward to the server.
      * @return mixed Object representing the response of the server or null if parameters are missing
      * @throws PIBadRequestException
      */
-    public function enrollToken($username, $genkey, $type, $description = "") // No return type because mixed not allowed yet
+    public function enrollToken($username, $genkey, $type, $description = "", $headers = null) // No return type because mixed not allowed yet
     {
         assert('string' === gettype($username));
         assert('string' === gettype($type));
@@ -196,10 +217,18 @@ class PrivacyIDEA
         $authToken = $this->getAuthToken();
 
         // If error occurred in getAuthToken() - return this error in PIResponse object
-        $header = array("authorization:" . $authToken);
+        $authTokenHeader = array("authorization:" . $authToken);
+        if (!empty($headers))
+        {
+            $headers = array_merge($headers, $authTokenHeader);
+        }
+        else
+        {
+            $headers = $authTokenHeader;
+        }
 
         // Check if user has token
-        $tokenInfo = json_decode($this->sendRequest(array("user" => $username, "realm" => $params["realm"]), $header, 'GET', '/token'));
+        $tokenInfo = json_decode($this->sendRequest(array("user" => $username, "realm" => $params["realm"]), $headers, 'GET', '/token'));
 
         if (!empty($tokenInfo->result->value->tokens))
         {
@@ -209,7 +238,7 @@ class PrivacyIDEA
         else
         {
             // Call /token/init endpoint and return the response
-            return json_decode($this->sendRequest($params, $header, 'POST', '/token/init'));
+            return json_decode($this->sendRequest($params, $headers, 'POST', '/token/init'));
         }
     }
 
@@ -220,10 +249,11 @@ class PrivacyIDEA
      * @param string $transactionID
      * @param string $webAuthnSignResponse
      * @param string $origin
+     * @param null $headers Optional headers array to forward to the server.
      * @return PIResponse|null returns null if the response was empty or malformed
      * @throws PIBadRequestException
      */
-    public function validateCheckWebAuthn($username, $transactionID, $webAuthnSignResponse, $origin)
+    public function validateCheckWebAuthn($username, $transactionID, $webAuthnSignResponse, $origin, $headers = null)
     {
         assert('string' === gettype($username));
         assert('string' === gettype($transactionID));
@@ -259,9 +289,17 @@ class PrivacyIDEA
                 $params[ASSERTIONCLIENTEXTENSIONS] = $tmp[ASSERTIONCLIENTEXTENSIONS];
             }
 
-            $header = array("Origin:" . $origin);
+            $originHeader = array("Origin:" . $origin);
+            if (!empty($headers))
+            {
+                $headers = array_merge($headers, $originHeader);
+            }
+            else
+            {
+                $headers = $originHeader;
+            }
 
-            $response = $this->sendRequest($params, $header, 'POST', '/validate/check');
+            $response = $this->sendRequest($params, $headers, 'POST', '/validate/check');
 
             return PIResponse::fromJSON($response, $this);
         }
@@ -279,10 +317,11 @@ class PrivacyIDEA
      * @param string $username
      * @param string $transactionID
      * @param string $u2fSignResponse
+     * @param null $headers Optional headers array to forward to the server.
      * @return PIResponse|null
      * @throws PIBadRequestException
      */
-    public function validateCheckU2F($username, $transactionID, $u2fSignResponse)
+    public function validateCheckU2F($username, $transactionID, $u2fSignResponse, $headers = null)
     {
         assert('string' === gettype($username));
         assert('string' === gettype($transactionID));
@@ -301,12 +340,17 @@ class PrivacyIDEA
                 $params["realm"] = $this->realm;
             }
 
+            if (!empty($headers))
+            {
+                $headers = array('');
+            }
+
             // Additional U2F params from $u2fSignResponse
             $tmp = json_decode($u2fSignResponse, true);
             $params[CLIENTDATA] = $tmp["clientData"];
             $params[SIGNATUREDATA] = $tmp["signatureData"];
 
-            $response = $this->sendRequest($params, array(), 'POST', '/validate/check');
+            $response = $this->sendRequest($params, $headers, 'POST', '/validate/check');
 
             return PIResponse::fromJSON($response, $this);
         }
diff --git a/test/EnrollTokenTest.php b/test/EnrollTokenTest.php
index 5d26446..0fe0b65 100644
--- a/test/EnrollTokenTest.php
+++ b/test/EnrollTokenTest.php
@@ -71,7 +71,8 @@ class EnrollTokenTest extends TestCase implements PILog
             "testUser",
             "1",
             "totp",
-            "Enrolled for test");
+            "Enrolled for test",
+            array('accept-language:en'));
 
         $this->assertNotNull($response);
         $this->assertIsObject($response);
diff --git a/test/PollTransactionTest.php b/test/PollTransactionTest.php
index 80d947e..46f4392 100644
--- a/test/PollTransactionTest.php
+++ b/test/PollTransactionTest.php
@@ -51,7 +51,7 @@ class PollTransactionTest extends TestCase implements PILog
             ->end();
         $this->http->setUp();
 
-        $response = $this->pi->validateCheck("testUser", "testPass");
+        $response = $this->pi->validateCheck("testUser", "testPass", null, array('accept-language:en'));
 
         $this->assertEquals("Bitte geben Sie einen OTP-Wert ein: , Please confirm the authentication on your mobile device!", $response->message);
         $this->assertEquals("Bitte geben Sie einen OTP-Wert ein: , Please confirm the authentication on your mobile device!", $response->messages);
@@ -80,7 +80,7 @@ class PollTransactionTest extends TestCase implements PILog
             ->end();
         $this->http->setUp();
 
-        $response = $this->pi->pollTransaction("1234567890");
+        $response = $this->pi->pollTransaction("1234567890", array('accept-language:en'));
 
         $this->assertNotNull($response);
         $this->assertTrue($response);
diff --git a/test/TriggerChallengeTest.php b/test/TriggerChallengeTest.php
index fbfaed0..84f9c05 100644
--- a/test/TriggerChallengeTest.php
+++ b/test/TriggerChallengeTest.php
@@ -87,7 +87,7 @@ class TriggerChallengeTest extends TestCase implements PILog
      */
     public function testNoServiceAccount()
     {
-        $response = $this->pi->triggerchallenge("testUser");
+        $response = $this->pi->triggerchallenge("testUser", array('accept-language:en'));
 
         $this->assertNull($response);
     }
diff --git a/test/ValidateCheckTest.php b/test/ValidateCheckTest.php
index d53c15f..5a92b6e 100644
--- a/test/ValidateCheckTest.php
+++ b/test/ValidateCheckTest.php
@@ -54,7 +54,7 @@ class ValidateCheckTest extends TestCase implements PILog
             ->end();
         $this->http->setUp();
 
-        $response = $this->pi->validateCheck("testUser", "testPass");
+        $response = $this->pi->validateCheck("testUser", "testPass", null, array('accept-language:en'));
 
         $this->assertEquals("matching 1 tokens", $response->message);
         $this->assertEquals(Utils::matchingOneTokenResponseBody(), $response->raw);
diff --git a/test/ValidateCheckU2FTest.php b/test/ValidateCheckU2FTest.php
index 1085177..e929e78 100644
--- a/test/ValidateCheckU2FTest.php
+++ b/test/ValidateCheckU2FTest.php
@@ -51,7 +51,7 @@ class ValidateCheckU2FTest extends TestCase implements PILog
             ->end();
         $this->http->setUp();
 
-        $response = $this->pi->validateCheck("testUser", "testPass");
+        $response = $this->pi->validateCheck("testUser", "testPass", null, array('accept-language:en'));
 
         $this->assertEquals("Please confirm with your U2F token (Yubico U2F EE Serial 61730834)", $response->message);
         $this->assertEquals("Please confirm with your U2F token (Yubico U2F EE Serial 61730834)", $response->messages);
@@ -82,7 +82,7 @@ class ValidateCheckU2FTest extends TestCase implements PILog
             ->end();
         $this->http->setUp();
 
-        $response = $this->pi->validateCheckU2F("testUser", "12345678", Utils::u2fSignResponse());
+        $response = $this->pi->validateCheckU2F("testUser", "12345678", Utils::u2fSignResponse(), array('accept-language:en'));
 
         $this->assertEquals("matching 1 tokens", $response->message);
         $this->assertEquals(Utils::matchingOneTokenResponseBody(), $response->raw);
diff --git a/test/ValidateCheckWebauthnTest.php b/test/ValidateCheckWebauthnTest.php
index 021c195..a795108 100644
--- a/test/ValidateCheckWebauthnTest.php
+++ b/test/ValidateCheckWebauthnTest.php
@@ -84,7 +84,7 @@ class ValidateCheckWebauthnTest extends TestCase implements PILog
             ->end();
         $this->http->setUp();
 
-        $response = $this->pi->validateCheckWebAuthn("testUser", "12345678", Utils::webauthnSignResponse(), "test.it");
+        $response = $this->pi->validateCheckWebAuthn("testUser", "12345678", Utils::webauthnSignResponse(), "test.it", array('accept-language:en'));
 
         $this->assertNotNull($response);
         $this->assertEquals(Utils::matchingOneTokenResponseBody(), $response->raw);
-- 
GitLab