ericferon / glpi-webhook

GNU General Public License v2.0
20 stars 5 forks source link

Headers customization #18

Open olivierbicler opened 1 year ago

olivierbicler commented 1 year ago

Hi,

TLDR : It would be nice to allow customization of http headers sent + a pre-login phase

Long version : I want to use Webhook (latest version 1.0.12) to send a message to our instance of Rocket Chat. I don't actually understand how the multiple platforms are managed. I saw the 3 authentication methods (None / Basic Auth / Encoded Basic Auth), but none of them apply to Rocket Chat, since it requires two specific headers : X-Auth-Token and X-User-Id (source).

For my testing purposes, I used secret field for X-Auth-Token and user field for X-User-Id, and I didn't bother to add a new Auth Method, so I simply added following lines in inc/notificationeventwebhook.class.php :

case 1: // No Authentication
   $headers = 
      [
         'Content-type: application/json',
         'X-Auth-Token: '.$webhook_infos['additionnaloption']['secret'],
         'X-User-Id: '.$webhook_infos['additionnaloption']['user']
      ];
      break;

I don't know how long token is valid, so I'm fully aware this piece of code is messy (not to mention I broke the "no authentication" case). A better approach would be to log in first, get token, and then post message.

I'm surprised this problem doesn't occur more often : it seems to me each platform has its own method, and headers can't literally be anything. Since follow all platforms and all possible actions and update the code accordingly is impossible, I think it would be a great addition if user could set http headers.

However, it would solve half of the problem with rocket chat, since it requires to be logged in before the message posting. I'm not quite sure it would address every problem, but an option allowing us to login (login url + name of token field to be used in "real" request) in a first place may be useful.

For the record, there is a wrapper to use rocket chat. Not quite sure it's useful (post message in chat and send message to single user are the two only functions which make sense IMHO), but an option to use a library could be a possible solution too.

Smiley-k commented 1 year ago

You need to configure integration in RC section to get web hook link (its lifetime is not limited) and you can perform sending from external systems to RC.

ftoledo commented 10 months ago

I made this simple and waful patch as proof of concept to use the css field from the template for set custom headers:

--- notificationeventwebhook.class.php.orig 2024-01-28 17:27:12.101130410 -0300
+++ notificationeventwebhook.class.php  2024-01-28 17:30:04.206960358 -0300
@@ -29,7 +29,11 @@ if (!defined('GLPI_ROOT')) {
    die("Sorry. You can't access this file directly");
 }

-function str_replace_deep($search, $replace, $subject) {
+function str_replace_deep($search, $replace, $subject) {    
+    if (is_null($subject))
+    {
+        return '';
+    } 
     if (is_array($subject))
     {
         foreach($subject as &$oneSubject)
@@ -159,6 +163,8 @@ static public function extraRaise($param
                            $url = $webhook_infos['additionnaloption']['address']; 
                            $url = NotificationTemplate::process($webhook_infos['additionnaloption']['address'], $data); // substitute variables in url
                            $url = str_replace(["\n", "\r", "\t"], ['', '', ''], htmlentities($url)); // translate HTML-significant characters and suppress remaining escape characters
+                           $css = $template->fields['css'];
+
                            if ($template_datas = $template->getByLanguage($webhook_infos['language']))
                            {
                                $template_datas  = Sanitizer::unsanitize($template_datas); // unescape html from DB
@@ -173,7 +179,8 @@ static public function extraRaise($param

                                $content = NotificationTemplate::process($template, $data);
                                $curl = curl_init($url);
-                               $secrettype = $webhook_infos['additionnaloption']['plugin_webhook_secrettypes_id'];    
+                               $secrettype = $webhook_infos['additionnaloption']['plugin_webhook_secrettypes_id'];
+                               $headers = array();    
                                switch ($secrettype)
                                {
                                    case 1: // No Authentication
@@ -202,7 +209,13 @@ static public function extraRaise($param
                                    case 4: // JSON Web Token
                                    break;
                                }
-                               
+                               //add custom headers from css -> json
+                               $json_data = json_decode($css);
+                               if ($json_data != NULL) {
+                                   foreach ($json_data as $k => $v) {
+                                       array_push($headers,$k . ': ' . $v);
+                                   }
+                               }
                                curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
                                curl_setopt($curl, CURLOPT_HEADER, false);
                                curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

so, you can add a json (key -> value) on that field and will be appended to headers array

imagen

webbokhook debug log:

2024-01-28 17:24:08 [12@host]
Debug : call to URL http://localhost/test.php returned status 200 and response 
HTTP Headers : Array
(
    [0] => Content-type: application/json
    [1] => test: hola
    [2] => char: asdasasd
    [3] => auth: peapsdasdsdasdfasdfasd
)

POST Content : {
 "json": "prueba",
 "tikcet" : 0000039"
}

obviously it is better to add a new string field as an additional option that contains this instead of using the core css field

ftoledo commented 10 months ago

Just ideas:

Going back to @olivierbicler discussion regarding the pre-login phase, perhaps you can think of multiple steps (instead of two step ony) for example thinking about the same workflow that the glpi api uses that need almost three (init session, do some work, kill session).

You have to send a first post with the "authorization token" and this returns the "session token" in the results that must be put in the following calls to the api example:

step 1: plugin must send post with headers / body according to the configuration. If data is received (a json or headers), one of those fields must be able to be saved in a variable (or several variables)

step 2 The plugin can send a second call using any of the variables obtained from step 1 as part of the headers or body

I use another software called typebot, This has a very good approach of how to execute the entire process of calling an external API with several steps example:

imagen

first step:

imagen

here pass the "get session"

imagen

set several headers:

imagen

The values returned by the call can be saved in variables imagen

then a second step :

yo can use the variable that was returned from step 1

imagen