new api 2.0

This commit is contained in:
daniel 2024-02-07 08:53:54 +01:00
parent b80f7bdeb1
commit 8cfb04d243
28 changed files with 1032 additions and 297 deletions

View File

@ -1,6 +1,8 @@
<?php
use PandoraFMS\Modules\Shared\Enums\HttpCodesEnum;
use PandoraFMS\Modules\Shared\Middlewares\AclListMiddleware;
use PandoraFMS\Modules\Shared\Middlewares\UserTokenMiddleware;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
@ -22,38 +24,24 @@ return function (App $app, ContainerInterface $container) {
$app,
$container
) {
global $config;
$authorization = $request->getHeader('Authorization');
$user = false;
if (empty($authorization) === false && empty($authorization[0]) === false) {
$bearer = explode('Bearer ', $authorization[0]);
if (empty($bearer) === false && isset($bearer[1]) === true) {
$user = \db_get_value(
'id_user',
'tusuario',
'api_token',
$bearer[1]
);
if ($user !== false) {
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
$_SESSION['id_usuario'] = $user;
$config['id_user'] = $user;
if (session_status() === PHP_SESSION_ACTIVE) {
session_write_close();
}
}
}
}
if (empty($user) === true) {
$ipOrigin = $_SERVER['REMOTE_ADDR'];
$aclListMiddleware = $container->get(AclListMiddleware::class);
if ($aclListMiddleware->check($ipOrigin) === false) {
$response = $app->getResponseFactory()->createResponse();
$response->getBody()->write(
json_encode(['error' => 'You need to be authenticated to perform this action'])
json_encode(['error' => __('IP %s is not in ACL list', $ipOrigin)])
);
$errorCode = HttpCodesEnum::UNAUTHORIZED;
$newResponse = $response->withStatus($errorCode);
return $newResponse;
}
$userTokenMiddleware = $container->get(UserTokenMiddleware::class);
if ($userTokenMiddleware->check($request) === false) {
$response = $app->getResponseFactory()->createResponse();
$response->getBody()->write(
json_encode(['error' => __('You need to be authenticated to perform this action')])
);
$errorCode = HttpCodesEnum::UNAUTHORIZED;
@ -66,7 +54,7 @@ return function (App $app, ContainerInterface $container) {
} catch (\Throwable $th) {
$response = $app->getResponseFactory()->createResponse();
$response->getBody()->write(
json_encode(['error' => 'Invalid License'])
json_encode(['error' => __('Invalid License')])
);
$errorCode = HttpCodesEnum::UNAUTHORIZED;

View File

@ -1985,15 +1985,18 @@
"nullable": true
},
"validity": {
"description": "validity of the token",
"description": "Date until which tocken is valid, if it is void it will never expire",
"type": "string",
"default": null,
"example": "2023-02-21 08:34:16",
"nullable": true
},
"lastUsage": {
"description": "last_usage of the token",
"type": "string",
"default": null,
"readOnly": true,
"example": "2023-02-21 08:34:16",
"nullable": true
}
},

View File

@ -250,6 +250,11 @@ if ($access_console_node === true) {
$sub['godmode/users/profile_list']['id'] = 'Profile_management';
}
if ((bool) check_acl($config['id_user'], 0, 'PM') === true) {
$sub['godmode/users/token_list']['text'] = __('Token management');
$sub['godmode/users/token_list']['id'] = 'token_management';
}
if (empty($sub) === false) {
$menu_godmode['gusuarios']['sub'] = $sub;
$menu_godmode['gusuarios']['text'] = __('Profiles');

View File

@ -44,57 +44,9 @@ $new_profile = (bool) get_parameter('new_profile');
$id_profile = (int) get_parameter('id');
// Header.
if (is_metaconsole() === false) {
$buttons = [
'user' => [
'active' => false,
'text' => '<a href="index.php?sec=gusuarios&sec2=godmode/users/user_list&tab=user&pure='.$pure.'">'.html_print_image(
'images/user.svg',
true,
[
'title' => __('User management'),
'class' => 'invert_filter main_menu_icon',
]
).'</a>',
],
'profile' => [
'active' => false,
'text' => '<a href="index.php?sec=gusuarios&sec2=godmode/users/profile_list&tab=profile&pure='.$pure.'">'.html_print_image(
'images/suitcase@svg.svg',
true,
[
'title' => __('Profile management'),
'class' => 'invert_filter main_menu_icon',
]
).'</a>',
],
];
$buttons[$tab]['active'] = true;
$profile = db_get_row('tperfil', 'id_perfil', $id_profile);
ui_print_standard_header(
__('Edit profile %s', $profile['name']),
'images/user.svg',
false,
'configure_profiles_tab',
true,
$buttons,
[
[
'link' => '',
'label' => __('Profiles'),
],
[
'link' => '',
'label' => __('Manage users'),
],
[
'link' => ui_get_full_url('index.php?sec=gusuarios&sec2=godmode/users/profile_list&tab=profile'),
'label' => __('User Profile management'),
],
]
);
$title = __('Edit profile %s', $profile['name']);
user_print_header($pure, $tab, $title);
$sec2 = 'gusuarios';
} else {
user_meta_print_header();

View File

@ -0,0 +1,202 @@
<?php
/**
* Configure Token.
*
* @category Users
* @package Pandora FMS
* @subpackage Community
* @version 1.0.0
* @license See below
*
*
* Pandora FMS - https://pandorafms.com
* ==================================================
* Copyright (c) 2005-2024 Pandora FMS
* Please see https://pandorafms.com/community/ for full contribution list
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation for version 2.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
// Global variables.
global $config;
check_login();
require_once $config['homedir'].'/include/functions_token.php';
enterprise_include_once('meta/include/functions_users_meta.php');
// Get parameters.
$tab = get_parameter('tab', 'token');
$pure = get_parameter('pure', 0);
$id_token = (int) get_parameter('id_token');
// Header.
if (is_metaconsole() === false) {
user_print_header($pure, $tab);
$sec2 = 'gusuarios';
} else {
user_meta_print_header();
$sec2 = 'advanced';
}
$url_list = 'index.php?sec='.$sec;
$url_list .= '&sec2=godmode/users/token_list';
$url_list .= '&pure='.$pure;
// Edit token.
if (empty($id_token) === true) {
$label = '';
$validity = '';
$page_title = __('Create token');
} else {
try {
$token = get_user_token($id_token);
} catch (\Exception $e) {
ui_print_error_message(
__('There was a problem get token, %s', $e->getMessage())
);
}
}
$table = new StdClass();
$table->width = '100%';
$table->class = 'databox filters';
$table->data = [];
$table->rowspan = [];
$table->colspan = [];
if (is_metaconsole() === true) {
$table->class = 'databox data';
if (empty($id_token) === true) {
$table->head[0] = __('Update Profile');
} else {
$table->head[0] = __('Create Profile');
}
$table->head_colspan[0] = 4;
$table->headstyle[0] = 'text-align: center';
}
$table->data[0][0] = __('Token label');
$table->data[0][1] = html_print_input_text(
'label',
$token['label'],
'',
50,
255,
true
);
if ((bool) users_is_admin() === true) {
$table->data[0][2] = __('User');
$table->data[0][3] = 'aaaa';
}
$expiration_date = null;
$expiration_time = null;
if (empty($token['validity']) === false) {
$array_date = explode(' ', io_safe_output($token['validity']));
if (is_array($array_date) === true) {
$expiration_date = $array_date[0];
if (isset($array_date[1]) === true
&& empty($array_date[1]) === false
) {
$expiration_time = $array_date[1];
}
}
}
$table->data[1][0] = __('Expiration');
$table->data[1][1] = html_print_input_text(
'date-expiration',
$expiration_date,
'',
50,
255,
true
);
$table->data[1][2] = __('Expiration Time');
$table->data[1][3] = html_print_input_text(
'time-expiration',
$expiration_time,
'',
50,
255,
true
);
echo '<form class="max_floating_element_size" method="post" action="'.$url_list.'">';
html_print_table($table);
$actionButtons = [];
if (empty($id_token) === true) {
$actionButtons[] = html_print_submit_button(
__('Create'),
'crt',
false,
['icon' => 'wand'],
true
);
html_print_input_hidden('create_token', 1);
} else {
$actionButtons[] = html_print_submit_button(
__('Update'),
'upd',
false,
['icon' => 'update'],
true
);
html_print_input_hidden('id_token', $id_token);
html_print_input_hidden('update_token', 1);
}
$actionButtons[] = html_print_go_back_button(
ui_get_full_url($url_list),
['button_class' => ''],
true
);
html_print_action_buttons(
implode('', $actionButtons),
['type' => 'form_action']
);
echo '</form>';
ui_include_time_picker();
ui_require_jquery_file('ui.datepicker-'.get_user_language(), 'include/javascript/i18n/');
?>
<script type="text/javascript" language="javascript">
$(document).ready (function () {
$('#text-date-expiration').datepicker({
dateFormat: 'yy-mm-dd',
changeMonth: true,
changeYear: true,
showAnim: 'slideDown'
});
$('[id^=text-time-expiration]').timepicker({
showSecond: true,
timeFormat: '<?php echo TIME_FORMAT_JS; ?>',
timeOnlyTitle: '<?php echo __('Choose time'); ?>',
timeText: '<?php echo __('Time'); ?>',
hourText: '<?php echo __('Hour'); ?>',
minuteText: '<?php echo __('Minute'); ?>',
secondText: '<?php echo __('Second'); ?>',
currentText: '<?php echo __('Now'); ?>',
closeText: '<?php echo __('Close'); ?>'
});
});
</script>

View File

@ -246,60 +246,9 @@ if (is_metaconsole() === true) {
user_meta_print_header();
$sec = 'advanced';
} else {
if ((bool) check_acl($config['id_user'], 0, 'UM') === false) {
$buttons = [];
} else {
$buttons = [
'user' => [
'active' => false,
'text' => '<a href="index.php?sec=gusuarios&sec2=godmode/users/user_list&tab=user&pure='.$pure.'">'.html_print_image(
'images/user.svg',
true,
[
'title' => __('User management'),
'class' => 'invert_filter main_menu_icon',
]
).'</a>',
],
'profile' => [
'active' => false,
'text' => '<a href="index.php?sec=gusuarios&sec2=godmode/users/profile_list&tab=profile&pure='.$pure.'">'.html_print_image(
'images/suitcase@svg.svg',
true,
[
'title' => __('Profile management'),
'class' => 'invert_filter main_menu_icon',
]
).'</a>',
],
];
$buttons[$tab]['active'] = true;
}
$edit_user = get_parameter('edit_user');
ui_print_standard_header(
($edit_user) ? sprintf('%s [ %s ]', __('Update User'), $id) : __('Create User'),
'images/gm_users.png',
false,
'',
true,
$buttons,
[
[
'link' => '',
'label' => __('Profiles'),
],
[
'link' => ui_get_full_url('index.php?sec=gusuarios&sec2=godmode/users/user_list'),
'label' => __('Manage users'),
],
[
'link' => '',
'label' => __('User Detail Editor'),
],
]
);
$title = ($edit_user) ? sprintf('%s [ %s ]', __('Update User'), $id) : __('Create User');
user_print_header($pure, $tab, $title);
$sec = 'gusuarios';
}

View File

@ -51,52 +51,7 @@ $pure = get_parameter('pure', 0);
// Header.
if (is_metaconsole() === false) {
$buttons = [
'user' => [
'active' => false,
'text' => '<a href="index.php?sec=gusuarios&sec2=godmode/users/user_list&tab=user&pure='.$pure.'">'.html_print_image(
'images/user.svg',
true,
[
'title' => __('User management'),
'class' => 'invert_filter main_menu_user',
]
).'</a>',
],
'profile' => [
'active' => false,
'text' => '<a href="index.php?sec=gusuarios&sec2=godmode/users/profile_list&tab=profile&pure='.$pure.'">'.html_print_image(
'images/suitcase@svg.svg',
true,
[
'title' => __('Profile management'),
'class' => 'invert_filter main_menu_user',
]
).'</a>',
],
];
$buttons[$tab]['active'] = true;
// Header.
ui_print_standard_header(
__('User Profile management'),
'images/user.svg',
false,
'profile_tab',
false,
$buttons,
[
[
'link' => '',
'label' => __('Profiles'),
],
[
'link' => '',
'label' => __('Manage users'),
],
]
);
user_print_header($pure, $tab);
$sec = 'gusuarios';
} else {
user_meta_print_header();

View File

@ -0,0 +1,193 @@
<?php
/**
* Tokens.
*
* @category Users
* @package Pandora FMS
* @subpackage Community
* @version 1.0.0
* @license See below
*
* ______ ___ _______ _______ ________
* | __ \.-----.--.--.--| |.-----.----.-----. | ___| | | __|
* | __/| _ | | _ || _ | _| _ | | ___| |__ |
* |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______|
*
* ============================================================================
* Copyright (c) 2005-2024 Pandora FMS
* Please see https://pandorafms.com/community/ for full contribution list
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation for version 2.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* ============================================================================
*/
// Load global vars.
global $config;
check_login();
require_once $config['homedir'].'/include/functions_token.php';
require_once $config['homedir'].'/include/functions_users.php';
enterprise_include_once('meta/include/functions_users_meta.php');
$tab = get_parameter('tab', 'token');
$pure = get_parameter('pure', 0);
// Header.
if (is_metaconsole() === false) {
user_print_header($pure, $tab);
$sec = 'gusuarios';
} else {
user_meta_print_header();
$sec = 'advanced';
}
$edit_url = 'index.php?sec='.$sec;
$edit_url .= '&sec2=godmode/users/configure_token';
$edit_url .= '&pure='.$pure;
$delete_token = (bool) get_parameter('delete_token');
$create_token = (bool) get_parameter('create_token');
$update_token = (bool) get_parameter('update_token');
$id_token = (int) get_parameter('id_token');
// Token deletion.
if ($delete_token === true) {
try {
delete_user_token($id_token);
ui_print_success_message(__('Successfully deleted'));
} catch (\Exception $e) {
ui_print_error_message(
__('There was a problem deleting token, %s', $e->getMessage())
);
}
}
$tokenMsg = '';
if ($create_token === true || $update_token === true) {
$label = get_parameter('label', null);
$expirationDate = get_parameter('date-expiration', null);
$expirationTime = get_parameter('time-expiration', null);
$validity = null;
if (empty($expirationDate) === false) {
$validity = $expirationDate;
if (empty($expirationTime) === false) {
$validity .= ' '.$expirationTime;
}
}
$values = [
'label' => $label,
'validity' => $validity,
];
// Create token.
if ($create_token === true) {
try {
$token = create_user_token($values);
$smgInfo = __('This code will appear only once, please keep it in a safe place');
$smgInfo .= '.</br>';
$smgInfo .= __('If you lose the code, you will only able to delete it and create a new one');
$smgInfo .= '.</br></br>';
$smgInfo .= '<i>';
$smgInfo .= $token['token'];
$smgInfo .= '</i>';
$tokenMsg = ui_print_info_message($smgInfo, '', true);
ui_print_success_message(__('Successfully created'));
} catch (\Exception $e) {
ui_print_error_message(
__('There was a problem creating this token, %s', $e->getMessage())
);
}
}
// Update token.
if ($update_token === true) {
try {
$token = update_user_token($id_token, $values);
ui_print_success_message(__('Successfully updated'));
} catch (\Exception $e) {
ui_print_error_message(
__('There was a problem updating this token, %s', $e->getMessage())
);
}
}
}
try {
$columns = [
'label',
'validity',
'lastUsage',
'options',
];
$column_names = [
__('Label'),
__('Expiration'),
__('Last usage'),
[
'text' => __('Options'),
'class' => 'w20px table_action_buttons',
],
];
$tableId = 'token_table';
// Load datatables user interface.
ui_print_datatable(
[
'id' => $tableId,
'class' => 'info_table',
'style' => 'width: 100%',
'columns' => $columns,
'column_names' => $column_names,
'ajax_url' => 'include/ajax/token',
'ajax_data' => ['list_user_tokens' => 1],
'extra_html' => $tokenMsg,
'no_sortable_columns' => [ -1 ],
'order' => [
'field' => 'label',
'direction' => 'asc',
],
'search_button_class' => 'sub filter float-right',
'form' => [
'inputs' => [
[
'label' => __('Free search'),
'type' => 'text',
'class' => 'w25p',
'id' => 'freeSearch',
'name' => 'freeSearch',
],
],
],
'filter_main_class' => 'box-flat white_table_graph fixed_filter_bar',
'dom_elements' => 'lftpB',
]
);
} catch (Exception $e) {
echo $e->getMessage();
}
echo '<form method="post" action="'.$edit_url.'">';
html_print_action_buttons(
html_print_submit_button(
__('Create Token'),
'crt',
false,
['icon' => 'next'],
true
),
[
'type' => 'data_table',
'class' => 'fixed_action_buttons',
]
);
echo '</form>';

View File

@ -240,73 +240,10 @@ if (is_metaconsole() === true) {
user_meta_print_header();
$sec = 'advanced';
} else {
if (check_acl($config['id_user'], 0, 'PM')) {
$buttons = [
'user' => [
'active' => false,
'text' => '<a href="index.php?sec=gusuarios&sec2=godmode/users/user_list&tab=user&pure='.$pure.'">'.html_print_image(
'images/user.svg',
true,
[
'title' => __('User management'),
'class' => 'invert_filter main_menu_icon',
]
).'</a>',
],
'profile' => [
'active' => false,
'text' => '<a href="index.php?sec=gusuarios&sec2=godmode/users/profile_list&tab=profile&pure='.$pure.'">'.html_print_image(
'images/suitcase@svg.svg',
true,
[
'title' => __('Profile management'),
'class' => 'invert_filter main_menu_icon',
]
).'</a>',
],
];
} else {
$buttons = [
'user' => [
'active' => false,
'text' => '<a href="index.php?sec=gusuarios&sec2=godmode/users/user_list&tab=user&pure='.$pure.'">'.html_print_image(
'images/user.svg',
true,
[
'title' => __('User management'),
'class' => 'invert_filter main_menu_icon',
]
).'</a>',
],
];
}
$buttons[$tab]['active'] = true;
// Header.
ui_print_standard_header(
__('Users management'),
'images/user.svg',
false,
'',
false,
$buttons,
[
[
'link' => '',
'label' => __('Profiles'),
],
[
'link' => '',
'label' => __('Manage users'),
],
]
);
user_print_header($pure, $tab);
$sec = 'gusuarios';
}
$disable_user = get_parameter('disable_user', false);
$delete_user = (bool) get_parameter('user_del', false);

View File

@ -0,0 +1,164 @@
<?php
/**
* Tokens ajax.
*
* @category Users
* @package Pandora FMS
* @subpackage Community
* @version 1.0.0
* @license See below
*
* ______ ___ _______ _______ ________
* | __ \.-----.--.--.--| |.-----.----.-----. | ___| | | __|
* | __/| _ | | _ || _ | _| _ | | ___| |__ |
* |___| |___._|__|__|_____||_____|__| |___._| |___| |__|_|__|_______|
*
* ============================================================================
* Copyright (c) 2005-2024 Pandora FMS
* Please see https://pandorafms.com/community/ for full contribution list
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation for version 2.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* ============================================================================
*/
$list_user_tokens = (bool) get_parameter('list_user_tokens');
if ($list_user_tokens === true) {
global $config;
// Datatables offset, limit and order.
$filter = get_parameter('filter', []);
$page = (int) get_parameter('start', 0);
$pageSize = (int) get_parameter('length', $config['block_size']);
$orderBy = get_datatable_order(true);
$sortField = ($orderBy['field'] ?? null);
$sortDirection = ($orderBy['direction'] ?? null);
try {
ob_start();
include_once $config['homedir'].'/include/functions_token.php';
if (isset($filter['form_token_table_search_bt']) === true) {
unset($filter['form_token_table_search_bt']);
}
$return = list_user_tokens(
($page / $pageSize),
$pageSize,
$sortField,
strtoupper($sortDirection),
$filter
);
if (empty($return['data']) === false) {
// Format end of life date.
$return['data'] = array_map(
function ($item) use ($config) {
$itemArray = $item->toArray();
$sec = 'gusuarios';
if (is_metaconsole() === true) {
$sec = 'advanced';
}
$edit_url = 'index.php?sec='.$sec;
$edit_url .= '&sec2=godmode/users/configure_token&pure=0';
$edit_url .= '&id_token='.$itemArray['idToken'];
$delete_url = 'index.php?sec='.$sec;
$delete_url .= '&sec2=godmode/users/token_list';
$delete_url .= '&pure=0&delete_token=1';
$delete_url .= '&id_token='.$itemArray['idToken'];
$itemArray['label'] = html_print_anchor(
[
'href' => $edit_url,
'content' => $itemArray['label'],
],
true
);
if (empty($itemArray['validity']) === true) {
$itemArray['validity'] = __('Never');
} else {
$itemArray['validity'] = date($config['date_format'], strtotime($itemArray['validity']));
}
if (empty($itemArray['lastUsage']) === true) {
$itemArray['lastUsage'] = __('Never');
} else {
$itemArray['lastUsage'] = human_time_comparation($itemArray['lastUsage']);
}
$itemArray['options'] = '<div class="table_action_buttons float-right">';
$itemArray['options'] .= html_print_anchor(
[
'href' => $edit_url,
'content' => html_print_image(
'images/edit.svg',
true,
[
'title' => __('Show'),
'class' => 'main_menu_icon invert_filter',
]
),
],
true
);
$itemArray['options'] .= html_print_anchor(
[
'href' => $delete_url,
'onClick' => 'if (!confirm(\' '.__('Are you sure?').'\')) return false;',
'content' => html_print_image(
'images/delete.svg',
true,
[
'title' => __('Delete'),
'class' => 'invert_filter main_menu_icon',
]
),
],
true
);
$itemArray['options'] .= '</div>';
return $itemArray;
},
$return['data']
);
}
// Datatables format: RecordsTotal && recordsfiltered.
echo json_encode(
[
'data' => $return['data'],
'recordsTotal' => $return['paginationData']['totalRegisters'],
'recordsFiltered' => $return['paginationData']['totalRegisters'],
]
);
// Capture output.
$response = ob_get_clean();
} catch (Exception $e) {
echo json_encode(['error' => $e->getMessage()]);
return;
}
// If not valid, show error with issue.
json_decode($response);
if (json_last_error() == JSON_ERROR_NONE) {
// If valid dump.
echo $response;
} else {
echo json_encode(
['error' => $response]
);
}
return;
}

View File

@ -17,7 +17,9 @@
* @subpackage Config
*/
/**
use DI\ContainerBuilder;
/*
* Pandora build version and version
*/
$build_version = 'PC240126';
@ -336,3 +338,15 @@ if (isset($config['console_log_enabled']) === true
ini_set('log_errors', false);
ini_set('error_log', '');
}
global $container;
if (empty($container) === true) {
include_once $config['homedir'].'/vendor/autoload.php';
// Solution to load the ContainerBuilder class.
$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions(__DIR__.'/../api/v1/config/container.php');
// Create DI container instance.
$container = $containerBuilder->build();
}

View File

@ -0,0 +1,161 @@
<?php
/**
* Functions Token.
*
* @category Users
* @package Pandora FMS
* @subpackage Community
* @version 1.0.0
* @license See below
*
*
* Pandora FMS - https://pandorafms.com
* ==================================================
* Copyright (c) 2005-2024 Pandora FMS
* Please see https://pandorafms.com/community/ for full contribution list
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation for version 2.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
use PandoraFMS\Modules\Authentication\Actions\CreateTokenAction;
use PandoraFMS\Modules\Authentication\Actions\DeleteTokenAction;
use PandoraFMS\Modules\Authentication\Actions\GetTokenAction;
use PandoraFMS\Modules\Authentication\Actions\ListTokenAction;
use PandoraFMS\Modules\Authentication\Actions\UpdateTokenAction;
use PandoraFMS\Modules\Authentication\Entities\Token;
use PandoraFMS\Modules\Authentication\Entities\TokenFilter;
/**
* Get token.
*
* @param integer $idToken Token ID.
*
* @return array
*/
function get_user_token(int $idToken): array
{
global $container;
$token = $container->get(GetTokenAction::class)->__invoke($idToken)->toArray();
return $token;
}
/**
* Get info tokens for user.
*
* @param integer $page Page.
* @param integer $pageSize Size page.
* @param string|null $sortField Sort field.
* @param string|null $sortDirection Sort direction.
* @param array $filters Filters.
*
* @return array
*/
function list_user_tokens(
int $page=0,
int $pageSize=0,
?string $sortField=null,
?string $sortDirection=null,
array $filters=[]
): array {
global $config;
global $container;
$tokenFilter = new TokenFilter;
$tokenFilter->setPage($page);
$tokenFilter->setSizePage($pageSize);
$tokenFilter->setSortField($sortField);
$tokenFilter->setSortDirection($sortDirection);
if (empty($filters['freeSearch']) === false) {
$tokenFilter->setFreeSearch($filters['freeSearch']);
}
// phpcs:ignore
/** @var Token $entityFilter */
$entityFilter = $tokenFilter->getEntityFilter();
if (empty($filters['idUser']) === true) {
$entityFilter->setIdUser($config['id_user']);
} else {
$entityFilter->setIdUser($filters['idUser']);
}
$result = $container->get(ListTokenAction::class)->__invoke($tokenFilter);
return $result;
}
/**
* Create token.
*
* @param array $params Params.
*
* @return array
*/
function create_user_token(array $params): array
{
global $config;
global $container;
$token = new Token;
$token->setIdUser($config['id_user']);
$token->setLabel(io_safe_output($params['label']));
$token->setValidity((empty($params['validity']) === false) ? io_safe_output($params['validity']) : null);
$result = $container->get(CreateTokenAction::class)->__invoke($token)->toArray();
return $result;
}
/**
* Update token.
*
* @param integer $idToken Token ID.
* @param array $params Params.
*
* @return array
*/
function update_user_token(int $idToken, array $params): array
{
global $config;
global $container;
$token = $container->get(GetTokenAction::class)->__invoke($idToken);
$oldToken = clone $token;
$token->setIdUser($config['id_user']);
$token->setLabel(io_safe_output($params['label']));
$token->setValidity((empty($params['validity']) === false) ? io_safe_output($params['validity']) : null);
$result = $container->get(UpdateTokenAction::class)->__invoke($token, $oldToken)->toArray();
return $result;
}
/**
* Delete token.
*
* @param integer $idToken Token ID.
*
* @return boolean
*/
function delete_user_token(int $idToken): bool
{
global $container;
$token = $container->get(GetTokenAction::class)->__invoke($idToken);
$container->get(DeleteTokenAction::class)->__invoke($token);
$result = true;
return $result;
}

View File

@ -1016,3 +1016,111 @@ function checkIPInRange(
return $output;
}
/**
* Build header user options to manage.
*
* @param integer $pure Pure.
* @param string $tab Tab.
* @param string $title Title.
*
* @return void
*/
function user_print_header(int $pure=0, string $tab='user', ?string $title=null): void
{
global $config;
$url_list_user = 'index.php?sec=gusuarios&sec2=godmode/users/user_list';
$url_list_user .= '&tab=user&pure='.$pure;
$url_list_profile = 'index.php?sec=gusuarios&sec2=godmode/users/profile_list';
$url_list_profile .= '&tab=profile&pure='.$pure;
$url_list_token = 'index.php?sec=gusuarios&sec2=godmode/users/token_list';
$url_list_token .= '&tab=token&pure='.$pure;
$buttons['user'] = [
'active' => false,
'text' => '<a href="'.$url_list_user.'">'.html_print_image(
'images/user.svg',
true,
[
'title' => __('User management'),
'class' => 'invert_filter main_menu_icon',
]
).'</a>',
];
if ((bool) check_acl($config['id_user'], 0, 'PM') === true) {
$buttons['profile'] = [
'active' => false,
'text' => '<a href="'.$url_list_profile.'">'.html_print_image(
'images/suitcase@svg.svg',
true,
[
'title' => __('Profile management'),
'class' => 'invert_filter main_menu_user',
]
).'</a>',
];
}
$buttons['token'] = [
'active' => false,
'text' => '<a href="'.$url_list_token.'">'.html_print_image(
'images/incremental-data@svg.svg',
true,
[
'title' => __('Token management'),
'class' => 'invert_filter main_menu_user',
]
).'</a>',
];
$buttons[$tab]['active'] = true;
switch ($tab) {
case 'token':
$title = (empty($title) === false) ? $title : __('Token management');
$img = 'images/incremental-data@svg.svg';
$tab_name = 'token_tab';
$short_title = __('Token');
break;
case 'profile':
$title = (empty($title) === false) ? $title : __('Profile management');
$img = 'images/suitcase@svg.svg';
$tab_name = 'profile_tab';
$short_title = __('Profile');
break;
case 'user':
default:
$title = (empty($title) === false) ? $title : __('User management');
$img = 'images/user.svg';
$tab_name = 'user_tab';
$short_title = __('User');
break;
}
// Header.
ui_print_standard_header(
$title,
$img,
false,
$tab_name,
false,
$buttons,
[
[
'link' => '',
'label' => $short_title,
],
[
'link' => $url_list_user,
'label' => __('Manage users'),
],
]
);
}

View File

@ -44,14 +44,17 @@ use PandoraFMS\Modules\Shared\Validators\Validator;
* type="string",
* nullable=true,
* default=null,
* description="validity of the token"
* description="Date until which tocken is valid, if it is void it will never expire",
* example="2023-02-21 08:34:16",
* ),
* @OA\Property(
* property="lastUsage",
* type="string",
* nullable=true,
* default=null,
* description="last_usage of the token"
* description="last_usage of the token",
* example="2023-02-21 08:34:16",
* readOnly=true
* )
* )
*
@ -101,7 +104,7 @@ final class Token extends Entity
private ?string $validity = null;
private ?string $lastUsage = null;
private ?string $token =null;
private ?string $token = null;
public function __construct()
{
@ -115,6 +118,7 @@ final class Token extends Entity
'challenge' => 1,
'idUser' => 1,
'token' => 1,
'lastUsage' => 1,
];
}
@ -127,7 +131,7 @@ final class Token extends Entity
'idUser' => $this->getIdUser(),
'validity' => $this->getValidity(),
'lastUsage' => $this->getLastUsage(),
'token' => $this->getToken()
'token' => $this->getToken(),
];
}
@ -142,7 +146,7 @@ final class Token extends Entity
'uuid' => Validator::STRING,
'challenge' => Validator::STRING,
'idUser' => Validator::STRING,
'validity' => Validator::STRING,
'validity' => Validator::DATETIME,
'lastUsage' => Validator::DATETIME,
];
}
@ -315,7 +319,7 @@ final class Token extends Entity
}
/**
* Get the value of token
* Get the value of token.
*
* @return ?string
*/
@ -325,11 +329,10 @@ final class Token extends Entity
}
/**
* Set the value of token
* Set the value of token.
*
* @param ?string $token
*
* @return self
*/
public function setToken(?string $token): self
{

View File

@ -16,6 +16,13 @@ use PandoraFMS\Modules\Shared\Validators\Validator;
* property="idToken",
* default=null,
* readOnly=false
* ),
* @OA\Property(
* property="freeSearch",
* type="string",
* nullable=true,
* default=null,
* description="Find word in name field."
* )
* )
* }
@ -32,6 +39,8 @@ use PandoraFMS\Modules\Shared\Validators\Validator;
*/
final class TokenFilter extends FilterAbstract
{
private ?string $freeSearch = null;
public function __construct()
{
$this->setDefaultFieldOrder(TokenDataMapper::LABEL);
@ -39,28 +48,28 @@ final class TokenFilter extends FilterAbstract
$this->setEntityFilter(new Token());
}
public function fieldsTranslate(): array
{
return [
'idToken' => TokenDataMapper::ID_TOKEN,
'label' => TokenDataMapper::LABEL,
'idToken' => TokenDataMapper::ID_TOKEN,
'label' => TokenDataMapper::LABEL,
'validity' => TokenDataMapper::VALIDITY,
'lastUsage' => TokenDataMapper::LAST_USAGE,
];
}
public function fieldsReadOnly(): array
{
return [];
}
public function jsonSerialize(): mixed
{
return [];
return [
'freeSearch' => $this->getFreeSearch(),
];
}
public function getValidations(): array
{
$validations = [];
@ -71,11 +80,24 @@ final class TokenFilter extends FilterAbstract
return $validations;
}
public function validateFields(array $filters): array
{
return (new Validator())->validate($filters);
}
public function getFreeSearch(): ?string
{
return $this->freeSearch;
}
public function setFreeSearch(?string $freeSearch): self
{
$this->freeSearch = $freeSearch;
return $this;
}
public function getFieldsFreeSearch(): ?array
{
return [TokenDataMapper::TABLE_NAME.'.'.TokenDataMapper::LABEL];
}
}

View File

@ -61,5 +61,4 @@ class TokenRepository
{
$this->repository->__delete($id, $this->tokenDataMapper);
}
}

View File

@ -33,8 +33,9 @@ final class CreateTokenService
$token = $this->tokenRepository->create($token);
$this->audit->write(
'Token Management',
' Create token #'.$token->getIdToken()
AUDIT_LOG_USER_MANAGEMENT,
'Create token '.$token->getLabel(),
json_encode($token->toArray())
);
return $token;

View File

@ -22,8 +22,9 @@ final class DeleteTokenService
$this->tokenRepository->delete($idToken);
$this->audit->write(
'Token Management',
' Deleted token #'.$idToken
AUDIT_LOG_USER_MANAGEMENT,
'Delete token '.$token->getLabel(),
json_encode($token->toArray())
);
}
}

View File

@ -20,8 +20,6 @@ final class GetUserTokenService
$entityFilter = $tokenFilter->getEntityFilter();
$entityFilter->setUuid($uuid);
// TODO: Validity.
return $this->tokenRepository->getOne($tokenFilter);
}
}

View File

@ -23,8 +23,9 @@ final class UpdateTokenService
$token = $this->tokenRepository->update($token);
$this->audit->write(
'Token Management',
' Update token #'.$token->getIdToken()
AUDIT_LOG_USER_MANAGEMENT,
'Update token '.$token->getLabel(),
json_encode($token->toArray())
);
return $token;

View File

@ -0,0 +1,21 @@
<?php
namespace PandoraFMS\Modules\Authentication\Services;
use PandoraFMS\Core\Config;
final class ValidateServerIdentifierTokenService
{
public function __construct(
private readonly Config $config,
) {
}
public function __invoke(string $token): bool {
$serverUniqueIdentifier = $this->config->get('server_unique_identifier');
$apiPassword = $this->config->get('api_password');
$tokenUniqueServerIdentifier = md5($serverUniqueIdentifier).md5($apiPassword);
return ($tokenUniqueServerIdentifier === $token);
}
}

View File

@ -11,12 +11,20 @@ final class ValidateUserTokenService
public function __invoke(
string $uuid,
string $token,
string $strToken,
): bool {
$token = $this->getUserTokenService->__invoke($uuid);
$validity = $token?->getValidity();
$challenge = $token?->getChallenge();
if (empty($validity) === false) {
if (strtotime($validity) < time()) {
return false;
}
}
$challenge = $this->getUserTokenService->__invoke($uuid)?->getChallenge();
return password_verify(
$token,
$strToken,
$challenge
);
}

View File

@ -15,8 +15,8 @@ abstract class FilterAbstract extends SerializableAbstract
use OrderFilterTrait;
use GroupByFilterTrait;
public const ASC = 'ascending';
public const DESC = 'descending';
public const ASC = 'ASC';
public const DESC = 'DESC';
private ?int $limit = null;
private ?int $offset = null;

View File

@ -105,8 +105,8 @@ use OpenApi\Annotations as OA;
* @OA\Schema(
* type="string",
* enum={
* "ascending",
* "descending"
* "ASC",
* "DESC"
* },
* default=""
* ),

View File

@ -0,0 +1,29 @@
<?php
namespace PandoraFMS\Modules\Shared\Middlewares;
use PandoraFMS\Core\Config;
use PandoraFMS\Modules\Shared\Exceptions\NotFoundException;
final class AclListMiddleware
{
public function __construct(
private readonly Config $config
) {
}
public function check(string $ipOrigin): bool
{
$result = true;
try {
require_once $this->config->get('homedir').'/include/functions_api.php';
if ((bool) \isInACL($ipOrigin) === false) {
$result = false;
}
} catch (NotFoundException) {
$result = false;
}
return $result;
}
}

View File

@ -2,31 +2,41 @@
namespace PandoraFMS\Modules\Shared\Middlewares;
use PandoraFMS\Core\Config;
use PandoraFMS\Modules\Authentication\Services\GetUserTokenService;
use PandoraFMS\Modules\Authentication\Services\UpdateTokenService;
use PandoraFMS\Modules\Authentication\Services\ValidateServerIdentifierTokenService;
use PandoraFMS\Modules\Authentication\Services\ValidateUserTokenService;
use PandoraFMS\Modules\Shared\Exceptions\NotFoundException;
use PandoraFMS\Modules\Shared\Services\Timestamp;
use Psr\Http\Message\ServerRequestInterface as Request;
class UserTokenMiddleware
final class UserTokenMiddleware
{
public function __construct(
private readonly ValidateServerIdentifierTokenService $validateServerIdentifierTokenService,
private readonly ValidateUserTokenService $validateUserTokenService,
private readonly GetUserTokenService $getUserTokenService,
private readonly UpdateTokenService $updateTokenService,
private readonly Timestamp $timestamp
private readonly Timestamp $timestamp,
private readonly Config $config
) {
}
public function check(Request $request): bool
{
global $config;
$authorization = ($request->getHeader('Authorization')[0] ?? '');
hd('El server UUID:', true);
hd($this->config->get('server_unique_identifier'), true);
/*
@var ?Token $token
*/
hd('El api pass es:', true);
hd($this->config->get('api_password'), true);
hd('El token de md5 con el que se puede loguear: ', true);
hd(md5($this->config->get('server_unique_identifier')).'-'.md5($this->config->get('api_password')), true);
$authorization = ($request->getHeader('Authorization')[0] ?? '');
$token = null;
try {
$authorization = str_replace('Bearer ', '', $authorization);
preg_match(
@ -36,13 +46,19 @@ class UserTokenMiddleware
);
$uuid = ($matches[0] ?? '');
$token = str_replace($uuid.'-', '', $authorization);
$validToken = $this->validateUserTokenService->__invoke($uuid, $token);
$token = $this->getUserTokenService->__invoke($uuid);
if ($token !== null && $validToken) {
$oldToken = clone $token;
$token->setLastUsage($this->timestamp->getMysqlCurrentTimestamp(0));
$this->updateTokenService->__invoke($token, $oldToken);
$strToken = str_replace($uuid.'-', '', $authorization);
$validTokenUiniqueServerIdentifier = $this->validateServerIdentifierTokenService->__invoke($strToken);
if ($validTokenUiniqueServerIdentifier === false) {
$validToken = $this->validateUserTokenService->__invoke($uuid, $strToken);
$token = $this->getUserTokenService->__invoke($uuid);
if ($token !== null && $validToken) {
$oldToken = clone $token;
$token->setLastUsage($this->timestamp->getMysqlCurrentTimestamp(0));
$this->updateTokenService->__invoke($token, $oldToken);
}
} else {
$validToken = true;
$token = false;
}
} catch (NotFoundException) {
$token = null;
@ -53,8 +69,13 @@ class UserTokenMiddleware
session_start();
}
$_SESSION['id_usuario'] = $token->getIdUser();
$config['id_user'] = $token->getIdUser();
if ($validTokenUiniqueServerIdentifier === true) {
$_SESSION['id_usuario'] = 'admin';
$this->config->set('id_user', 'admin');
} else {
$_SESSION['id_usuario'] = $token->getIdUser();
$this->config->set('id_user', $token->getIdUser());
}
if (session_status() === PHP_SESSION_ACTIVE) {
session_write_close();

View File

@ -316,8 +316,8 @@ class RepositoryMySQL extends Repository
private function checkDirectionOrderByMsql(?string $direction): string
{
$directionArray = [
'descending' => 'DESC',
'ascending' => 'ASC',
'DESC' => 'DESC',
'ASC' => 'ASC',
];
return (isset($directionArray[$direction]) === true) ? $directionArray[$direction] : 'ASC';

View File

@ -206,7 +206,7 @@ ui_print_standard_header(
);
$only_count = false;
hd(io_safe_input($_SESSION['stringSearchSQL']), true);
switch ($searchTab) {
case 'main':
$only_count = true;