Viewing File: /usr/local/cpanel/whostmgr/docroot/templates/support/support.js

/*
# whostmgr/docroot/tempaltes/support/support.js   Copyright(c) 2020 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/

/* global angular, $, confirm, PAGE */

/**
 * Angular application that handles granting cPanel support techs access to a cPanel/WHM server.
 *
 * @module TicketSupportApp
 *
 */

var TicketSupportApp = angular.module( "TicketSupportApp", [ "ui.bootstrap", "ngSanitize" ] );
TicketSupportApp.value("PAGE", PAGE);

/**
 * Service for the ticket list that will be used by support controllers
 */
TicketSupportApp.service( "AuthorizedTickets", function() {
    this.serial = 0;
    this.auths = [];
    this.authCount = 0;
    this.closedCount = 0;

    this.getClosedCount = function() {
        return this.closedCount;
    };

    this.getAuthCount = function() {
        return this.authCount;
    };

    this.clear = function() {
        this.auths.length = 0;
        this.closedCount = 0;
    };
    this.update = function( auth, updated_auth, index ) {

        // update an existing item
        angular.forEach( auth.auth, function(v, k) {
            if (updated_auth[k] === undefined) {
                updated_auth[k] = v;
            }
        });
        updated_auth._serial = ++this.serial;
        this.authCount++;
        this.auths[auth.$parent.$index].servers[index] = updated_auth;
    };
    this.extend = function(auths) {

        // add serials
        for ( var a = 0, authLength = auths.length; a < authLength; a++ ) {
            auths[a]._serial = ++this.serial;
            for ( var s = 0, servLength = auths[a].servers.length; s < servLength; s++ ) {

                // keep track of the array index before filtering
                auths[a].servers[s]._index = s;
                auths[a].servers[s]._serial = ++this.serial;
                if ( !auths[a].servers[s].bound ) {

                    // set the unbound servers flag in the parent ticket
                    auths[a].unboundServers = true;
                }
                if ( auths[a].servers[s].auth_status === "AUTHED" ) {
                    this.authCount++;
                }
                if ((auths[a].ticket_status === "CLOSED" || auths[a].ticket_status === "UNKNOWN") && auths[a].servers[s].auth_status === "EXPIRED") {
                    this.closedCount++;
                }
            }
        }

        // now extend the list
        angular.extend( this.auths, auths );
    };
    this.list = function() {
        return this.auths;
    };
    this.remove = function(auth, index) {
        var parentTicket = this.auths[auth.$parent.$index];
        if ( parentTicket.servers.length === 1 && auth.$parent.ticket.ticket_status !== "OPEN" ) {

            if (auth.$parent.ticket.ticket_status !== "OPEN") {
                this.closedCount--;
            }

            // only one auth, remove the ticket
            this.auths.splice( auth.$parent.$index, 1 );
        } else {

            // remove individual server info
            parentTicket.servers.splice( index, 1 );
            this.auths[auth.$parent.$index]._serial = ++this.serial;
        }
    };
    this.revoke_auth = function(auth, index) {

        // unset auth_status and auth information
        // we need a new object for this so that angular recognizes the change
        var new_auth = angular.copy(auth.auth);
        new_auth._serial = ++this.serial;
        new_auth.auth_status = "NOT_AUTHED";
        delete new_auth.auth_time;
        delete new_auth.ssh_test_result;
        this.auths[auth.$parent.$index].servers[index] = new_auth;
        this.auths[auth.$parent.$index]._serial = ++this.serial;
        this.authCount--;
    };
    this.removeClosedTickets = function() {

        // remove all closed/unknown tickets
        for ( var ticketIndex = 0; ticketIndex < this.auths.length; ticketIndex++ ) {
            var ticketStatus = this.auths[ticketIndex].ticket_status;
            if ( ticketStatus === "CLOSED" || ticketStatus === "UNKNOWN") {

                // remove the ticket and update the array index
                this.auths.splice( ticketIndex, 1 );
                ticketIndex--;
            }
        }
        this.closedCount = 0;
    };
    this.set_ssh_test_result = function(auth, ssh_test_result, index) {
        var new_auth = angular.copy(auth.auth);
        new_auth._serial = ++this.serial;
        new_auth.ssh_test_result = ssh_test_result;
        this.auths[auth.$parent.$index].servers[index] = new_auth;
        this.auths[auth.$parent.$index]._serial = ++this.serial;
    };
    this.generate_alert = function(auth, alert) {

        // set the alert on the ticket
        auth.$parent.ticket.alert = alert;
    };
});

/**
 * Controller that handles listing the customer's tickets
 *
 * @method List controller
 * @param {Object} $scope The Angular scope variable
 * @param {Object} $http The Angular HTTP request object
 */
TicketSupportApp.controller( "List", [ "$scope", "$http", "AuthorizedTickets", "PAGE",
    function($scope, $http, AuthorizedTickets, PAGE) {

        // load the service with the page's initial data set
        AuthorizedTickets.extend(PAGE.tickets);

        // initial states
        $scope.firewallProblem = PAGE.firewall.problem;
        $scope.alert = undefined;
        $scope.tickets = AuthorizedTickets.list();
        $scope.formData = {};
        $scope.isFixingFirewall = false;
        $scope.isProcessing = false;
        $scope.isTesting = false;
        $scope.connectionFailure = {
            "type": "danger",
            "msg": LOCALE.maketext("Your computer is unable to contact [output,acronym,WHM, WebHost Manager]. Check the connection to your server and reload your browser.")
        };

        /**
         * POSTs the formData bound structure to the WHM backend API and handles the result alert
         */
        $scope.load = function() {

            // make the request with our form, bound data
            $scope.isProcessing = true;
            $http({
                "method": "POST",
                "url": CPANEL.security_token + "/json-api/ticket_list?api.version=1",
                "data": $.param($scope.formData),
                "headers": { "Content-Type": "application/x-www-form-urlencoded" }
            }).success(function(data) {
                $scope.isProcessing = false;

                // make sure the user knows what happened
                if (undefined === data.metadata) {
                    $scope.alert = {
                        "type": "danger",
                        "msg": data.cpanelresult.error
                    };
                } else if (!data.metadata.result) {
                    var message = data.metadata.reason.replace(/, key.*/, "");
                    $scope.alert = {
                        "type": "danger",
                        "msg": message
                    };
                } else {

                    // clear and repopulate the list
                    $scope.alert = undefined;
                    AuthorizedTickets.clear();
                    AuthorizedTickets.extend(data.data.auths);
                }
            }).error(function() {
                $scope.isProcessing = false;
                $scope.alert = $scope.connectionFailure;
            });
        };

        /**
         * Provides the count of closed or expired server authorizations to the view
         */
        $scope.getClosedCount = function() {
            return AuthorizedTickets.getClosedCount();
        };

        /**
         * Provides the count of authorized servers to the view
         */
        $scope.getAuthCount = function() {
            return AuthorizedTickets.getAuthCount();
        };

        /**
         * POSTs the formData bound structure to the WHM backend API and handles the result as an alert
         */
        $scope.grant = function(index) {

            // make the request with the list item
            this.auth.isProcessing = true;
            var auth = this,
                ticket_id = this.$parent.ticket.ticket_id;
            $http({
                "method": "POST",
                "url": CPANEL.security_token + "/json-api/ticket_grant?api.version=1",
                "data": $.param({
                    "ticket_id": ticket_id,
                    "server_num": this.auth.server_num
                }),
                "headers": { "Content-Type": "application/x-www-form-urlencoded" }
            }).success(function(data) {
                auth.auth.isProcessing = false;

                // make sure the user knows what happened
                var alert;
                if (undefined === data.metadata) {
                    alert = {
                        "type": "danger",
                        "msg": data.cpanelresult.error
                    };
                } else if (!data.metadata.result) {
                    var message = data.metadata.reason.replace(/, key.*/, "");
                    alert = {
                        "type": "danger",
                        "msg": message
                    };
                } else {

                    // construct success message
                    alert = {
                        "type": "success",
                        "msg": LOCALE.maketext( "You successfully granted access for Ticket ID “[_1]” on Server “[_2]” - “[_3]” for User “[_4]”.", data.data.ticket_id, data.data.server_num, data.data.server_name, data.data.ssh_username )
                    };

                    // update ticket
                    AuthorizedTickets.update( auth, data.data, index );

                    // clear the firewall problem flag
                    $scope.firewallProblem = false;
                }
                AuthorizedTickets.generate_alert( auth, alert );
            }).error(function() {
                auth.auth.isProcessing = false;
                AuthorizedTickets.generate_alert( auth, $scope.connectionFailure);
            });
        };

        /**
         * POSTs the formData bound structure to the WHM backend API and handles the result by manipulating the auth service
         */
        $scope.revoke = function(index) {
            var auth = this,
                ticket_id = auth.$parent.ticket.ticket_id;
            if (auth.auth.ticket_status === "CLOSED" || auth.auth.auth_status === "EXPIRED") {
                if (!confirm(LOCALE.maketext( "Ticket ID “[_1]” is closed. Do you want to revoke and remove this authorization?", ticket_id ))) {
                    return;
                }
            }
            auth.auth.isProcessing = true;
            auth.auth.isTesting    = false;
            $http({
                "method": "POST",
                "url": CPANEL.security_token + "/json-api/ticket_revoke?api.version=1",
                "data": $.param({
                    "ticket_id": ticket_id,
                    "server_num": this.auth.server_num,
                    "ssh_username": this.auth.ssh_username
                }),
                "headers": { "Content-Type": "application/x-www-form-urlencoded" }
            }).success(function(data) {

                // make sure the user knows what happened
                auth.auth.isProcessing = false;
                var alert, globalAlert;
                if (undefined === data.metadata) {
                    alert = {
                        "type": "danger",
                        "msg": data.cpanelresult.error
                    };
                } else if (!data.metadata.result) {
                    alert = {
                        "type": "danger",
                        "msg": data.metadata.reason
                    };
                } else {
                    var tmp_server_name = auth.auth.server_name !== undefined ? auth.auth.server_name : "undef";
                    var tmp_ssh_username = auth.auth.ssh_username !== undefined ? auth.auth.ssh_username : "undef";
                    alert = {
                        "type": "success",
                        "msg": LOCALE.maketext( "You successfully revoked access for Ticket ID “[_1]” on Server “[_2]” - “[_3]” for User “[_4]”.", data.data.ticket_id, data.data.server_num, tmp_server_name, tmp_ssh_username )
                    };

                    globalAlert = 0;
                    if (auth.ticket.ticket_status === "CLOSED" || auth.ticket.ticket_status === "UNKNOWN" || auth.auth.auth_status === "EXPIRED") {

                        // when the ticket is not open, and there's only one server left in it, display a global alert //
                        if (auth.ticket.ticket_status !== "OPEN" && 1 === auth.$parent.ticket.servers.length) {
                            globalAlert = 1;
                        }

                        // remove the item from the array
                        AuthorizedTickets.remove(auth, index);
                    } else {

                        // update
                        AuthorizedTickets.revoke_auth(auth, index);
                    }

                    // clear the firewall problem flag
                    $scope.firewallProblem = false;
                }

                if (globalAlert) {

                    // if we're out of tickets, do a global alert
                    $scope.alert = alert;
                } else {

                    // otherwise constrain to the relevant ticket
                    AuthorizedTickets.generate_alert( auth, alert );
                }
            }).error(function() {
                auth.auth.isProcessing = false;
                AuthorizedTickets.generate_alert( auth, $scope.connectionFailure);
            });
        };

        /**
         * POSTs the formData bound structure to the WHM backend API and handles the result as an alert
         */
        $scope.removeFromClosed = function() {

            if (!confirm(LOCALE.maketext("Are you sure you want to revoke authorization for all closed tickets and remove them from the list?"))) {
                return;
            }

            // request all closed ticket authorizations be revoked and removed
            $scope.isProcessing = true;
            $http({
                "method": "POST",
                "url": CPANEL.security_token + "/json-api/ticket_remove_closed?api.version=1",
                "data": {},
                "headers": { "Content-Type": "application/x-www-form-urlencoded" }
            }).success(function(data) {
                $scope.isProcessing = false;

                // make sure the user knows what happened
                if (undefined === data.metadata) {
                    $scope.alert = {
                        "type": "danger",
                        "msg": data.cpanelresult.error
                    };
                } else if (!data.metadata.result) {
                    var message = data.metadata.reason.replace(/, key.*/, "");
                    $scope.alert = {
                        "type": "danger",
                        "msg": message
                    };
                } else {

                    // construct success message
                    $scope.alert = {
                        "type": "success",
                        "msg": LOCALE.maketext("Successfully revoked and removed authorizations from all closed tickets.")
                    };

                    // update the list now
                    AuthorizedTickets.removeClosedTickets();
                }
            }).error(function() {
                $scope.isProcessing = false;
                $scope.alert = {
                    "type": "danger",
                    "msg": LOCALE.maketext("Failed to call backend to revoke and remove authorizations from all closed tickets!")
                };
            });
        };

        /**
         * Starts an SSH connection test via the WHM backend.
         */
        $scope.ssh_test = function(index) {
            var auth = this,
                ticket_id = auth.$parent.ticket.ticket_id;
            auth.auth.isTesting = true;
            $http({
                "method": "POST",
                "url": CPANEL.security_token + "/json-api/ticket_ssh_test?api.version=1",
                "data": $.param({ "ticket_id": ticket_id, "server_num": this.auth.server_num }),
                "headers": { "Content-Type": "application/x-www-form-urlencoded" }
            }).success(function(data) {
                if (!auth.auth.isTesting) {
                    return;
                }

                // make sure the user knows what happened
                auth.auth.isTesting = false;
                var alert;
                if (undefined === data.metadata) {
                    alert = {
                        "type": "danger",
                        "msg": data.cpanelresult.error
                    };
                } else if (!data.metadata.result) {
                    alert = {
                        "type": "danger",
                        "msg": data.metadata.reason
                    };
                } else if (data.data.result !== "SUCCESS") {

                    // let user know what happened ...
                    alert = {
                        "type": "danger",
                        "msg": LOCALE.maketext( "The [asis,SSH] test failed with the following error: “[_1]”", data.data.result ) + " " + LOCALE.maketext( "For more information, read our [output,url,_1,Grant cPanel Support Access,target,_2] documentation.", "https://go.cpanel.net/cpanelsupportaccess", "_new" )
                    };

                    // ... and set test result
                    AuthorizedTickets.set_ssh_test_result( auth, data.data.result, index );
                } else {

                    // everything was ok
                    alert = {
                        "type": "success",
                        "msg": LOCALE.maketext( "The [asis,SSH] connection test was successful for Ticket ID “[_1]” on Server “[_2]” - “[_3]” for User “[_4]”.", ticket_id, auth.auth.server_num, auth.auth.server_name, auth.auth.ssh_username )
                    };

                    // set test result
                    AuthorizedTickets.set_ssh_test_result( auth, data.data.result, index );
                }
                AuthorizedTickets.generate_alert( auth, alert );
            }).error(function() {
                if (!auth.auth.isTesting) {
                    return;
                }
                auth.auth.isTesting = false;
                AuthorizedTickets.generate_alert( auth, $scope.connectionFailure);
            });
        };

        /**
         * Starts the process of fixing the firewall configuration
         */
        $scope.fixFirewall = function() {
            $scope.isFixingFirewall = true;
            var apiProblemResolution;

            if ( $scope.firewallProblem === "NEED_SETUP" ) {
                apiProblemResolution = "ticket_whitelist_setup";
            } else {
                apiProblemResolution = "ticket_whitelist_unsetup";
            }

            $http({
                "method": "POST",
                "url": CPANEL.security_token + "/json-api/" + apiProblemResolution + "?api.version=1",
                "headers": { "Content-Type": "application/x-www-form-urlencoded" }
            }).success(function(data) {
                if (!$scope.isFixingFirewall) {
                    return;
                }

                if (undefined === data.metadata) {
                    $scope.alert = {
                        "type": "danger",
                        "msg": data.cpanelresult.error
                    };
                } else if (!data.metadata.result) {
                    $scope.alert = {
                        "type": "danger",
                        "msg": data.metadata.reason
                    };
                } else {

                    // unset the problem flag
                    $scope.firewallProblem = false;

                    $scope.alert = {
                        "type": "success",
                        "msg": LOCALE.maketext( "The server’s firewall configuration has been updated." )
                    };

                }
                $scope.isFixingFirewall = false;
            }).error(function() {
                if (!$scope.isFixingFirewall) {
                    return;
                }

                $scope.alert = $scope.connectionFailure;
                $scope.isFixingFirewall = false;
            });
        };
    }
]);

/**
 * Filter that converts date strings to friendly messages
 *
 * @method fromNow filter
 * @param {Object} dateString A datetime in epoch format
 */
TicketSupportApp.filter( "fromNow", function() {
    return function(dateString) {
        var current_date = new Date();
        var current_epoch = current_date.getTime() / 1000;
        var days_passed = ( current_epoch - dateString ) / ( 3600 * 24 );
        if (days_passed <= 2.0) {
            var passed_date = new Date(dateString * 1000);

            // adjust days_passed to prevent rounding issues
            days_passed = current_date.getDay() - passed_date.getDay();
            if (days_passed < 0) {

                // accommodate current_date being Sunday (0) and grant from day before Saturday (6)
                days_passed += 7.0;
            }
            if (days_passed === 1) {
                return LOCALE.maketext("Yesterday");
            } else if (days_passed >= 2) {
                return LOCALE.maketext( "[quant,_1,day,days] ago.", days_passed );
            }
            return LOCALE.maketext("Today");
        } else if (days_passed < 7.0) {
            return LOCALE.maketext( "[quant,_1,day,days] ago.", parseInt(days_passed, 10) );
        } else if (days_passed < 14.0) {
            return LOCALE.maketext("A week ago.");
        }
        return LOCALE.maketext( "[quant,_1,week,weeks] ago.", Math.ceil(days_passed / 7.0) );
    };
});

/**
 * A filter that returns an array of server authorization objects with IP addresses bound to this server
 *
 * @method boundServerFilter filter
 * @param {boolean} showAllServers A boolean value to alternatively return all servers
 */
TicketSupportApp.filter( "boundServerFilter", function() {
    return function(servers, showAllServers) {
        if ( showAllServers ) {
            return servers;
        } else {
            var boundServers = [];
            angular.forEach(servers, function(server) {
                if ( server.bound ) {
                    boundServers.push(server);
                }
            });
            return boundServers;
        }
    };
});
Back to Directory File Manager