<?php
/**
 * Credential store
 *
 * @category   Class
 * @package    Pandora FMS
 * @subpackage Credential store
 * @version    1.0.0
 * @license    See below
 *
 *    ______                 ___                    _______ _______ ________
 *   |   __ \.-----.--.--.--|  |.-----.----.-----. |    ___|   |   |     __|
 *  |    __/|  _  |     |  _  ||  _  |   _|  _  | |    ___|       |__     |
 * |___|   |___._|__|__|_____||_____|__| |___._| |___|   |__|_|__|_______|
 *
 * ============================================================================
 * Copyright (c) 2005-2022 Artica Soluciones Tecnologicas
 * Please see http://pandorafms.org 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 $config;

require_once $config['homedir'].'/godmode/wizards/Wizard.main.php';

/**
 * Provides functionality for credential store.
 */
class CredentialStore extends Wizard
{

    /**
     * Url of controller.
     *
     * @var string
     */
    public $ajaxController;

    /**
     * References datatables object identifier.
     *
     * @var string
     */
    public $tableId;

    /**
     * Allowed methods to be called using AJAX request.
     *
     * @var array
     */
    public $AJAXMethods = [
        'draw',
        'loadModal',
        'addKey',
        'updateKey',
        'deleteKey',
    ];


    /**
     * Checks if target method is available to be called using AJAX.
     *
     * @param string $method Target method.
     *
     * @return boolean True allowed, false not.
     */
    public function ajaxMethod($method)
    {
        return in_array($method, $this->AJAXMethods);
    }


    /**
     * Generates a JSON error.
     *
     * @param string $msg Error message.
     *
     * @return void
     */
    public function error($msg)
    {
        echo json_encode(
            ['error' => $msg]
        );
    }


    /**
     * Minor function to dump json message as ajax response.
     *
     * @param string  $type   Type: result || error.
     * @param string  $msg    Message.
     * @param boolean $delete Deletion messages.
     *
     * @return void
     */
    private function ajaxMsg($type, $msg, $delete=false)
    {
        if ($type === 'error') {
            $msg_title = ($delete === true) ? 'Failed while removing' : 'Failed while saving';
        } else {
            $msg_title = ($delete === true) ? 'Successfully deleted' : 'Successfully saved into keystore';
        }

        echo json_encode(
            [ $type => __($msg_title).':<br>'.$msg ]
        );

        exit;
    }


    /**
     * Initializes object and validates user access.
     *
     * @param string $ajax_controller Path of ajaxController, is the 'page'
     *                               variable sent in ajax calls.
     *
     * @return object
     */
    public function __construct($ajax_controller)
    {
        global $config;

        // Check access.
        check_login();

        if ((bool) check_acl($config['id_user'], 0, 'PM') === false
            || (bool) check_acl($config['id_user'], 0, 'UM') === false
        ) {
            db_pandora_audit(
                AUDIT_LOG_ACL_VIOLATION,
                'Trying to access credential store'
            );

            if (is_ajax()) {
                echo json_encode(['error' => 'noaccess']);
            } else {
                include 'general/noaccess.php';
            }

            exit;
        }

        $this->ajaxController = $ajax_controller;

        return $this;
    }


    /**
     * Returns an array with all the credentials matching filter and ACL.
     *
     * @param array   $fields     Fields array or 'count' keyword to retrieve count.
     * @param array   $filter     Filters to be applied.
     * @param integer $offset     Offset (pagination).
     * @param integer $limit      Limit (pagination).
     * @param string  $order      Sort order.
     * @param string  $sort_field Sort field.
     *
     * @return array With all results or false if error.
     * @throws Exception On error.
     */
    public static function getAll(
        $fields,
        $filter,
        $offset=null,
        $limit=null,
        $order=null,
        $sort_field=null
    ) {
        $sql_filters = [];
        $order_by = '';
        $pagination = '';

        global $config;

        if (!is_array($filter)) {
            error_log('[credential_get_all] Filter must be an array.');
            throw new Exception('[credential_get_all] Filter must be an array.');
        }

        $count = false;
        if (!is_array($fields) && $fields == 'count') {
            $fields = ['cs.*'];
            $count = true;
        } else if (!is_array($fields)) {
            error_log('[credential_get_all] Fields must be an array or "count".');
            throw new Exception('[credential_get_all] Fields must be an array or "count".');
        }

        if (isset($filter['product']) && !empty($filter['product'])) {
            $sql_filters[] = sprintf(' AND cs.product = "%s"', $filter['product']);
        }

        if (isset($filter['free_search']) && !empty($filter['free_search'])) {
            $sql_filters[] = vsprintf(
                ' AND (lower(cs.username) like lower("%%%s%%")
                    OR cs.identifier like "%%%s%%"
                    OR lower(cs.product) like lower("%%%s%%"))',
                array_fill(0, 3, $filter['free_search'])
            );
        }

        if (isset($filter['filter_id_group']) && $filter['filter_id_group'] > 0) {
            $propagate = db_get_value(
                'propagate',
                'tgrupo',
                'id_grupo',
                $filter['filter_id_group']
            );

            if (!$propagate) {
                $sql_filters[] = sprintf(
                    ' AND cs.id_group = %d ',
                    $filter['filter_id_group']
                );
            } else {
                $groups = [ $filter['filter_id_group'] ];
                $childrens = groups_get_children(
                    $filter['filter_id_group'],
                    null,
                    true
                );
                if (!empty($childrens)) {
                    foreach ($childrens as $child) {
                        $groups[] = (int) $child['id_grupo'];
                    }
                }

                $filter['filter_id_group'] = $groups;
                $sql_filters[] = sprintf(
                    ' AND cs.id_group IN (%s) ',
                    join(',', $filter['filter_id_group'])
                );
            }
        }

        if (isset($filter['group_list']) && is_array($filter['group_list'])) {
            $sql_filters[] = sprintf(
                ' AND cs.id_group IN (%s) ',
                join(',', $filter['group_list'])
            );
        } else if (users_is_admin() !== true) {
            $user_groups = users_get_groups(
                $config['id_user'],
                'AR'
            );

            // Always add group 'ALL' because 'ALL' group credentials
            // must be available for all users.
            if (is_array($user_groups) === true) {
                $user_groups = ([0] + array_keys($user_groups));
            } else {
                $user_groups = [0];
            }

            $sql_filters[] = sprintf(
                ' AND cs.id_group IN (%s) ',
                join(',', $user_groups)
            );
        }

        if (isset($filter['identifier'])) {
            $sql_filters[] = sprintf(
                ' AND cs.identifier = "%s" ',
                $filter['identifier']
            );
        }

        if (isset($order)) {
            $dir = 'asc';
            if ($order == 'desc') {
                $dir = 'desc';
            };

            if (in_array(
                $sort_field,
                [
                    'group',
                    'identifier',
                    'product',
                    'username',
                    'options',
                ]
            )
            ) {
                $order_by = sprintf(
                    'ORDER BY `%s` %s',
                    $sort_field,
                    $dir
                );
            }
        }

        if (isset($limit) && $limit > 0
            && isset($offset) && $offset >= 0
        ) {
            $pagination = sprintf(
                ' LIMIT %d OFFSET %d ',
                $limit,
                $offset
            );
        }

        $sql = sprintf(
            'SELECT %s
            FROM tcredential_store cs
            LEFT JOIN tgrupo tg
                ON tg.id_grupo = cs.id_group
            WHERE 1=1
            %s
            %s
            %s',
            join(',', $fields),
            join(' ', $sql_filters),
            $order_by,
            $pagination
        );

        if ($count) {
            $sql = sprintf('SELECT count(*) as n FROM ( %s ) tt', $sql);

            return db_get_value_sql($sql);
        }

        $return = db_get_all_rows_sql($sql);

        if ($return === false) {
            $return = [];
        }

        // Filter out those items of group all that cannot be edited by user.
        $return = array_filter(
            $return,
            function ($item) {
                if ($item['id_group'] == 0 && users_can_manage_group_all('AR') === false) {
                    return false;
                } else {
                    return true;
                }
            }
        );

        return $return;
    }


    /**
     * Retrieves target key from keystore or false in case of error.
     *
     * @param string $identifier Key identifier.
     *
     * @return array Key or false if error.
     */
    public static function getKey($identifier)
    {
        global $config;

        if (empty($identifier)) {
            return false;
        }

        $keys = self::getAll(
            [
                'cs.*',
                'tg.nombre as `group`',
            ],
            ['identifier' => $identifier]
        );

        if (is_array($keys) === true) {
            // Only 1 must exist.
            $key = $keys[0];

            // Decrypt content.
            $key['username'] = io_output_password($key['username']);
            $key['password'] = io_output_password($key['password']);
            $key['extra_1'] = io_output_password($key['extra_1']);
            $key['extra_2'] = io_output_password($key['extra_2']);

            return $key;
        }

        return false;
    }


    /**
     * Return all keys avaliable for current user.
     *
     * @param string $product Filter by product.
     *
     * @return array Keys or false if error.
     */
    public static function getKeys($product=false)
    {
        global $config;

        $filter = [];

        if ($product !== false) {
            $filter['product'] = $product;
        }

        $keys = self::getAll(
            [
                'cs.*',
                'tg.nombre as `group`',
            ],
            $filter
        );

        if (is_array($keys) === true) {
            // Improve usage and decode output.
            $return = array_reduce(
                $keys,
                function ($carry, $item) {
                    $item['username'] = io_output_password($item['username']);
                    $item['password'] = io_output_password($item['password']);
                    $item['extra_1'] = io_output_password($item['extra_1']);
                    $item['extra_2'] = io_output_password($item['extra_2']);
                    $carry[$item['identifier']] = $item['identifier'];
                    return $carry;
                },
                []
            );

            return $return;
        }

        return [];
    }


    /**
     * Ajax method invoked by datatables to draw content.
     *
     * @return void
     */
    public function draw()
    {
        global $config;

        // Datatables offset, limit and order.
        $filter = get_parameter('filter', []);
        $start = get_parameter('start', 0);
        $length = get_parameter('length', $config['block_size']);
        $order = get_datatable_order(true);
        if ((bool) users_is_admin() === false) {
            $all = users_can_manage_group_all('UM');

            $filter['group_list'] = array_keys(
                users_get_groups(
                    $config['id_user'],
                    'UM',
                    (bool) $all
                )
            );
        }

        try {
            ob_start();

            $fields = [
                'cs.*',
                'tg.nombre as `group`',
            ];

            // Retrieve data.
            $data = $this->getAll(
                // Fields.
                $fields,
                // Filter.
                $filter,
                // Offset.
                $start,
                // Limit.
                $length,
                // Order.
                $order['direction'],
                // Sort field.
                $order['field']
            );

            // Retrieve counter.
            $count = $this->getAll(
                'count',
                $filter
            );

            if ($data) {
                $data = array_reduce(
                    $data,
                    function ($carry, $item) {
                        // Transforms array of arrays $data into an array
                        // of objects, making a post-process of certain fields.
                        $tmp = (object) $item;
                        $tmp->username = io_output_password($tmp->username);

                        if (empty($tmp->group)) {
                            $tmp->group = __('All');
                        } else {
                            $tmp->group = io_safe_output($tmp->group);
                        }

                        $carry[] = $tmp;
                        return $carry;
                    }
                );
            }

            // Datatables format: RecordsTotal && recordsfiltered.
            echo json_encode(
                [
                    'data'            => $data,
                    'recordsTotal'    => $count,
                    'recordsFiltered' => $count,
                ]
            );
            // Capture output.
            $response = ob_get_clean();
        } catch (Exception $e) {
            echo json_encode(['error' => $e->getMessage()]);
            exit;
        }

        // 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]
            );
        }

        exit;
    }


    /**
     * Prints inputs for modal "Add key".
     *
     * @return void
     */
    public function loadModal()
    {
        $identifier = get_parameter('identifier', null);
        $key = self::getKey($identifier);

        echo $this->printInputs($key);
    }


    /**
     * Prepare variables received using form. AJAX environment only.
     *
     * @return array of values processed or false in case of error.
     */
    private function prepareKeyValues()
    {
        $identifier = get_parameter('identifier', null);
        $id_group = get_parameter('id_group', null);
        $product = get_parameter('product', null);
        $username = get_parameter('username', null);
        $password = get_parameter('password', null);
        $extra_1 = get_parameter('extra_1', null);
        $extra_2 = get_parameter('extra_2', null);

        if ($product === 'GOOGLE') {
            $google_creds = json_decode(io_safe_output($extra_1));

            if (json_last_error() !== JSON_ERROR_NONE) {
                $this->ajaxMsg(
                    'error',
                    __('Not a valid JSON: %s', json_last_error_msg())
                );
                exit;
            }

            $username = $google_creds->client_email;
            $password = $google_creds->private_key_id;
        }

        if ($product !== 'SNMP') {
            if (empty($identifier) === true) {
                $error = __('Key identifier is required');
            } else if ($id_group === null) {
                $error = __('You must select a group where store this key!');
            } else if (empty($product) === true) {
                $error = __('You must specify a product type');
            } else if (empty($username) === true || (empty($password) === true)) {
                $error = __('You must specify a username and/or password');
            } else if (evaluate_ascii_valid_string(io_safe_output($identifier)) === false) {
                $error = __('Identifier with forbidden characters. Check the documentation.');
            }

            if (isset($error) === true) {
                $this->ajaxMsg('error', $error);
                exit;
            }

            // Encrypt content (if needed).
            $values = [
                'identifier' => $identifier,
                'id_group'   => $id_group,
                'product'    => $product,
                'username'   => io_input_password(io_safe_output($username)),
                'password'   => io_input_password(io_safe_output($password)),
                'extra_1'    => io_input_password(io_safe_output($extra_1)),
                'extra_2'    => io_input_password(io_safe_output($extra_2)),
            ];
        } else {
            $values = [
                'identifier' => $identifier,
                'id_group'   => $id_group,
                'product'    => $product,
            ];

            $community = (string) get_parameter('community', '');
            $version = (string) get_parameter('version', '1');
            $extra_json = [
                'community' => $community,
                'version'   => $version,
            ];
            if ($version === '3') {
                $securityLevelV3 = (string) get_parameter('securityLevelV3', 'authNoPriv');
                $extra_json['securityLevelV3'] = $securityLevelV3;
                $authUserV3 = (string) get_parameter('authUserV3', '');
                $extra_json['authUserV3'] = $authUserV3;
                if ($securityLevelV3 === 'authNoPriv' || $securityLevelV3 === 'authPriv') {
                    $authUserV3 = (string) get_parameter('authUserV3', '');
                    $extra_json['authUserV3'] = $authUserV3;
                    $authMethodV3 = (string) get_parameter('authMethodV3', 'MD5');
                    $extra_json['authMethodV3'] = $authMethodV3;
                    $authPassV3 = (string) get_parameter('authPassV3', '');
                    $extra_json['authPassV3'] = $authPassV3;

                    if ($securityLevelV3 === 'authPriv') {
                        $privacyMethodV3 = (string) get_parameter('privacyMethodV3', 'AES');
                        $extra_json['privacyMethodV3'] = $privacyMethodV3;
                        $privacyPassV3 = (string) get_parameter('privacyPassV3', '');
                        $extra_json['privacyPassV3'] = $privacyPassV3;
                    }
                }
            }

            $values['extra_1'] = json_encode($extra_json);
        }

        // Spaces  are not allowed.
        $values['identifier'] = \io_safe_input(
            preg_replace(
                '/\s+/',
                '-',
                trim(
                    \io_safe_output($identifier)
                )
            )
        );
        return $values;
    }


    /**
     * Stores a key into credential store.
     *
     * @param array  $values     Key definition.
     * @param string $identifier Update or create.
     *
     * @return boolean True if ok, false if not ok.
     */
    private function storeKey($values, $identifier=false)
    {
        if ($identifier === false) {
            // New.
            return db_process_sql_insert('tcredential_store', $values);
        } else {
            // Update.
            return db_process_sql_update(
                'tcredential_store',
                $values,
                ['identifier' => $identifier]
            );
        }

    }


    /**
     * Add a new key into Credential Store
     *
     * @return void
     */
    public function addKey()
    {
        global $config;

        $values = $this->prepareKeyValues();

        if ($this->storeKey($values) === false) {
            $this->ajaxMsg('error', $config['dbconnection']->error);
        } else {
            $this->ajaxMsg('result', $values['identifier']);
        }

        exit;
    }


    /**
     * Add a new key into Credential Store
     *
     * @return void
     */
    public function updateKey()
    {
        global $config;

        $values = $this->prepareKeyValues();
        $identifier = $values['identifier'];

        if ($this->storeKey($values, $identifier) === false) {
            $this->ajaxMsg('error', $config['dbconnection']->error);
        } else {
            $this->ajaxMsg('result', $identifier);
        }

        exit;
    }


    /**
     * AJAX method. Delete key from keystore.
     *
     * @return void
     */
    public function deleteKey()
    {
        global $config;

        $identifier = get_parameter('identifier', null);

        if (empty($identifier)) {
            $this->ajaxMsg('error', __('identifier cannot be empty'), true);
        }

        if (self::getKey($identifier) === false) {
            // User has no grants to delete target key.
            $this->ajaxMsg('error', __('Not allowed'), true);
        }

        if (db_process_sql_delete(
            'tcredential_store',
            ['identifier' => $identifier]
        ) === false
        ) {
            $this->ajaxMsg('error', $config['dbconnection']->error, true);
        } else {
            $this->ajaxMsg('result', $identifier, true);
        }

    }


    /**
     * Run CredentialStore (main page).
     *
     * @return void
     */
    public function run()
    {
        global $config;

        // Require specific CSS and JS.
        ui_require_css_file('wizard');
        ui_require_css_file('discovery');
        ui_require_css_file('credential_store');

        if (!isset($config['encryption_passphrase'])) {
            $url = 'https://pandorafms.com/docs/index.php?title=Pandora:Documentation_en:Password_Encryption';
            if ($config['language'] == 'es') {
                $url = 'https://pandorafms.com/docs/index.php?title=Pandora:Documentation_es:Cifrado_Contrase%C3%B1as';
            }

            ui_print_warning_message(
                __(
                    'Database encryption is not enabled. Credentials will be stored in plaintext. %s',
                    '<a target="_new" href="'.$url.'">'.__('How to configure encryption.').'</a>'
                )
            );
        }

        // Datatables list.
        try {
            $columns = [
                'group',
                'identifier',
                'product',
                'username',
                'options',
            ];

            $column_names = [
                __('Group'),
                __('Identifier'),
                __('Product'),
                __('User'),
                [
                    'text'  => __('Options'),
                    'class' => 'table_action_buttons',
                ],
            ];

            $this->tableId = 'keystore';
            // Load datatables user interface.
            ui_print_datatable(
                [
                    'id'                  => $this->tableId,
                    'class'               => 'info_table',
                    'style'               => 'width: 99%',
                    'columns'             => $columns,
                    'column_names'        => $column_names,
                    'ajax_url'            => $this->ajaxController,
                    'ajax_data'           => ['method' => 'draw'],
                    'ajax_postprocess'    => 'process_datatables_item(item)',
                    'no_sortable_columns' => [-1],
                    'order'               => [
                        'field'     => 'identifier',
                        'direction' => 'asc',
                    ],
                    'search_button_class' => 'sub filter float-right',
                    'filter_main_class'   => 'box-flat white_table_graph fixed_filter_bar',
                    'form'                => [
                        'inputs' => [
                            [
                                'label'     => __('Group'),
                                'type'      => 'select_groups',
                                'id'        => 'filter_id_group',
                                'name'      => 'filter_id_group',
                                'privilege' => 'AR',
                                'type'      => 'select_groups',
                                'nothing'   => false,
                                'selected'  => (defined($id_group_filter) ? $id_group_filter : 0),
                                'return'    => true,
                                'size'      => '80%',
                            ],
                            [
                                'label' => __('Free search'),
                                'type'  => 'text',
                                'class' => 'mw250px',
                                'id'    => 'free_search',
                                'name'  => 'free_search',
                            ],
                        ],
                    ],
                ]
            );
        } catch (Exception $e) {
            echo $e->getMessage();
        }

        // Auxiliar div.
        $modal = '<div id="modal" class="invisible"></div>';
        $msg = '<div id="msg"     class="invisible"></div>';
        $aux = '<div id="aux"     class="invisible"></div>';

        echo $modal.$msg.$aux;

        // Create button.
        html_print_action_buttons(
            html_print_submit_button(
                __('Add key'),
                'create',
                false,
                ['icon' => 'next'],
                true
            )
        );

        echo $this->loadJS();

    }


    /**
     * Generates inputs for new/update forms.
     *
     * @param array $values Values or null.
     *
     * @return string Inputs.
     */
    public function printInputs($values=null)
    {
        if (!is_array($values)) {
            $values = [];
        }

        $return_all_group = false;

        if (users_can_manage_group_all('AR') === true) {
            $return_all_group = true;
        }

        $form = [
            'action'   => '#',
            'id'       => 'modal_form',
            'onsubmit' => 'return false;',
            'class'    => 'modal',
            'extra'    => 'autocomplete="new-password"',
        ];

        $inputs = [];

        $inputs[] = [
            'label'     => __('Identifier'),
            'id'        => 'div-identifier',
            'arguments' => [
                'name'     => 'identifier',
                'type'     => 'text',
                'value'    => $values['identifier'],
                'disabled' => (bool) $values['identifier'],
                'return'   => true,
            ],
        ];

        $inputs[] = [
            'label'     => __('Group'),
            'arguments' => [
                'name'           => 'id_group',
                'id'             => 'id_group',
                'input_class'    => 'flex-row',
                'type'           => 'select_groups',
                'returnAllGroup' => $return_all_group,
                'selected'       => $values['id_group'],
                'return'         => true,
                'class'          => 'w50p',
            ],
        ];

        $inputs[] = [
            'label'     => __('Product'),
            'id'        => 'div-product',
            'arguments' => [
                'name'        => 'product',
                'input_class' => 'flex-row',
                'type'        => 'select',
                'script'      => 'calculate_inputs()',
                'fields'      => [
                    'CUSTOM' => __('Custom'),
                    'AWS'    => __('Aws'),
                    'AZURE'  => __('Azure'),
                    'SAP'    => __('SAP'),
                    'GOOGLE' => __('Google'),
                    'WMI'    => __('WMI'),
                    'SNMP'   => __('SNMP'),
                ],
                'selected'    => (isset($values['product']) ? $values['product'] : 'CUSTOM'),
                'disabled'    => (bool) $values['product'],
                'return'      => true,
            ],
        ];

        $user_label = __('Username');
        $pass_label = __('Password');
        $extra_1_label = __('Extra');
        $extra_2_label = __('Extra (2)');
        $extra1_type = 'text';
        $user = true;
        $pass = true;
        $extra1 = true;
        $extra2 = true;

        // Remember to update credential_store.php also.
        switch ($values['product']) {
            case 'AWS':
                $user_label = __('Access key ID');
                $pass_label = __('Secret access key');
                $extra1 = false;
                $extra2 = false;
            break;

            case 'AZURE':
                $user_label = __('Account ID');
                $pass_label = __('Application secret');
                $extra_1_label = __('Tenant or domain name');
                $extra_2_label = __('Subscription id');
            break;

            case 'GOOGLE':
                $extra_1_label = __('Auth JSON');
                $user = false;
                $pass = false;
                $extra1 = true;
                $extra2 = false;
                $extra1_type = 'textarea';
            break;

            case 'WMI':
                $extra_1_label = __('Namespace');
                $extra1 = true;
                $extra2 = false;
            break;

            case 'SNMP':
                $user = false;
                $pass = false;
                $extra1 = false;
                $extra2 = false;
            break;

            case 'CUSTOM':
            case 'SAP':
                $user_label = __('Account ID');
                $pass_label = __('Password');
                $extra1 = false;
                $extra2 = false;
            default:
                // Use defaults.
            break;
        }

        if ($user) {
            $inputs[] = [
                'label'     => $user_label,
                'id'        => 'div-username',
                'arguments' => [
                    'name'        => 'username',
                    'input_class' => 'flex-row',
                    'type'        => 'text',
                    'value'       => $values['username'],
                    'return'      => true,
                ],
            ];
        }

        if ($pass) {
            $inputs[] = [
                'label'     => $pass_label,
                'id'        => 'div-password',
                'arguments' => [
                    'name'        => 'password',
                    'input_class' => 'flex-row',
                    'type'        => 'password',
                    'value'       => $values['password'],
                    'return'      => true,
                ],
            ];
        }

        if ($extra1) {
            $inputs[] = [
                'label'     => $extra_1_label,
                'id'        => 'div-extra_1',
                'arguments' => [
                    'name'        => 'extra_1',
                    'id'          => 'text-extra_1',
                    'input_class' => 'flex-row',
                    'type'        => $extra1_type,
                    'value'       => $values['extra_1'],
                    'return'      => true,
                ],
            ];
        }

        if ($extra2) {
            $inputs[] = [
                'label'     => $extra_2_label,
                'id'        => 'div-extra_2',
                'arguments' => [
                    'name'        => 'extra_2',
                    'input_class' => 'flex-row',
                    'type'        => 'text',
                    'value'       => $values['extra_2'],
                    'return'      => true,
                    'display'     => $extra2,
                ],

            ];
        }

        if ($values['product'] === 'SNMP') {
            $json_values = json_decode($values['extra_1'], true);
            $inputs[] = [
                'label'     => __('SNMP community'),
                'id'        => 'li_snmp_1',
                'arguments' => [
                    'name'        => 'community',
                    'input_class' => 'flex-row',
                    'type'        => 'text',
                    'value'       => $json_values['community'],
                    'return'      => true,
                ],
            ];

            $inputs[] = [
                'label'     => __('SNMP version'),
                'id'        => 'li_snmp_2',
                'arguments' => [
                    'name'        => 'version',
                    'input_class' => 'flex-row',
                    'type'        => 'select',
                    'script'      => 'showVersion()',
                    'fields'      => [
                        '1'  => __('1'),
                        '2'  => __('2'),
                        '2c' => __('2c'),
                        '3'  => __('3'),
                    ],
                    'selected'    => (isset($json_values['version']) ? $json_values['version'] : '1'),
                    'return'      => true,
                ],
            ];

            $inputs[] = [
                'label'     => __('Security level'),
                'id'        => 'li_snmp_3',
                'style'     => ($json_values['version'] !== '3') ? 'display: none;' : '',
                'arguments' => [
                    'name'        => 'securityLevelV3',
                    'input_class' => 'flex-row',
                    'type'        => 'select',
                    'script'      => 'showSecurity()',
                    'fields'      => [
                        'authNoPriv'   => __('Authenticated and non-private method'),
                        'authPriv'     => __('Authenticated and private method'),
                        'noAuthNoPriv' => __('Non-authenticated and non-private method'),
                    ],
                    'selected'    => (isset($json_values['securityLevelV3']) ? $json_values['securityLevelV3'] : 'authNoPriv'),
                    'return'      => true,
                ],
            ];

            $inputs[] = [
                'label'     => __('User authentication'),
                'id'        => 'li_snmp_4',
                'style'     => ($json_values['version'] !== '3') ? 'display: none;' : '',
                'arguments' => [
                    'name'        => 'authUserV3',
                    'input_class' => 'flex-row',
                    'type'        => 'text',
                    'value'       => $json_values['authUserV3'],
                    'return'      => true,
                ],
            ];

            $authNoPrivate = (
                isset($json_values['securityLevelV3']) &&
                ($json_values['securityLevelV3'] === 'authNoPriv' || $json_values['securityLevelV3'] === 'authPriv')
            ) ? '' : 'display: none;';

            $inputs[] = [
                'label'     => __('Authentication method'),
                'id'        => 'li_snmp_5',
                'style'     => $authNoPrivate,
                'arguments' => [
                    'name'        => 'authMethodV3',
                    'input_class' => 'flex-row',
                    'type'        => 'select',
                    'fields'      => [
                        'MD5' => __('MD5'),
                        'SHA' => __('SHA'),
                    ],
                    'selected'    => (isset($json_values['authMethodV3']) ? $json_values['authMethodV3'] : 'MD5'),
                    'return'      => true,
                ],
            ];

            $inputs[] = [
                'label'     => __('Password authentication'),
                'id'        => 'li_snmp_6',
                'style'     => $authNoPrivate,
                'arguments' => [
                    'name'        => 'authPassV3',
                    'input_class' => 'flex-row',
                    'type'        => 'password',
                    'value'       => $json_values['authPassV3'],
                    'return'      => true,
                ],
            ];

            $authPrivate = (isset($json_values['securityLevelV3']) && $json_values['securityLevelV3'] === 'authPriv')
                ? ''
                : 'display: none;';

            $inputs[] = [
                'label'     => __('Privacy method'),
                'id'        => 'li_snmp_7',
                'style'     => $authPrivate,
                'arguments' => [
                    'name'        => 'privacyMethodV3',
                    'input_class' => 'flex-row',
                    'type'        => 'select',
                    'fields'      => [
                        'AES' => __('AES'),
                        'DES' => __('DES'),
                    ],
                    'selected'    => (isset($json_values['privacyMethodV3']) ? $json_values['privacyMethodV3'] : 'AES'),
                    'return'      => true,
                ],
            ];

            $inputs[] = [
                'label'     => __('Privacy pass'),
                'id'        => 'li_snmp_8',
                'style'     => $authPrivate,
                'arguments' => [
                    'name'        => 'privacyPassV3',
                    'input_class' => 'flex-row',
                    'type'        => 'password',
                    'value'       => $json_values['privacyPassV3'],
                    'return'      => true,
                ],
            ];
        }

        return $this->printForm(
            [
                'form'   => $form,
                'inputs' => $inputs,
            ],
            true
        );
    }


    /**
     * Loads JS content.
     *
     * @return string JS content.
     */
    public function loadJS()
    {
        ob_start();

        // Javascript content.
        ?>
    <script type="text/javascript">
        /**
        * Process datatable item before draw it.
        */
        function process_datatables_item(item) {

            id = item.identifier;

            idrow = '<b><a href="javascript:" onclick="show_form(\'';
            idrow += item.identifier;
            idrow += '\')" >'+item.identifier+'</a></b>';
            item.identifier = idrow;

            item.options = '<div class="table_action_buttons">';
            item.options += '<a href="javascript:" onclick="show_form(\'';
            item.options += id;
            item.options += '\')" ><?php echo html_print_image('images/edit.svg', true, ['title' => __('Edit'), 'class' => 'main_menu_icon invert_filter']); ?></a>';

            item.options += '<a href="javascript:" onclick="delete_key(\'';
            item.options += id;
            item.options += '\')" ><?php echo html_print_image('images/delete.svg', true, ['title' => __('Delete'), 'class' => 'main_menu_icon invert_filter']); ?></a>';
            item.options += '</div>';
        }

        /**
         * Cleanup current dom entries.
         */
        function cleanupDOM() {
            $('#div-identifier').empty();
            $('#div-product').empty();
            $('#div-username').empty();
            $('#div-password').empty();
            $('#div-extra_1').empty();
            $('#div-extra_2').empty();
        }

        /**
         * Handles inputs visibility based on selected product.
         */
        function calculate_inputs() {
            if ($('#product :selected').val() != "GOOGLE") {
                // Restore text-extra_1.
                var val = $('#text-extra_1').val();
                if(typeof val == 'undefined') {
                    val = '';
                }
                $('#text-extra_1').remove();
                $('#div-extra_1').append(
                    $('<input type="text" name="extra_1" id="text-extra_1" size="50" value="'+val+'"></input>')
                );
            }

            if ($('#product :selected').val() == "CUSTOM") {
                $('#div-username label').text('<?php echo __('User'); ?>');
                $('#div-password label').text('<?php echo __('Password'); ?>');
                $('#div-username').show();
                $('#div-password').show();
                $('#div-extra_1').hide();
                $('#div-extra_2').hide();
            } else if ($('#product :selected').val() == "AWS") {
                $('#div-username label').text('<?php echo __('Access key ID'); ?>');
                $('#div-password label').text('<?php echo __('Secret access key'); ?>');
                $('#div-username').show();
                $('#div-password').show();
                $('#div-extra_1').hide();
                $('#div-extra_2').hide();
            } else if ($('#product :selected').val() == "AZURE") {
                $('#div-username label').text('<?php echo __('Client ID'); ?>');
                $('#div-password label').text('<?php echo __('Application secret'); ?>');
                $('#div-extra_1 label').text('<?php echo __('Tenant or domain name'); ?>');
                $('#div-extra_2 label').text('<?php echo __('Subscription id'); ?>');
                $('#div-username').show();
                $('#div-password').show();
                $('#div-extra_1').show();
                $('#div-extra_2').show();
            } else if ($('#product :selected').val() == "SAP") {
                $('#div-username label').text('<?php echo __('Account ID.'); ?>');
                $('#div-password label').text('<?php echo __('Password'); ?>');
                $('#div-username').show();
                $('#div-password').show();
                $('#div-extra_1').hide();
                $('#div-extra_2').hide();
            } else if ($('#product :selected').val() == "GOOGLE") {
                $('#div-username').hide();
                $('#div-password').hide();
                $('#div-extra_2').hide();
                $('#div-extra_1 label').text('<?php echo __('Auth JSON'); ?>');
                var val = $('#text-extra_1').val();
                if(typeof val == 'undefined') {
                    val = '';
                }

                $('#text-extra_1').remove();
                $('#div-extra_1').append(
                    $('<textarea name="extra_1" id="text-extra_1">'+val+'</textarea>')
                );
                $('#div-extra_1').show();
            } else if ($('#product :selected').val() == "WMI") {
                $('#div-username label').text('<?php echo __('Username'); ?>');
                $('#div-password label').text('<?php echo __('Password'); ?>');
                $('#div-extra_1 label').text('<?php echo __('Namespace'); ?>');
                $('#div-username').show();
                $('#div-password').show();
                $('#div-extra_1').show();
                $('#div-extra_2').hide();
            } else if ($('#product :selected').val() == "SNMP") {
                $('#div-username').hide();
                $('#div-password').hide();
                $('#div-extra_1').hide();
                $('#div-extra_2').hide();

                if ($('#li_snmp_1').length > 0) {
                    console.log($('#li_snmp_1').length);
                    const test = '<?php echo $json_values; ?>';
                    console.log(test);
                } else {
                    const ul = $('#modal_form').children('ul')[0];

                    // SNMP community.
                    const li_community = document.createElement("li");
                    li_community.id = 'li_snmp_1';
                    const label_community = document.createElement("label");
                    label_community.textContent = '<?php echo __('SNMP community'); ?>';
                    const input_community = document.createElement("input");
                    input_community.type = 'text';
                    input_community.className = 'text_input';
                    input_community.name = 'community';
                    li_community.append(label_community);
                    li_community.append(input_community);
                    ul.append(li_community);

                    // SNMP version.
                    const li_version = document.createElement("li");
                    li_version.id = 'li_snmp_2';
                    const label_version = document.createElement("label");
                    label_version.textContent = '<?php echo __('SNMP version'); ?>';
                    const select_version = document.createElement("select");
                    select_version.name = 'version';
                    select_version.id = 'version';
                    select_version.onchange = function() {
                        showVersion();
                    };
                    let option1 = document.createElement("option");
                    let option2 = document.createElement("option");
                    let option2c = document.createElement("option");
                    let option3 = document.createElement("option");
                    option1.value = '1';
                    option1.text = '1';
                    option2.value = '2';
                    option2.text = '2';
                    option2c.value = '2c';
                    option2c.text = '2c';
                    option3.value = '3';
                    option3.text = '3';
                    select_version.appendChild(option1);
                    select_version.appendChild(option2);
                    select_version.appendChild(option2c);
                    select_version.appendChild(option3);
                    li_version.append(label_version);
                    li_version.append(select_version);
                    ul.append(li_version);
                    $("#version").select2();

                    // Security.
                    const li_security = document.createElement("li");
                    li_security.id = 'li_snmp_3';
                    const label_security = document.createElement("label");
                    label_security.textContent = '<?php echo __('Security level'); ?>';
                    const select_security = document.createElement("select");
                    select_security.name = 'securityLevelV3';
                    select_security.id = 'securityLevelV3';
                    select_security.onchange = function() {
                        showSecurity();
                    }
                    option1 = document.createElement("option");
                    option2 = document.createElement("option");
                    option3 = document.createElement("option");
                    option1.value = 'authNoPriv';
                    option1.text = '<?php echo __('Authenticated and non-private method'); ?>';
                    option2.value = 'authPriv';
                    option2.text = '<?php echo __('Authenticated and private method'); ?>';
                    option3.value = 'noAuthNoPriv';
                    option3.text = '<?php echo __('Non-authenticated and non-private method'); ?>';
                    select_security.appendChild(option1);
                    select_security.appendChild(option2);
                    select_security.appendChild(option3);
                    li_security.append(label_security);
                    li_security.append(select_security);
                    ul.append(li_security);
                    $("#securityLevelV3").select2();

                    // User.
                    const li_user = document.createElement("li");
                    li_user.id = 'li_snmp_4';
                    const label_user = document.createElement("label");
                    label_user.textContent = '<?php echo __('User authentication'); ?>';
                    const input_user = document.createElement("input");
                    input_user.type = 'text';
                    input_user.className = 'text_input';
                    input_user.name = 'authUserV3';
                    li_user.append(label_user);
                    li_user.append(input_user);
                    ul.append(li_user);

                    // Authentication method.
                    const li_method = document.createElement("li");
                    li_method.id = 'li_snmp_5';
                    const label_method = document.createElement("label");
                    label_method.textContent = '<?php echo __('Authentication method'); ?>';
                    const select_method = document.createElement("select");
                    select_method.name = 'authMethodV3';
                    select_method.id = 'method';
                    option1 = document.createElement("option");
                    option2 = document.createElement("option");
                    option1.value = 'MD5';
                    option1.text = '<?php echo __('MD5'); ?>';
                    option2.value = 'SHA';
                    option2.text = '<?php echo __('SHA'); ?>';
                    select_method.appendChild(option1);
                    select_method.appendChild(option2);
                    li_method.append(label_method);
                    li_method.append(select_method);
                    ul.append(li_method);
                    $("#method").select2();

                    // Password.
                    const li_password = document.createElement("li");
                    li_password.id = 'li_snmp_6';
                    const label_password = document.createElement("label");
                    label_password.textContent = '<?php echo __('Password authentication'); ?>';
                    const input_password = document.createElement("input");
                    input_password.type = 'password';
                    input_password.className = 'text_input';
                    input_password.name = 'authPassV3';
                    li_password.append(label_password);
                    li_password.append(input_password);
                    ul.append(li_password);

                    // Privacy method.
                    const li_privacy = document.createElement("li");
                    li_privacy.id = 'li_snmp_7';
                    const label_privacy = document.createElement("label");
                    label_privacy.textContent = '<?php echo __('Privacy method'); ?>';
                    const select_privacy = document.createElement("select");
                    select_privacy.name = 'privacyMethodV3';
                    select_privacy.id = 'privacy';
                    option1 = document.createElement("option");
                    option2 = document.createElement("option");
                    option1.value = 'AES';
                    option1.text = '<?php echo __('AES'); ?>';
                    option2.value = 'DES';
                    option2.text = '<?php echo __('DES'); ?>';
                    select_privacy.appendChild(option1);
                    select_privacy.appendChild(option2);
                    li_privacy.append(label_privacy);
                    li_privacy.append(select_privacy);
                    ul.append(li_privacy);
                    $("#privacy").select2();

                    // Privacy pass.
                    const li_privacyPassV3 = document.createElement("li");
                    li_privacyPassV3.id = 'li_snmp_8';
                    const label_privacyPassV3 = document.createElement("label");
                    label_privacyPassV3.textContent = '<?php echo __('Privacy pass'); ?>';
                    const input_privacyPassV3 = document.createElement("input");
                    input_privacyPassV3.type = 'password';
                    input_privacyPassV3.className = 'text_input';
                    input_privacyPassV3.name = 'privacyPassV3';
                    li_privacyPassV3.append(label_privacyPassV3);
                    li_privacyPassV3.append(input_privacyPassV3);
                    ul.append(li_privacyPassV3);

                    $('#li_snmp_3').hide();
                    $('#li_snmp_4').hide();
                    $('#li_snmp_5').hide();
                    $('#li_snmp_6').hide();
                    $('#li_snmp_7').hide();
                    $('#li_snmp_8').hide();
                }
            }
        }

        function showVersion() {
            if ($('#version').val() === '3') {
                $('#li_snmp_3').show();
                $('#li_snmp_4').show();
                $('#li_snmp_5').show();
                $('#li_snmp_6').show();
            } else {
                $('#li_snmp_3').hide();
                $('#li_snmp_4').hide();
                $('#li_snmp_5').hide();
                $('#li_snmp_6').hide();
                $('#li_snmp_7').hide();
                $('#li_snmp_8').hide();
            }
        }

        function showSecurity() {
            const value = $('#securityLevelV3').val();
            switch (value) {
                case 'noAuthNoPriv':
                    $('#li_snmp_4').show();
                    $('#li_snmp_5').hide();
                    $('#li_snmp_6').hide();
                    $('#li_snmp_7').hide();
                    $('#li_snmp_8').hide();
                break;

                case 'authPriv':
                    $('#li_snmp_4').show();
                    $('#li_snmp_5').show();
                    $('#li_snmp_6').show();
                    $('#li_snmp_7').show();
                    $('#li_snmp_8').show();
                break;

                case 'authNoPriv':
                default:
                    $('#li_snmp_4').show();
                    $('#li_snmp_5').show();
                    $('#li_snmp_6').show();
                    $('#li_snmp_7').hide();
                    $('#li_snmp_8').hide();
                break;
            }
        }

        function hideSNMP() {
            $('#li_snmp_1').hide();
            $('#li_snmp_2').hide();
            $('#li_snmp_3').hide();
            $('#li_snmp_4').hide();
            $('#li_snmp_5').hide();
            $('#li_snmp_6').hide();
            $('#li_snmp_7').hide();
            $('#li_snmp_8').hide();
        }

        /**
        * Process ajax responses and shows a dialog with results.
        */
        function showMsg(data) {
            var title = "<?php echo __('Success'); ?>";
            var text = '';
            var failed = 0;
            try {
                data = JSON.parse(data);
                text = data['result'];
            } catch (err) {
                title =  "<?php echo __('Failed'); ?>";
                text = err.message;
                failed = 1;
            }
            if (!failed && data['error'] != undefined) {
                title =  "<?php echo __('Failed'); ?>";
                text = data['error'];
                failed = 1;
            }
            if (data['report'] != undefined) {
                data['report'].forEach(function (item){
                    text += '<br>'+item;
                });
            }

            $('#msg').empty();
            $('#msg').html(text);
            $('#msg').dialog({
                width: 450,
                position: {
                    my: 'center',
                    at: 'center',
                    of: window,
                    collision: 'fit'
                },
                title: title,
                buttons: [
                    {
                        class: "ui-widget ui-state-default ui-corner-all ui-button-text-only sub ok submit-next",
                        text: 'OK',
                        click: function(e) {
                            if (!failed) {
                                $(".ui-dialog-content").dialog("close");
                                $('.info').hide();
                                cleanupDOM();
                                dt_keystore.draw(false);
                            } else {
                                $(this).dialog('close');
                            }
                        }
                    }
                ]
            });
        }

        /**
         * Loads modal from AJAX to add a new key or edit an existing one.
         */
        function show_form(id) {
            var btn_ok_text = '<?php echo __('OK'); ?>';
            var btn_cancel_text = '<?php echo __('Cancel'); ?>';
            var title = '<?php echo __('Register new key into keystore'); ?>';
            var method = 'addKey';
            if(id) {
                btn_ok_text = '<?php echo __('Update'); ?>';
                title = "<?php echo __('Update key'); ?> "+id;
                method = 'updateKey';
            }

            load_modal({
                target: $('#modal'),
                form: 'modal_form',
                url: '<?php echo ui_get_full_url('ajax.php', false, false, false); ?>',
                ajax_callback: showMsg,
                cleanup: cleanupDOM,
                modal: {
                    title: title,
                    ok: btn_ok_text,
                    cancel: btn_cancel_text,
                },
                extradata: [
                    {
                        name: 'identifier',
                        value: id,
                    }
                ],
                onload: function() {
                    calculate_inputs();
                },
                onshow: {
                    page: '<?php echo $this->ajaxController; ?>',
                    method: 'loadModal'
                },
                onsubmit: {
                    page: '<?php echo $this->ajaxController; ?>',
                    method: method
                }
            });

        }

        /**
         * Delete selected key
         */
        function delete_key(id) {
            $('#aux').empty();
            $('#aux').text('<?php echo __('Are you sure?'); ?>');
            $('#aux').dialog({
                title: '<?php echo __('Delete'); ?> ' + id,
                buttons: [
                    {
                        class: 'ui-widget ui-state-default ui-corner-all ui-button-text-only sub upd submit-cancel',
                        text: '<?php echo __('Cancel'); ?>',
                        click: function(e) {
                            $(this).dialog('close');
                            cleanupDOM();

                        }
                    },
                    {
                        text: 'Delete',
                        class: 'ui-widget ui-state-default ui-corner-all ui-button-text-only sub ok submit-next',
                        click: function(e) {
                            $.ajax({
                                method: 'post',
                                url: '<?php echo ui_get_full_url('ajax.php', false, false, false); ?>',
                                data: {
                                    page: 'godmode/groups/credential_store',
                                    method: 'deleteKey',
                                    identifier: id
                                },
                                datatype: "json",
                                success: function (data) {
                                    showMsg(data);
                                },
                                error: function(e) {
                                    showMsg(e);
                                }
                            });
                        }
                    }
                ]
            });
        }

        $(document).ready(function(){
            $("#button-create").on('click', function(){
                show_form();
            });
        });


    </script>
        <?php
        // EOF Javascript content.
        return ob_get_clean();
    }


}