Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat(Inventory): Basic auth for Agent #17834

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions front/inventory.conf.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,13 @@
$conf->displayImportFiles($_FILES);
} elseif (isset($_POST['update'])) {
unset($_POST['update']);
$conf->saveConf($_POST);
Session::addMessageAfterRedirect(
__s('Configuration has been updated'),
false,
INFO
);
if ($conf->saveConf($_POST)) {
Session::addMessageAfterRedirect(
__s('Configuration has been updated'),
false,
INFO
);
}
Html::back();
} else {
$conf->display(['id' => 1]);
Expand Down
35 changes: 34 additions & 1 deletion src/Glpi/Agent/Communication/AbstractRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@
use DOMDocument;
use DOMElement;
use Glpi\Agent\Communication\Headers\Common;
use Glpi\Inventory\Conf;
use Glpi\Application\ErrorHandler;
use Glpi\Http\Request;
use Glpi\OAuth\Server;
use League\OAuth2\Server\Exception\OAuthServerException;
use Toolbox;
use GLPIKey;

/**
* Handle agent requests
Expand Down Expand Up @@ -207,7 +209,7 @@ public function handleHeaders()
public function handleRequest($data): bool
{
$auth_required = \Config::getConfigurationValue('inventory', 'auth_required');
if ($auth_required === 'client_credentials') {
if ($auth_required === Conf::CLIENT_CREDENTIALS) {
$request = new Request('POST', $_SERVER['REQUEST_URI'], $this->headers->getHeaders());
try {
$client = Server::validateAccessToken($request);
Expand All @@ -223,6 +225,37 @@ public function handleRequest($data): bool
}
}

if ($auth_required === Conf::BASIC_AUTH) {
$authorization_header = $this->headers->getHeader('Authorization');
if (is_null($authorization_header)) {
$this->setMode(self::JSON_MODE);
$this->headers->setHeader("www-authenticate", 'Basic realm="basic"');
$this->addError('Authorization header required to send an inventory', 401);
return false;
} else {
$allowed = false;
// if Authorization start with 'Basic'
if (preg_match('/^Basic\s+(.*)$/i', $authorization_header, $matches)) {
$inventory_login = \Config::getConfigurationValue('inventory', 'basic_auth_login');
$inventory_password = (new GLPIKey())
->decrypt(\Config::getConfigurationValue('inventory', 'basic_auth_password'));
$agent_credential = base64_decode($matches[1]);
list($agent_login, $agent_password) = explode(':', $agent_credential, 2);
if (
$inventory_login == $agent_login &&
$inventory_password == $agent_password
) {
$allowed = true;
}
}
if (!$allowed) {
$this->setMode(self::JSON_MODE);
$this->addError('Access denied. Wrong login or password for basic authentication.', 401);
return false;
}
}
}

// Some network inventories may request may contains lots of information.
// e.g. a Huawei S5720-52X-LI-AC inventory file may weigh 20MB,
// and GLPI will consume about 500MB of memory to handle it,
Expand Down
6 changes: 6 additions & 0 deletions src/Glpi/Agent/Communication/Headers/Common.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ class Common
*/
protected $authorization;

/**
* Define HTTP Authorization Header Authorization type
* @var string
*/
protected $www_authenticate;

public function getRequireds(): array
{
return [
Expand Down
78 changes: 75 additions & 3 deletions src/Glpi/Inventory/Conf.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
use Session;
use State;
use Toolbox;
use GLPIKey;
use wapmorgan\UnifiedArchive\UnifiedArchive;

/**
Expand Down Expand Up @@ -109,6 +110,10 @@ class Conf extends CommonGLPI

public const STALE_AGENT_ACTION_TRASHBIN = 2;

public const CLIENT_CREDENTIALS = 'client_credentials';

public const BASIC_AUTH = 'basic_auth';

public static $rightname = 'inventory';

const IMPORTFROMFILE = 1024;
Expand Down Expand Up @@ -384,26 +389,70 @@ public function showConfigForm()
echo '</div>';
echo "</td>";
echo "</tr>";

echo "<tr class='tab_bg_1'>";
echo "<td>";
echo "<i class='ti ti-cloud-lock me-2'></i>";
echo "<label for='auth'>" . __s('Authorization header') . "</label>";
echo "</td>";
echo "<td>";
Dropdown::showFromArray('auth_required', [
'none' => __('None'),
'client_credentials' => __('OAuth - Client credentials')
self::CLIENT_CREDENTIALS => __s('OAuth - Client credentials'),
self::BASIC_AUTH => __s('Basic Authentication'),
], [
'value' => $config['auth_required'] ?? 'none'
]);
echo "</td></tr>";
echo "<tr class='tab_bg_1' id='basic_auth_login_row'>";
echo "<td>";
echo "<i class='ti ti-abc me-2'></i>";
echo "<label for='basic_auth_login'>" . __s('Login') . "</label>";
cedric-anne marked this conversation as resolved.
Show resolved Hide resolved
echo "<span class='required'>*</span>";
echo "</label>";
echo "</td>";
echo "<td>";
echo Html::input("basic_auth_login", [
"value" => $config["basic_auth_login"],
]);
echo "</td>";
echo "</tr>";
echo "<tr class='tab_bg_1' id='basic_auth_password_row'>";
echo "<td>";
echo "<i class='ti ti-password me-2'></i>";
echo "<label for='basic_auth_password'>" . __s('Password') . "</label>";
cedric-anne marked this conversation as resolved.
Show resolved Hide resolved
echo "<span class='required'>*</span>";
echo "</label>";
echo "</td>";
echo "<td>";
echo Html::input("basic_auth_password", [
"value" => (new GLPIKey())->decrypt($config['basic_auth_password']),
"type" => "password",
]);
echo "</td>";
echo "</tr>";
echo Html::scriptBlock("
function toggleDisplayLoginInputs(select) {
let displayedInputs = false;
const selectedValue = $(select).val();
if (selectedValue == '" . self::BASIC_AUTH . "') {
displayedInputs = true;
}
$('#basic_auth_login_row').toggle(displayedInputs);
$('#basic_auth_password_row').toggle(displayedInputs);
}

const selectAuthHeader = $(`select[name='auth_required']`);
selectAuthHeader.change(function() {
toggleDisplayLoginInputs(this);
});

toggleDisplayLoginInputs(selectAuthHeader);
");
echo "<tr>";
echo "<th colspan='4'>";
echo __('Import options');
echo "</th>";
echo "</tr>";

echo "<tr class='tab_bg_1'>";
echo "<td>";
echo "<label for='import_volume'>";
Expand Down Expand Up @@ -1102,6 +1151,27 @@ public function saveConf(array $values)
$values['stale_agents_status_condition'] = ['all'];
}

if (isset($values['auth_required']) && $values['auth_required'] === Conf::BASIC_AUTH) {
if (
!empty($values['basic_auth_password']) &&
!empty($values['basic_auth_login'])
) {
$values['basic_auth_password'] = (new GLPIKey())->encrypt($values['basic_auth_password']);
} else {
Session::addMessageAfterRedirect(
__s("Basic Authentication is active. The login and/or password fields are missing."),
false,
ERROR
);
return false;
}
}

if (isset($values['auth_required']) && $values['auth_required'] !== Conf::BASIC_AUTH) {
$values['basic_auth_login'] = null;
$values['basic_auth_password'] = null;
}

$to_process = [];
foreach ($defaults as $prop => $default_value) {
$to_process[$prop] = $values[$prop] ?? $default_value;
Expand Down Expand Up @@ -1226,6 +1296,8 @@ public static function getDefaults(): array
'stale_agents_status_condition' => exportArrayToDB(['all']),
'import_env' => 0,
'auth_required' => 'none',
'basic_auth_login' => '',
'basic_auth_password' => ''
];
}

Expand Down
Loading