<?php

/**
 * Double Authentication Ajax file.
 *
 * @category   Users
 * @package    Pandora FMS
 * @subpackage Community
 * @version    1.0.0
 * @license    See below
 *
 *    ______                 ___                    _______ _______ ________
 * |   __ \.-----.--.--.--|  |.-----.----.-----. |    ___|   |   |     __|
 * |    __/|  _  |     |  _  ||  _  |   _|  _  | |    ___|       |__     |
 * |___|   |___._|__|__|_____||_____|__| |___._| |___|   |__|_|__|_______|
 *
 * ============================================================================
 * Copyright (c) 2005-2023 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.
 * ============================================================================
 */

// Begin.
global $config;

// Login check.
check_login();

// Security check.
$id_user = (string) get_parameter('id_user');
$FA_forced = (int) get_parameter('FA_forced');
$id_user_auth = (string) get_parameter('id_user_auth', $config['id_user']);


if ($id_user !== $config['id_user'] && $FA_forced != 1) {
    db_pandora_audit(
        AUDIT_LOG_ACL_VIOLATION,
        'Trying to access Double Authentication'
    );
    echo json_encode(-1);
    return;
}

// Load the class.
require_once $config['homedir'].'/include/auth/GAuth/Auth.php';

// Default lenght of the secret.
$secret_lenght = 16;
// Default lenght of the code.
$code_lenght = 6;

// Generate a new secret for the user.
$generate_double_auth_secret = (bool) get_parameter('generate_double_auth_secret');
if ($generate_double_auth_secret) {
    $gAuth = new \GAuth\Auth();
    $code = $gAuth->generateCode($secret_lenght);

    echo json_encode($code);
    return;
}

// Validate the provided secret with a code provided by the user.
// If the parameter 'save' is set to true, the secret will
// be stored into the database.
// The results can be true, false or 1 if the validation is true
// but the secret can't be stored into the database.
$validate_double_auth_code = (bool) get_parameter('validate_double_auth_code');
if ($validate_double_auth_code) {
    $result = false;

    $secret = (string) get_parameter('secret');

    if (!empty($secret) && strlen($secret) === $secret_lenght) {
        $code = (string) get_parameter('code');

        if (!empty($code) && strlen($code) === $code_lenght) {
            $save = (bool) get_parameter('save');

            if (!empty($code)) {
                $gAuth = new \GAuth\Auth($secret);
                $result = $gAuth->validateCode($code);
            }

            if ($result && $save) {
                // Delete the actual value (if exists)
                $where = ['id_user' => $id_user_auth];
                db_process_sql_delete('tuser_double_auth', $where);

                // Insert the new value
                $values = [
                    'id_user' => $id_user_auth,
                    'secret'  => $secret,
                ];
                $result = (bool) db_process_sql_insert('tuser_double_auth', $values);

                if (!$result) {
                    $result = 1;
                }
            }
        }
    }

    echo json_encode($result);
    return;
}

// Set the provided secret to the user.
$save_double_auth_secret = (bool) get_parameter('save_double_auth_secret');
if ($save_double_auth_secret) {
    $result = false;

    $secret = (string) get_parameter('secret');

    if (strlen($secret) === $secret_lenght) {
        // Delete the actual value (if exists).
        $where = ['id_user' => $id_user];
        db_process_sql_delete('tuser_double_auth', $where);
        // Insert the new value.
        $values = [
            'id_user' => $id_user,
            'secret'  => $secret,
        ];
        $result = (bool) db_process_sql_insert('tuser_double_auth', $values);
    }

    echo json_encode($result);
    return;
}

// Disable the double auth for the user.
$deactivate_double_auth = (bool) get_parameter('deactivate_double_auth');
if ($deactivate_double_auth) {
    $result = false;

    // Delete the actual value (if exists).
    $where = ['id_user' => $id_user];
    $result = db_process_sql_delete('tuser_double_auth', $where);

    echo json_encode($result);
    return;
}

// Get the info page to the container dialog.
$get_double_auth_data_page = (bool) get_parameter('get_double_auth_data_page');
if ($get_double_auth_data_page) {
    $secret = db_get_value('secret', 'tuser_double_auth', 'id_user', $id_user);

    if (empty($secret)) {
        return;
    }

    $html = '';
    $html .= '<div class="left_align">';
    $html .= '<p>';
    $html .= __('This is the private code that you should use with your authenticator app').'. ';
    $html .= __('You could enter the code manually or use the QR code to add it automatically').'.';
    $html .= '</p>';
    $html .= '</div>';
    $html .= '<div class="center_align">';
    $html .= __('Code').': <b>'.$secret.'</b>';
    $html .= '<br>';
    $html .= __('QR').': <br>';
    $html .= '<div id="qr-container"></div>';
    $html .= '</div>';

    ob_clean();
    ?>
    
<script type="text/javascript" src="../../include/javascript/qrcode.js"></script>
<script type="text/javascript">

    var secret = "<?php echo $secret; ?>";
    var id_user_auth = "<?php echo $id_user_auth; ?>";

    // QR code with the secret to add it to the app.
    paint_qrcode("otpauth://totp/"+id_user_auth+"?secret="+secret, $("div#qr-container").get(0), 200, 200);

    $("div#qr-container").attr("title", "").find("canvas").remove();
    // Don't delete this timeout. It's necessary to perform the style change.
    // Chrome min. milliseconds: 1.
    // Firefox min. milliseconds: 9.
    setTimeout(function() {
            $("div#qr-container").find("img").attr("style", "");
        }, 10);
</script>
    <?php
    $html .= ob_get_clean();

    echo $html;
    return;
}

// Get the info page to the container dialog.
$get_double_auth_info_page = (bool) get_parameter('get_double_auth_info_page');
if ($get_double_auth_info_page) {
    $container_id = (string) get_parameter('containerID');

    $html = '';
    $html .= '<div class="left_align">';
    $html .= '<p>';
    $html .= __('You are about to activate the double authentication').'. ';
    $html .= __(
        'With this option enabled, your account access will be more secure, 
		cause a code generated by other application will be required after the login'
    ).'. ';
    $html .= '</p>';
    $html .= '<p>';
    $html .= __('You will need to install the app from the following link before continue').'. ';
    $html .= '</p>';
    $html .= '</div>';
    $html .= '<br>';
    $html .= '<div class="flex flex-space-around">';
    $html .= html_print_button(__('Download the app'), 'google_authenticator_download', false, '', '', true);
    $html .= html_print_button(__('Continue'), 'continue_to_generate', false, '', '', true);
    $html .= '</div>';

    ob_clean();
    ?>
<script type="text/javascript">
    // Open the download page on click.
    $("#button-google_authenticator_download").click(function (e) {
        e.preventDefault();
        window.open("https://support.google.com/accounts/answer/1066447");
    });

    // Change the container content with the generation page.
    $("#button-continue_to_generate").click(function (e) {
        e.preventDefault();

        if (!confirm("<?php echo __('Are you installed the app yet?'); ?>")) {
            return false;
        }

        var containerID = "<?php echo $container_id; ?>";
        var id_user_auth = "<?php echo $id_user_auth; ?>";

        $("#"+containerID).html("<img src=\"<?php echo $config['homeurl']; ?>/images/spinner.gif\" />");

        $.ajax({
            url: "<?php echo ui_get_full_url('ajax.php', false, false, false); ?>",
            type: 'POST',
            dataType: 'html',
            data: {
                page: 'include/ajax/double_auth.ajax',
                id_user: "<?php echo $config['id_user']; ?>",
                id_user_auth: id_user_auth,
                get_double_auth_generation_page: 1,
                containerID: containerID
            },
            complete: function(xhr, textStatus) {
                
            },
            success: function(data, textStatus, xhr) {
                // isNaN = is not a number
                if (isNaN(data)) {
                    $("#"+containerID).html(data);
                }
                // data is a number, convert it to integer to do the compare
                else if (Number(data) === -1) {
                    $("#"+containerID).html("<?php echo '<b><div class=\"red\">'.__('Authentication error').'</div></b>'; ?>");
                }
                else {
                    $("#"+containerID).html("<?php echo '<b><div class=\"red\">'.__('Error').'</div></b>'; ?>");
                }
            },
            error: function(xhr, textStatus, errorThrown) {
                $("#"+containerID).html("<?php echo '<b><div class=\"red\">'.__('There was an error loading the data').'</div></b>'; ?>");
            }
        });
    });
</script>
    <?php
    $html .= ob_get_clean();

    echo $html;
    return;
}

// Get the page that generates a secret for the user.
$get_double_auth_generation_page = (bool) get_parameter('get_double_auth_generation_page');
if ($get_double_auth_generation_page) {
    $container_id = (string) get_parameter('containerID');

    $gAuth = new \GAuth\Auth();
    $secret = $gAuth->generateCode($secret_lenght);

    $html = '';
    $html .= '<div class="center_align">';
    $html .= '<p>';
    $html .= '<b>'.__('A private code has been generated').'</b>.';
    $html .= '</p>';
    $html .= '</div>';
    $html .= '<div class="left_align">';
    $html .= '<p>';
    $html .= __('Before continue, you should create a new entry into the authenticator app').'. ';
    $html .= __('You could enter the code manually or use the QR code to add it automatically').'.';
    $html .= '</p>';
    $html .= '</div>';
    $html .= '<div class="center_align">';
    $html .= __('Code').': <b>'.$secret.'</b>';
    $html .= '<br>';
    $html .= __('QR').': <br>';
    $html .= '<div id="qr-container"></div>';
    $html .= '<br><div class="flex flex-space-around">';
    $html .= html_print_button(__('Refresh code'), 'continue_to_generate', false, '', '', true);
    $html .= html_print_button(__('Continue'), 'continue_to_validate', false, '', '', true);
    $html .= '</div>';
    $html .= '</div>';

    ob_clean();
    ?>

<script type="text/javascript" src="../../include/javascript/qrcode.js"></script>
<script type="text/javascript">
    var secret = "<?php echo $secret; ?>";
    var id_user_auth = "<?php echo $id_user_auth; ?>";

    // QR code with the secret to add it to the app
    paint_qrcode("otpauth://totp/"+id_user_auth+"?secret="+secret, $("div#qr-container").get(0), 200, 200);

    $("div#qr-container").attr("title", "").find("canvas").remove();
    // Don't delete this timeout. It's necessary to perform the style change.
    // Chrome min. milliseconds: 1.
    // Firefox min. milliseconds: 9.
    setTimeout(function() {
            $("div#qr-container").find("img").attr("style", "");
        }, 10);

    // Load the same page with another secret
    $("#button-continue_to_generate").click(function(e) {
        e.preventDefault();

        var containerID = "<?php echo $container_id; ?>";

        $("#"+containerID).html("<img src=\"<?php echo $config['homeurl']; ?>/images/spinner.gif\" />");

        $.ajax({
            url: "<?php echo ui_get_full_url('ajax.php', false, false, false); ?>",
            type: 'POST',
            dataType: 'html',
            data: {
                page: 'include/ajax/double_auth.ajax',
                id_user: "<?php echo $config['id_user']; ?>",
                id_user_auth, id_user_auth,
                get_double_auth_generation_page: 1,
                containerID: containerID
            },
            complete: function(xhr, textStatus) {
                
            },
            success: function(data, textStatus, xhr) {
                // isNaN = is not a number
                if (isNaN(data)) {
                    $("#"+containerID).html(data);
                }
                // data is a number, convert it to integer to do the compare
                else if (Number(data) === -1) {
                    $("#"+containerID).html("<?php echo '<b><div class=\"red\">'.__('Authentication error').'</div></b>'; ?>");
                }
                else {
                    $("#"+containerID).html("<?php echo '<b><div class=\"red\">'.__('Error').'</div></b>'; ?>");
                }
            },
            error: function(xhr, textStatus, errorThrown) {
                $("#"+containerID).html("<?php echo '<b><div class=\"red\">'.__('There was an error loading the data').'</div></b>'; ?>");
            }
        });
    });

    // Load the validation page
    $("#button-continue_to_validate").click(function(e) {
        e.preventDefault();
        
        if (!confirm("<?php echo __('Are you introduced the code in the authenticator app yet?'); ?>")) {
            return false;
        }

        var containerID = "<?php echo $container_id; ?>";

        $("#"+containerID).html("<img src=\"<?php echo $config['homeurl']; ?>/images/spinner.gif\" />");

        $.ajax({
            url: "<?php echo ui_get_full_url('ajax.php', false, false, false); ?>",
            type: 'POST',
            dataType: 'html',
            data: {
                page: 'include/ajax/double_auth.ajax',
                id_user: "<?php echo $config['id_user']; ?>",
                id_user_auth: id_user_auth,
                get_double_auth_validation_page: 1,
                secret: secret,
                containerID: containerID
            },
            complete: function(xhr, textStatus) {
                
            },
            success: function(data, textStatus, xhr) {
                // isNaN = is not a number
                if (isNaN(data)) {
                    $("#"+containerID).html(data);
                }
                // data is a number, convert it to integer to do the compare
                else if (Number(data) === -1) {
                    $("#"+containerID).html("<?php echo '<b><div class=\"red\">'.__('Authentication error').'</div></b>'; ?>");
                }
                else {
                    $("#"+containerID).html("<?php echo '<b><div class=\"red\">'.__('Error').'</div></b>'; ?>");
                }
            },
            error: function(xhr, textStatus, errorThrown) {
                $("#"+containerID).html("<?php echo '<b><div class=\"red\">'.__('There was an error loading the data').'</div></b>'; ?>");
            }
        });
    });
</script>
    <?php
    $html .= ob_get_clean();

    echo $html;
    return;
}

// Get the validation page
$get_double_auth_validation_page = (bool) get_parameter('get_double_auth_validation_page');
if ($get_double_auth_validation_page) {
    $container_id = (string) get_parameter('containerID');
    $secret = (string) get_parameter('secret');

    if (empty($secret) || strlen($secret) != $secret_lenght) {
        echo json_encode(false);
        return;
    }

    $html = '';
    $html .= '<div class="left_align">';
    $html .= '<p>';
    $html .= __('Introduce a code generated by the app').'. ';
    $html .= __('If the code is valid, the double authentication will be activated').'.';
    $html .= '</p>';
    $html .= '</div>';
    $html .= '<br>';
    $html .= '<div class="center_align">';
    $html .= html_print_input_text('code', '', '', 50, $secret_lenght, true);
    $html .= '<div id="code_input_message" class="red"></div>';
    $html .= '<br><br>';
    $html .= '<div id="button-container" class="flex flex-space-around">';
    $html .= html_print_button(__('Validate code'), 'continue_to_validate', false, '', '', true);
    $html .= html_print_image('images/spinner.gif', true);
    $html .= '</div>';
    $html .= '</div>';

    ob_clean();
    ?>
<script type="text/javascript">
    $("div#button-container").find("img").hide();

    // Start the error message hiden
    $("div#code_input_message").hide();

    var secret = "<?php echo $secret; ?>";

    $("input#text-code").keypress(function() {
        $(this).removeClass("red").css('border-color', '#cbcbcb');
    });

    $("#button-continue_to_validate").click(function(e) {
        e.preventDefault();

        // Hide the error message
        $("div#code_input_message").hide();
        
        var containerID = "<?php echo $container_id; ?>";

        $("#button-continue_to_validate").prop('enabled', false).hide();
        $("div#button-container").find("img").show();

        $.ajax({
            url: "<?php echo ui_get_full_url('ajax.php', false, false, false); ?>",
            type: 'POST',
            dataType: 'json',
            data: {
                page: 'include/ajax/double_auth.ajax',
                id_user: "<?php echo $config['id_user']; ?>",
                id_user_auth: id_user_auth,
                validate_double_auth_code: 1,
                save: 1,
                secret: secret,
                code: function () {
                    return $("input#text-code").val();
                },
                containerID: containerID
            },
            complete: function(xhr, textStatus) {
                
            },
            success: function(data, textStatus, xhr) {
                // Valid code
                if (data === true) {
                    $("#"+containerID).html("<b><?php echo '<b><div class=\"green\">'.__('The code is valid, you can exit now').'</div></b>'; ?></b>");
                    $("input#checkbox-double_auth").prop( "checked", true );
                }
                // Invalid code
                else if (data === false) {
                    $("#button-continue_to_validate").prop('enabled', true).show();
                    $("div#button-container").find("img").hide();
                    $("input#text-code").addClass("red").css('border-color', '#c00');

                    $("div#code_input_message").html("<?php echo __('Invalid code'); ?>").show();
                }
                // Valid code but not saved
                else if (data === 1) {
                    $("#button-continue_to_validate").prop('enabled', true).show();
                    $("div#button-container").find("img").hide();
                    $("input#text-code").addClass("red").css('border-color', '#c00');

                    $("div#code_input_message").html("<?php echo __('The code is valid, but it was an error saving the data'); ?>").show();
                }
                // Authentication error
                else if (data === -1) {
                    $("#"+containerID).html("<?php echo '<b><div class=\"red\">'.__('Authentication error').'</div></b>'; ?>");
                }
                // Not expected results
                else {
                    $("#"+containerID).html("<?php echo '<b><div class=\"red\">'.__('Error').'</div></b>'; ?>");
                }
            },
            error: function(xhr, textStatus, errorThrown) {
                $("#"+containerID).html("<?php echo '<b><div class=\"red\">'.__('There was an error loading the data').'</div></b>'; ?>");
            }
        });
    });
</script>
    <?php
    $html .= ob_get_clean();

    echo $html;
    return;
}

return;