Skip to content

Commit 8c59088

Browse files
committed
Agent communication protocol
Add brotli compression support Add accepted encodings Add common headers for agent Use common headers on inventory requests Work with new protocol
1 parent 118334c commit 8c59088

File tree

7 files changed

+644
-55
lines changed

7 files changed

+644
-55
lines changed

.composer-require-checker.config.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,11 @@
6262
"sodium_crypto_aead_chacha20poly1305_ietf_keygen", "SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES", "sodium_crypto_aead_xchacha20poly1305_ietf_encrypt", "sodium_crypto_aead_xchacha20poly1305_ietf_decrypt",
6363

6464
"//xhprof",
65-
"XHPROF_FLAGS_NO_BUILTINS", "XHPROF_FLAGS_CPU", "XHPROF_FLAGS_MEMORY"
65+
"XHPROF_FLAGS_NO_BUILTINS", "XHPROF_FLAGS_CPU", "XHPROF_FLAGS_MEMORY",
66+
67+
"//brotli compression",
68+
"brotli_uncompress",
69+
"brotli_compress"
6670
],
6771
"scan-files": [
6872
"ajax/**/*.php",

front/inventory.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
include ('../inc/includes.php');
3636

3737
$inventory_request = new Request();
38-
38+
$inventory_request->handleHeaders();
3939

4040
$handle = true;
4141
if (isset($_GET['refused'])) {
@@ -61,10 +61,10 @@
6161
$redirect_url = $refused->handleInventoryRequest($inventory_request);
6262
Html::redirect($redirect_url);
6363
} else {
64-
header('Content-Type: ' . $inventory_request->getContentType());
65-
header('Cache-Control: no-cache,no-store');
66-
header('Pragma: no-cache');
67-
header('Connection: close');
68-
64+
$headers = $inventory_request->getHeaders(true);
65+
http_response_code($inventory_request->getHttpResponseCode());
66+
foreach ($headers as $key => $value) {
67+
header(sprintf('%1$s: %2$s', $key, $value));
68+
}
6969
echo $inventory_request->getResponse();
7070
}
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
<?php
2+
/**
3+
* ---------------------------------------------------------------------
4+
* GLPI - Gestionnaire Libre de Parc Informatique
5+
* Copyright (C) 2015-2021 Teclib' and contributors.
6+
*
7+
* http://glpi-project.org
8+
*
9+
* based on GLPI - Gestionnaire Libre de Parc Informatique
10+
* Copyright (C) 2003-2014 by the INDEPNET Development Team.
11+
*
12+
* ---------------------------------------------------------------------
13+
*
14+
* LICENSE
15+
*
16+
* This file is part of GLPI.
17+
*
18+
* GLPI is free software; you can redistribute it and/or modify
19+
* it under the terms of the GNU General Public License as published by
20+
* the Free Software Foundation; either version 2 of the License, or
21+
* (at your option) any later version.
22+
*
23+
* GLPI is distributed in the hope that it will be useful,
24+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
25+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26+
* GNU General Public License for more details.
27+
*
28+
* You should have received a copy of the GNU General Public License
29+
* along with GLPI. If not, see <http://www.gnu.org/licenses/>.
30+
* ---------------------------------------------------------------------
31+
*/
32+
33+
namespace Glpi\Agent\Communication\Headers;
34+
35+
use ReflectionClass;
36+
use ReflectionProperty;
37+
38+
class Common {
39+
//Global headers
40+
/**
41+
* "Content-Type" HTTP header
42+
*
43+
* @var string
44+
*/
45+
protected $content_type;
46+
47+
/**
48+
* "Accept" HTTP header
49+
*
50+
* Must follow RFC7231 - https://tools.ietf.org/html/rfc7231#page-38
51+
*
52+
* @var string
53+
*/
54+
protected $accept;
55+
56+
/**
57+
* "Cache-Control" HTTP header
58+
* Required
59+
*
60+
* @var string
61+
*/
62+
protected $cache_control = 'no-cache,no-store';
63+
64+
/**
65+
* "Connection" HTTP header
66+
* Required
67+
*
68+
* @var string
69+
*/
70+
protected $connection = 'close';
71+
72+
/**
73+
* "Pragma" HTTP header
74+
* Required
75+
*
76+
* Avoid any caching done by the server
77+
*
78+
* @var string
79+
*/
80+
protected $pragma = 'no-cache';
81+
82+
//GLPI agent headers
83+
/**
84+
* "GLPI-Agent-ID" HTTP header
85+
* Required
86+
*
87+
* Plain text UUID which can be reduced in a 128 bits raw id (ex. 3a609a2e-947f-4e6a-9af9-32c024ac3944)
88+
*
89+
* @var string
90+
*/
91+
protected $glpi_agent_id;
92+
93+
/**
94+
* "GLPI-Request-ID" HTTP header
95+
*
96+
* 8 digit hexadecimal string in higher case like 42E6A9AF1
97+
*
98+
* @var string
99+
*/
100+
protected $glpi_request_id;
101+
102+
/**
103+
* "GLPI-CryptoKey-ID" HTTP header
104+
*
105+
* List of agentid separated by commas
106+
*
107+
* @var string
108+
*/
109+
protected $glpi_cryptokey_id;
110+
111+
/**
112+
* "GLPI-Proxy-ID" HTTP header
113+
*
114+
* List of agentid separated by commas
115+
*
116+
* @var string
117+
*/
118+
protected $glpi_proxy_id;
119+
120+
public function getRequireds(): array {
121+
return [
122+
'content_type',
123+
'pragma',
124+
'glpi_agent_id',
125+
'cache_control',
126+
'connection'
127+
];
128+
}
129+
130+
public function getHeadersNames(): array {
131+
return [
132+
'glpi_cryptokey_id' => 'GLPI-CryptoKey-ID'
133+
];
134+
}
135+
136+
/**
137+
* Get HTTP headers
138+
*
139+
* @param boolean $legacy Set to true to shunt required headers checks
140+
*
141+
* @return array
142+
*/
143+
public function getHeaders($legacy = false): array {
144+
//parse class attributes and normalize key name
145+
$reflect = new ReflectionClass($this);
146+
$props = $reflect->getProperties(ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED);
147+
148+
$headers = [];
149+
150+
foreach ($props as $prop) {
151+
$propname = $prop->getName();
152+
$headername = $this->getHeaderName($propname);
153+
if (!empty($this->$propname)) {
154+
$headers[$headername] = $this->$propname;
155+
} else if (in_array($propname, $this->getRequireds()) && $legacy === false) {
156+
throw new \RuntimeException(
157+
sprintf(
158+
'%1$s HTTP header is mandatory!',
159+
$headername
160+
)
161+
);
162+
}
163+
}
164+
165+
return $headers;
166+
}
167+
168+
/**
169+
* Get header value
170+
*
171+
* @param string $name Header name
172+
*/
173+
public function getHeader($name) {
174+
$propname = strtolower(str_replace('-', '_', $name));
175+
176+
//TODO: check header does exists, and has expected value
177+
return $this->$propname;
178+
}
179+
180+
/**
181+
* Return HTTP header name from class property name
182+
*
183+
* @param string $prop Property name
184+
*
185+
* @return string
186+
*/
187+
public final function getHeaderName($prop): string {
188+
$name = $prop;
189+
190+
if (isset($this->getHeadersNames()[$prop])) {
191+
return $this->getHeadersNames()[$prop];
192+
}
193+
194+
$exploded = explode('_', $prop);
195+
foreach ($exploded as &$entry) {
196+
$lowered = strtolower($entry);
197+
switch ($lowered) {
198+
case 'glpi':
199+
case 'id':
200+
$entry = strtoupper($entry);
201+
break;
202+
default:
203+
$entry = ucfirst($entry);
204+
break;
205+
}
206+
}
207+
208+
return implode('-', $exploded);
209+
}
210+
211+
/**
212+
* Set multiple HTTP header values at once
213+
*
214+
* @param $headers Array of HTTP header name as key and value
215+
*
216+
* @return $this
217+
*/
218+
public function setHeaders($headers): self {
219+
foreach ($headers as $header => $value) {
220+
$this->setHeader($header, $value);
221+
}
222+
223+
return $this;
224+
}
225+
/**
226+
* Set HTTP header value
227+
*
228+
* @param $name HTTP header name
229+
* @param $value Value to set
230+
*
231+
* @return $this
232+
*/
233+
public function setHeader($name, $value): self {
234+
$propname = strtolower(str_replace('-', '_', $name));
235+
236+
if (!property_exists($this, $propname)) {
237+
trigger_error(
238+
sprintf(
239+
"Unknown header %1$s",
240+
$propname
241+
),
242+
E_USER_ERROR
243+
);
244+
}
245+
$this->$propname = $value;
246+
247+
return $this;
248+
}
249+
250+
/**
251+
* Is header set
252+
*
253+
* @param string $name Property name
254+
*
255+
* @return bool
256+
*/
257+
public function hasHeader($name): bool {
258+
$propname = strtolower(str_replace('-', '_', $name));
259+
return (isset($this->$propname) && !empty($this->$propname));
260+
}
261+
}

inc/inventory/inventory.class.php

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -146,27 +146,59 @@ public function extractMetadata() :array {
146146
throw new \RuntimeException(print_r($this->getErrors(), true));
147147
}
148148

149-
$this->metadata = [
150-
'deviceid' => $this->raw_data->deviceid,
151-
'version' => $this->raw_data->content->versionclient,
152-
'itemtype' => $this->raw_data->itemtype ?? 'Computer'
153-
];
154-
155-
// Get tag if defined
156-
if (property_exists($this->raw_data->content, 'accountinfo')) {
157-
$ainfos = $this->raw_data->content->accountinfo;
158-
if (property_exists($ainfos, 'keyname')
159-
&& $ainfos->keyname == 'TAG'
160-
&& property_exists($ainfos, 'keyvalue')
161-
&& $ainfos->keyvalue != ''
162-
) {
163-
$this->metadata['tag'] = $ainfos->keyvalue;
149+
if (property_exists($this->raw_data, 'action')) {
150+
//new protocol
151+
$this->metadata = [
152+
'deviceid' => $this->raw_data->deviceid,
153+
'version' => $this->raw_data->version,
154+
'itemtype' => $this->raw_data->itemtype ?? 'Computer',
155+
];
156+
157+
// Get tag if defined
158+
$expecteds = ['action', 'name', 'installed-tasks', 'enabled-tasks', 'tag'];
159+
foreach ($expecteds as $expected) {
160+
if (property_exists($this->raw_data, $expected)) {
161+
$this->metadata[$expected] = $this->raw_data->{$expected};
162+
}
163+
}
164+
} else {
165+
//old protocol
166+
$this->metadata = [
167+
'deviceid' => $this->raw_data->deviceid,
168+
'version' => $this->raw_data->content->versionclient ?? $this->raw_data->version,
169+
'itemtype' => $this->raw_data->itemtype ?? 'Computer'
170+
];
171+
172+
// Get tag if defined
173+
if (property_exists($this->raw_data, 'tag')) {
174+
$this->metadata['tag'] = $this->raw_data->tag;
175+
} else if (property_exists($this->raw_data->content, 'accountinfo')) {
176+
$ainfos = $this->raw_data->content->accountinfo;
177+
if (property_exists($ainfos, 'keyname')
178+
&& $ainfos->keyname == 'TAG'
179+
&& property_exists($ainfos, 'keyvalue')
180+
&& $ainfos->keyvalue != ''
181+
) {
182+
$this->metadata['tag'] = $ainfos->keyvalue;
183+
}
164184
}
185+
165186
}
166187

167188
return $this->metadata;
168189
}
169190

191+
/**
192+
* CONTACT request from agent
193+
*/
194+
public function contact($data) {
195+
$this->raw_data = json_decode($data);
196+
$this->extractMetadata();
197+
//create/load agent
198+
$this->agent = new Agent();
199+
$this->agent->handleAgent($this->metadata);
200+
}
201+
170202
/**
171203
* Do inventory
172204
*

0 commit comments

Comments
 (0)