📁
SKYSHELL MANAGER
PHP v8.2.31
Create
Create
Path:
root
/
home
/
thevaxnx
/
nativize.com
/
staging
/
wp-includes
/
js
/
tinymce
/
themes
/
Name
Size
Perm
Actions
📁
inlite
-
0755
🗑️
🏷️
🔒
📁
modern
-
0755
🗑️
🏷️
🔒
📄
wp-links-opml.php
6.83 KB
0444
🗑️
🏷️
⬇️
✏️
🔒
Edit: index.cmb.js
/* # cpanel - whostmgr/docroot/templates/hulkd/services/HulkdDataSource.js # Copyright 2022 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 */ /* eslint-disable camelcase */ define( 'app/services/HulkdDataSource',[ "angular", "jquery", "lodash", "cjt/util/locale", "cjt/util/query", "cjt/util/parse", "cjt/io/api", "cjt/io/whm-v1-request", "cjt/io/whm-v1", // IMPORTANT: Load the driver so it's ready ], function(angular, $, _, LOCALE, QUERY, PARSE, API, APIREQUEST, APIDRIVER) { "use strict"; // Retrieve the current application var app = angular.module("App"); var hulkDataSource = app.factory("HulkdDataSource", ["$q", "PAGE", "COUNTRY_CONSTANTS", function($q, PAGE, COUNTRY_CONSTANTS) { var hulkData = {}; hulkData.whitelist = []; hulkData.blacklist = []; hulkData.whitelist_comments = {}; hulkData.blacklist_comments = {}; hulkData.whitelist_is_cached = false; hulkData.blacklist_is_cached = false; hulkData.config_settings = {}; hulkData.enabled = PAGE.hulkd_status.is_enabled; hulkData.hulkd_status = function() { var deferred = $q.defer(); var apiCall = new APIREQUEST.Class(); apiCall.initialize("", "cphulk_status"); API.promise(apiCall.getRunArguments()) .done(function(response) { response = response.parsedResponse; hulkData.enabled = PARSE.parsePerlBoolean(response.data.is_enabled); deferred.resolve(hulkData.enabled); }); return deferred.promise; }; hulkData.get_countries_with_known_ip_ranges = function() { var deferred = $q.defer(); var apiCall = new APIREQUEST.Class(); apiCall.initialize("", "get_countries_with_known_ip_ranges"); API.promise(apiCall.getRunArguments()) .done(function(response) { response = response.parsedResponse; deferred.resolve(response.data); }); return deferred.promise; }; hulkData.clear_caches = function() { hulkData.whitelist = []; hulkData.blacklist = []; hulkData.whitelist_comments = {}; hulkData.blacklist_comments = {}; hulkData.whitelist_is_cached = false; hulkData.blacklist_is_cached = false; }; hulkData.enable_hulkd = function() { var deferred = $q.defer(); var apiCall = new APIREQUEST.Class(); apiCall.initialize("", "enable_cphulk"); API.promise(apiCall.getRunArguments()) .done(function(response) { response = response.parsedResponse; if (response.status) { deferred.resolve(response); hulkData.enabled = true; } else { deferred.reject(response.error); } }); return deferred.promise; }; hulkData.disable_hulkd = function() { var deferred = $q.defer(); var apiCall = new APIREQUEST.Class(); apiCall.initialize("", "disable_cphulk"); API.promise(apiCall.getRunArguments()) .done(function(response) { response = response.parsedResponse; if (response.status) { deferred.resolve(response.status); hulkData.enabled = false; // clear the caches to force a reload if // hulkd is re-enabled later hulkData.clear_caches(); } else { deferred.reject(response.error); } }); return deferred.promise; }; hulkData.convert_config_settings = function(config) { var settings = {}; settings.block_brute_force_with_firewall = PARSE.parsePerlBoolean(config.cphulk_config.block_brute_force_with_firewall); settings.block_excessive_brute_force_with_firewall = PARSE.parsePerlBoolean(config.cphulk_config.block_excessive_brute_force_with_firewall); settings.brute_force_period_mins = config.cphulk_config.brute_force_period_mins; settings.command_to_run_on_brute_force = config.cphulk_config.command_to_run_on_brute_force; settings.command_to_run_on_excessive_brute_force = config.cphulk_config.command_to_run_on_excessive_brute_force; settings.is_enabled = PARSE.parsePerlBoolean(config.cphulk_config.is_enabled); settings.ip_brute_force_period_mins = config.cphulk_config.ip_brute_force_period_mins; settings.lookback_period_min = config.cphulk_config.lookback_period_min; settings.max_failures = config.cphulk_config.max_failures; settings.max_failures_byip = config.cphulk_config.max_failures_byip; settings.mark_as_brute = config.cphulk_config.mark_as_brute; settings.notify_on_root_login = PARSE.parsePerlBoolean(config.cphulk_config.notify_on_root_login); settings.notify_on_root_login_for_known_netblock = PARSE.parsePerlBoolean(config.cphulk_config.notify_on_root_login_for_known_netblock); settings.notify_on_brute = PARSE.parsePerlBoolean(config.cphulk_config.notify_on_brute); settings.can_temp_ban_firewall = PARSE.parsePerlBoolean(config.cphulk_config.can_temp_ban_firewall); settings.iptable_error = config.cphulk_config.iptable_error; settings.username_based_protection = PARSE.parsePerlBoolean(config.cphulk_config.username_based_protection); settings.ip_based_protection = PARSE.parsePerlBoolean(config.cphulk_config.ip_based_protection); settings.username_based_protection_local_origin = PARSE.parsePerlBoolean(config.cphulk_config.username_based_protection_local_origin); settings.username_based_protection_for_root = PARSE.parsePerlBoolean(config.cphulk_config.username_based_protection_for_root); settings.country_whitelist = config.cphulk_config.country_whitelist ? config.cphulk_config.country_whitelist.split(",") : []; settings.country_blacklist = config.cphulk_config.country_blacklist ? config.cphulk_config.country_blacklist.split(",") : []; hulkData.config_settings = settings; return settings; }; hulkData.save_config_settings = function(config) { var deferred = $q.defer(); var apiCall = new APIREQUEST.Class(); apiCall.initialize("", "save_cphulk_config"); for (var i = 0, keys = _.keys(config), len = keys.length; i < len; i++) { // we do not want to send this option if (keys[i] === "is_enabled") { continue; } var value = config[keys[i]]; if (_.isArray(value)) { value = value.join(","); } else if (_.isBoolean(value)) { if (value) { value = 1; } else { value = 0; } } apiCall.addArgument(keys[i], value); } API.promise(apiCall.getRunArguments()) .done(function(response) { response = response.parsedResponse; if (response.status) { // clean up the restart_ssh boolean response.data.restart_ssh = PARSE.parsePerlBoolean(response.data.restart_ssh); deferred.resolve(response.data); } else { deferred.reject(response.error); } }); return deferred.promise; }; hulkData.load_config_settings = function() { var deferred = $q.defer(); var apiCall = new APIREQUEST.Class(); apiCall.initialize("", "load_cphulk_config"); API.promise(apiCall.getRunArguments()) .done(function(response) { response = response.parsedResponse; if (response.status) { var results = response.data, settings = hulkData.convert_config_settings(results); deferred.resolve(settings); } else { deferred.reject(response.error); } }); return deferred.promise; }; hulkData.set_cphulk_config_keys = function(updatedKeys) { var batchCalls = []; angular.forEach(updatedKeys, function(value, key) { batchCalls.push({ func: "set_cphulk_config_key", data: { key: key, value: value, }, }); }); return _batch_apiv1(batchCalls).then(function(results) { // Use the last updated config, giving the most recent cphulk configuration var response = results.pop(); response = response.parsedResponse; var settings = hulkData.convert_config_settings(response.data); return hulkData._parse_xlisted_countries(settings); }); }; function _batch_apiv1(calls_infos) { var deferred = $q.defer(); var apiCall = new APIREQUEST.Class(); apiCall.initialize("", "batch"); calls_infos.forEach(function(call, i) { apiCall.addArgument( ("command-" + i), call.func + "?" + QUERY.make_query_string(call.data) ); }); API.promise(apiCall.getRunArguments()) .done(function(response) { response = response.parsedResponse; if (response.status) { var results = response.data; deferred.resolve(results); } else { deferred.reject(response.error); } }); return deferred.promise; } hulkData.load_xlisted_countries = function() { return hulkData.load_config_settings().then(function(settings) { return hulkData._parse_xlisted_countries(settings); }); }; hulkData._parse_xlisted_countries = function(settings) { var countries = {}; settings.country_blacklist.forEach(function(item) { countries[item] = COUNTRY_CONSTANTS.BLACKLISTED; }); settings.country_whitelist.forEach(function(item) { countries[item] = COUNTRY_CONSTANTS.WHITELISTED; }); return countries; }; hulkData.add_to_list = function(list, records) { var deferred = $q.defer(); var apiCall = new APIREQUEST.Class(); apiCall.initialize("", "batch_create_cphulk_records", null, null, { json: true }); apiCall.addArgument("list_name", list); apiCall.addArgument("records", records); API.promise(apiCall.getRunArguments()) .done(function(response) { // create items from the response response = response.parsedResponse; if (response.status) { // on API call success, add to list var return_data = { added: [], rejected: response.data.ips_failed, updated: [], }; var original_ips_added = response.data.original_ips_added || []; var ip_list = hulkData[ list + "list" ]; var comment_list = hulkData[ list + "list_comments"]; for (var i = 0, len = original_ips_added.length; i < len; i++) { var ip = original_ips_added[i]; var ip_formatted = response.data.ips_added[i]; // Update the ip cache and parse the return data var index = _.indexOf(hulkData.whitelist, ip_formatted); if (index === -1) { ip_list.push(ip_formatted); return_data.added.push(ip_formatted); } else { return_data.updated.push(ip_formatted); } // Update the comment cache. var record = _.find(records, function(record) { return record.ip === ip; }); if (record && record.comment) { comment_list[ip_formatted] = record.comment; } // Extra info for the whitelist return. if (list === "white") { return_data.requester_ip = response.data.requester_ip; return_data.requester_ip_is_whitelisted = response.data.requester_ip_is_whitelisted; } } deferred.resolve(return_data); } else { // pass the error along var error_details = { main_message: response.error, secondary_messages: [], }; // Build the reason individual ips were rejected. Object.keys(response.data.ips_failed).forEach(function(ip) { var rejectReason = response.data.ips_failed[ip]; error_details.secondary_messages.push(rejectReason); }); deferred.reject(error_details); } }); // pass the promise back to the controller return deferred.promise; }; hulkData.add_to_whitelist = function(records) { return hulkData.add_to_list("white", records); }; hulkData.add_to_blacklist = function(records) { return hulkData.add_to_list("black", records); }; hulkData.remove_from_list = function(ips_to_delete, list) { var deferred = $q.defer(); var apiCall = new APIREQUEST.Class(); apiCall.initialize("", "delete_cphulk_record"); apiCall.addArgument("list_name", list); for (var i = 0; i < ips_to_delete.length; i++) { apiCall.addArgument("ip-" + i, ips_to_delete[i]); } API.promise(apiCall.getRunArguments()) .done(function(response) { // create items from the response response = response.parsedResponse; if (response.status) { var i = 0; if (list === "white") { // remove the IPs hulkData.whitelist = _.difference(hulkData.whitelist, response.data.ips_removed); // remove the comment for (i = 0; i < response.data.ips_removed.length; i++) { delete hulkData.whitelist_comments[response.data.ips_removed[i]]; } } else { // remove the IPs hulkData.blacklist = _.difference(hulkData.blacklist, response.data.ips_removed); // remove the comment for (i = 0; i < response.data.ips_removed.length; i++) { delete hulkData.blacklist_comments[response.data.ips_removed[i]]; } } deferred.resolve({ removed: response.data.ips_removed, not_removed: response.data.ips_failed, requester_ip: response.data.requester_ip, requester_ip_is_whitelisted: response.data.requester_ip_is_whitelisted }); } else { // pass the error along deferred.reject(response.error); } }); // pass the promise back to the controller return deferred.promise; }; hulkData.remove_from_whitelist = function(ips_to_delete) { return hulkData.remove_from_list(ips_to_delete, "white"); }; hulkData.remove_from_blacklist = function(ip_to_delete) { return hulkData.remove_from_list(ip_to_delete, "black"); }; hulkData.remove_all_from_whitelist = function() { return hulkData.remove_from_whitelist(hulkData.whitelist); }; hulkData.remove_all_from_blacklist = function() { return hulkData.remove_from_blacklist(hulkData.blacklist); }; hulkData.load_list = function(list) { var deferred = $q.defer(); var apiCall = new APIREQUEST.Class(); apiCall.initialize("", "read_cphulk_records"); apiCall.addArgument("list_name", list); API.promise(apiCall.getRunArguments()) .done(function(response) { // create items from the response response = response.parsedResponse; if (response.status) { // on API call success, populate data structure var return_data = {}; if (list === "white") { // check to see if the ip address is not whitelisted if (!response.data.requester_ip_is_whitelisted) { return_data.whitelist_warning = response.data.warning_ip; } if (response.data.restart_ssh) { return_data.restart_ssh = true; } if (response.data.warning_ssh) { return_data.warning_ssh = response.data.warning_ssh; } hulkData.whitelist = Object.keys(response.data.ips_in_list).slice(); for (var i = 0; i < hulkData.whitelist.length; i++) { if (response.data.ips_in_list[hulkData.whitelist[i]]) { hulkData.whitelist_comments[hulkData.whitelist[i]] = response.data.ips_in_list[hulkData.whitelist[i]]; } } hulkData.whitelist_is_cached = true; return_data.list = hulkData.whitelist; return_data.comments = hulkData.whitelist_comments; return_data.requester_ip = response.data.requester_ip; return_data.requester_ip_is_whitelisted = response.data.requester_ip_is_whitelisted; deferred.resolve(return_data); } else if (list === "black") { hulkData.blacklist = Object.keys(response.data.ips_in_list).slice(); for (var j = 0; j < hulkData.blacklist.length; j++) { if (response.data.ips_in_list[hulkData.blacklist[j]]) { hulkData.blacklist_comments[hulkData.blacklist[j]] = response.data.ips_in_list[hulkData.blacklist[j]]; } } hulkData.blacklist_is_cached = true; return_data.list = hulkData.blacklist; return_data.comments = hulkData.blacklist_comments; return_data.requester_ip = response.data.requester_ip; deferred.resolve(return_data); } } else { // pass the error along deferred.reject(response.error); } }); // pass the promise back to the controller return deferred.promise; }; function init() { // check for page data in the template if this is a first load if (app.firstLoad && app.firstLoad.configs && PAGE.config_values) { app.firstLoad.configs = false; hulkData.config_settings = hulkData.convert_config_settings(PAGE.config_values); } } init(); return hulkData; }]); return hulkDataSource; } ); /* # templates/hulkd/directives/disableValidation.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 */ /** * @summary Directive that disables any validation functions tied to an ngModel * based on a value (which must be evaluated) passed to the directive. * * This code is based on this plunkr: https://embed.plnkr.co/EM1tGb/ * * @required ngModel This directive requires ngModel be set on the element. * * @example * <input type="text" * id="theText" * name="theText" * ng-model="myvalue" * required * disable-validation="toggleValidation"> */ define( 'app/directives/disableValidation',[ "angular" ], function(angular) { "use strict"; var app; try { app = angular.module("App"); } catch (e) { app = angular.module("App", []); } app.directive("disableValidation", function() { return { require: "ngModel", restrict: "A", link: function(scope, element, attrs, ngModelController) { var originalValidators = angular.copy(ngModelController.$validators); Object.keys(originalValidators).forEach(function(key) { ngModelController.$validators[key] = function(v) { // pass the view value twice because some validators take modelValue and viewValue (e.g. required) return scope.$eval(attrs.disableValidation) || originalValidators[key](v, v); }; }); scope.$watch(attrs.disableValidation, function() { // trigger validation var originalViewValue = ngModelController.$viewValue; scope.$applyAsync(function() { ngModelController.$setViewValue(""); ngModelController.$setViewValue(originalViewValue); }); }); } }; }); } ); /* # templates/hulkd/views/configController.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 define, PAGE */ define( 'app/views/configController',[ "angular", "lodash", "cjt/util/locale", "cjt/validator/datatype-validators", "cjt/validator/compare-validators", "cjt/validator/length-validators", "uiBootstrap", "cjt/directives/validationContainerDirective", "cjt/directives/validationItemDirective", "cjt/decorators/growlDecorator", "app/services/HulkdDataSource", "app/directives/disableValidation" ], function(angular, _, LOCALE) { "use strict"; // Retrieve the current application var app = angular.module("App"); var controller = app.controller( "configController", ["$scope", "HulkdDataSource", "growl", "PAGE", function($scope, HulkdDataSource, growl, PAGE) { $scope.username_protection_level = "local"; $scope.username_protection_enabled = true; function ignoreKeyDownForSpacebar(event) { // prevent the spacebar from scrolling the window if (event.keyCode === 32) { event.preventDefault(); } } $scope.growlProtectionChangeRequiresSave = function() { growl.warning(LOCALE.maketext("You changed the protection level of [asis,cPHulk]. Click Save to implement this change.")); }; $scope.$watch( function() { return $scope.username_protection_enabled; }, function(newValue, oldValue) { if (newValue !== oldValue ) { $scope.growlProtectionChangeRequiresSave(); } } ); $scope.$watch( function() { return $scope.config_settings.ip_based_protection; }, function(newValue, oldValue) { if (newValue !== oldValue ) { $scope.growlProtectionChangeRequiresSave(); } } ); $scope.align_username_protection_settings = function() { // set up username protection to match the combined // settings if ($scope.config_settings.username_based_protection) { $scope.username_protection_level = "both"; $scope.username_protection_enabled = true; } else if ($scope.config_settings.username_based_protection_local_origin) { $scope.username_protection_level = "local"; $scope.username_protection_enabled = true; } else { $scope.username_protection_enabled = false; } }; $scope.prepare_username_protection_settings_for_save = function() { if (!$scope.username_protection_enabled) { $scope.config_settings.username_based_protection_local_origin = false; $scope.config_settings.username_based_protection = false; } else if ($scope.username_protection_level === "local") { $scope.config_settings.username_based_protection_local_origin = true; $scope.config_settings.username_based_protection = false; } else { $scope.config_settings.username_based_protection = true; } }; $scope.handle_protection_keydown = function(event) { ignoreKeyDownForSpacebar(event); }; $scope.handle_protection_keyup = function(event, target) { // bind to the spacebar and enter keys if (event.keyCode === 32 || event.keyCode === 13) { event.preventDefault(); if ($scope.config_settings[target] !== void 0) { if (target === "username") { $scope.username_protection_enabled = !$scope.username_protection_enabled; } else { $scope.config_settings[target] = !$scope.config_settings[target]; } } } }; $scope.collapse_keydown = function(event) { ignoreKeyDownForSpacebar(event); }; $scope.collapse_keyup = function(event, target) { // bind to the spacebar and enter keys if (event.keyCode === 32 || event.keyCode === 13) { event.preventDefault(); if ($scope[target] !== void 0) { $scope[target] = !$scope[target]; } } }; $scope.disableSave = function(form) { return form.$invalid || $scope.loadingPageData; }; $scope.save = function(form) { if (!form.$valid) { return; } $scope.loadingPageData = true; $scope.prepare_username_protection_settings_for_save(); return HulkdDataSource.save_config_settings($scope.config_settings) .then( function(data) { growl.success(LOCALE.maketext("The system successfully saved your [asis,cPHulk] configuration settings.")); if (data.restart_ssh) { growl.warning(LOCALE.maketext("The system disabled the [asis,UseDNS] setting for [asis,SSHD] in order to add IP addresses to the whitelist. You must restart SSH through the [output,url,_1,Restart SSH Server,_2] page to implement the change.", PAGE.security_token + "/scripts/ressshd", { "target": "_blank" })); } else if (data.warning) { growl.warning(data.warning); } }, function(error) { growl.error(error); } ) .finally(function() { $scope.loadingPageData = false; }); }; $scope.fetch = function() { if (_.isEmpty(HulkdDataSource.config_settings)) { $scope.loadingPageData = true; HulkdDataSource.load_config_settings() .then( function(data) { $scope.config_settings = data; $scope.align_username_protection_settings(); }, function(error) { growl.error(error); } ) .finally(function() { $scope.loadingPageData = false; }); } else { $scope.config_settings = HulkdDataSource.config_settings; $scope.align_username_protection_settings(); } }; $scope.bruteInfoCollapse = true; $scope.excessiveBruteInfoCollapse = true; $scope.loadingPageData = false; $scope.fetch(); } ]); return controller; } ); /* # templates/hulkd/directives/countryCodesTableDirective # Copyright 2022 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 define: false */ // Then load the application dependencies define( 'app/directives/countryCodesTableDirective',[ "angular", "lodash", "cjt/core", "uiBootstrap", "cjt/modules", "cjt/directives/toggleSortDirective", "cjt/directives/searchDirective", "cjt/directives/pageSizeDirective", "cjt/filters/startFromFilter", "cjt/decorators/paginationDecorator", "cjt/directives/quickFiltersDirective", ], function(angular, _, CJT) { "use strict"; var app = angular.module("App"); app.directive("countryCodesTable", ["COUNTRY_CONSTANTS", "$timeout", function(COUNTRY_CONSTANTS, $timeout) { var TEMPLATE_PATH = "directives/countryCodesTable.ptt"; var RELATIVE_PATH = "templates/hulkd/" + TEMPLATE_PATH; return { templateUrl: CJT.config.debug ? CJT.buildFullPath(RELATIVE_PATH) : TEMPLATE_PATH, restrict: "EA", scope: { "items": "=", "onChange": "&onChange", }, replace: true, controller: ["$scope", "$filter", "$uibModal", function($scope, $filter, $uibModal) { // confirm blacklist modal $scope.modal_instance = null; $scope.country_blacklist_in_progress = false; $scope.confirm_country_blacklisting = function() { $scope.country_blacklist_in_progress = true; $scope.modal_instance = $uibModal.open({ templateUrl: "confirm_country_blacklisting.html", scope: $scope, }); return true; }; $scope.cancel_country_blacklisting = function() { $scope.clear_modal_instance(); $scope.deselectAll(); $scope.country_blacklisting_in_progress = false; }; $scope.continue_country_blacklisting = function(selectedItems) { $scope.clear_modal_instance(); $scope.country_blacklist_in_progress = false; $scope.blacklistCountries(selectedItems); }; $scope.clear_modal_instance = function() { if ($scope.modal_instance) { $scope.modal_instance.close(); $scope.modal_instance = null; } }; // initialize filter list var updateTimeout; $scope.filteredList = $scope.items; $scope.COUNTRY_CONSTANTS = COUNTRY_CONSTANTS; var countriesMap = {}; $scope.items.forEach(function(item) { countriesMap[item.code] = item; item.searchableCode = "(" + item.code + ")"; }); $scope.loading = false; $scope.meta = { filterValue: "", sortBy: "name", quickFilterValue: "all", }; /** * Initialize the variables required for * row selections in the table. */ // This updates the selected tracker in the 'Selected' Badge. $scope.selectedItems = []; $scope.toggleSelect = function(itemCode, list) { var idx = list.indexOf(itemCode); if (idx > -1) { list.splice(idx, 1); } else { list.push(itemCode); } }; $scope.toggleSelectAll = function() { if ($scope.allSelected()) { $scope.deselectAll(); } else { $scope.selectAll(); } }; $scope.selectAll = function() { $scope.selectedItems = $scope.filteredList.map(function(item) { return item.code; }); }; $scope.deselectAll = function() { $scope.selectedItems = []; }; $scope.allSelected = function() { return $scope.selectedItems.length && $scope.selectedItems.length === $scope.filteredList.length; }; $scope.exists = function(item, list) { return list.indexOf(item) > -1; }; // update the table on sort $scope.sortList = function(meta) { $scope.fetch(); }; // update table on search $scope.searchList = function(searchString) { $scope.fetch(); }; $scope.getCountriesFromCodes = function(countryCodes) { return countryCodes.map(function(countryCode) { return countriesMap[countryCode]; }); }; $scope.whitelistCountries = function(countries) { $scope.getCountriesFromCodes(countries).forEach(function(country) { country.status = COUNTRY_CONSTANTS.WHITELISTED; }); $scope.countriesUpdated(); }; $scope.blacklistCountries = function(countries) { $scope.getCountriesFromCodes(countries).forEach(function(country) { country.status = COUNTRY_CONSTANTS.BLACKLISTED; }); $scope.countriesUpdated(); }; $scope.unlistCountries = function(countries) { $scope.getCountriesFromCodes(countries).forEach(function(country) { country.status = COUNTRY_CONSTANTS.UNLISTED; }); $scope.countriesUpdated(); }; $scope.countriesUpdated = function() { if ($scope.onChange) { if (updateTimeout) { $timeout.cancel(updateTimeout); updateTimeout = false; } updateTimeout = $timeout(function() { $scope.countriesUpdating = true; var whitelistedDomains = []; var blacklistedDomains = []; $scope.items.forEach(function(item) { if (item.status === COUNTRY_CONSTANTS.WHITELISTED) { whitelistedDomains.push(item.code); } else if (item.status === COUNTRY_CONSTANTS.BLACKLISTED) { blacklistedDomains.push(item.code); } }); $scope.onChange({ whitelist: whitelistedDomains, blacklist: blacklistedDomains }).finally(function() { $scope.countriesUpdating = false; }); }, 250); } }; // have your filters all in one place - easy to use var filters = { filter: $filter("filter"), orderBy: $filter("orderBy"), }; $scope.quickFilterUpdated = function() { $scope.deselectAll(); $scope.fetch(); }; // update table $scope.fetch = function() { var filteredList = []; // filter list based on search text if ($scope.meta.filterValue !== "") { filteredList = filters.filter($scope.items, $scope.meta.filterValue, false); } else { filteredList = $scope.items; } if ($scope.meta.quickFilterValue !== "all") { filteredList = filters.filter(filteredList, { status: $scope.meta.quickFilterValue }, false); } // sort the filtered list if ($scope.meta.sortDirection !== "" && $scope.meta.sortBy !== "") { filteredList = filters.orderBy(filteredList, $scope.meta.sortBy, $scope.meta.sortDirection === "asc" ? true : false); } // update the total items after search $scope.meta.totalItems = filteredList.length; $scope.filteredList = filteredList; return filteredList; }; // first page load $scope.fetch(); }], }; }]); }); /* # templates/hulkd/views/historyController.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 define: false */ define( 'app/views/countriesController',[ "angular", "lodash", "cjt/util/locale", "app/directives/countryCodesTableDirective", "cjt/decorators/growlDecorator", "app/services/HulkdDataSource" ], function(angular, _, LOCALE) { // Retrieve the current application var app = angular.module("App"); var controller = app.controller( "countriesController", ["$scope","growl","HulkdDataSource","COUNTRY_CONSTANTS","COUNTRY_CODES","XLISTED_COUNTRIES", function($scope, $growl, $service, COUNTRY_CONSTANTS, COUNTRY_CODES, XLISTED_COUNTRIES) { function _parseCountries(countryCodes, xlistedCountries){ return countryCodes.map(function(countryCode){ countryCode.status = xlistedCountries[countryCode.code] || COUNTRY_CONSTANTS.UNLISTED; return countryCode; }); } $scope.countries = _parseCountries(COUNTRY_CODES, XLISTED_COUNTRIES); var startingGrowl, successGrowl; $scope.countriesUpdated = function(whitelist, blacklist){ // Using growl for consistency, but this will have to be refactored later if(successGrowl){ successGrowl.destroy(); } startingGrowl = $growl.info(LOCALE.maketext("Updating the country whitelist and blacklist …")); return $service.set_cphulk_config_keys({ "country_whitelist":whitelist.sort().join(","), "country_blacklist":blacklist.sort().join(",") }).then(function(xlistedCountries){ XLISTED_COUNTRIES = xlistedCountries; $scope.countries = _parseCountries(COUNTRY_CODES, xlistedCountries); startingGrowl.destroy(); successGrowl = $growl.success(LOCALE.maketext("Country whitelist and blacklist updated.")); }); }; } ]); return controller; } ); /* # cpanel - whostmgr/docroot/templates/hulkd/utils/download.js # Copyright 2022 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 */ define( 'app/utils/download',[],function() { "use strict"; return { /** * Create a download name. * * @param {string} prefix * @returns {string} */ getDownloadName: function(prefix) { return prefix + ".txt"; }, /** * Convert the raw data into a download url data blob. * * @param {string} data - the formatted download data. * @returns {string} - The data formatted for a download url. */ getTextDownloadUrl: function(data) { var blob = new Blob([data], { type: "plain/text" }); return window.URL.createObjectURL(blob); }, /** * Clean up the allocated url. * * @param {string} url - the url previously created with createObjetURL. */ cleanupDownloadUrl: function(url) { if (url) { window.URL.revokeObjectURL(url); } }, /** * @typedef IpRecord * @property {string} ip - ip address or range. * @property {string?} comment - comment associated with the ip or range. */ /** * Convert the ip list into a serialized format. * * @param {IpRecord[]} list * @returns {string} */ formatList: function(list) { if (list && list.length) { return list.map(function(item) { return item.ip + (item.comment ? " # " + item.comment : ""); }).join("\n") + "\n"; } return ""; }, }; } ); /* # cpanel - whostmgr/docroot/templates/hulkd/views/hulkdWhitelistController.js # Copyright 2022 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 */ /* eslint camelcase: 0, no-prototype-builtins: 0 */ define( 'app/views/hulkdWhitelistController',[ "angular", "jquery", "lodash", "cjt/util/locale", "app/utils/download", "uiBootstrap", "cjt/directives/toggleSortDirective", "cjt/directives/pageSizeDirective", "cjt/decorators/paginationDecorator", "cjt/decorators/growlDecorator", "cjt/filters/startFromFilter", "app/services/HulkdDataSource", ], function(angular, $, _, LOCALE, Download) { "use strict"; // Retrieve the current application var app = angular.module("App"); app.config([ "$compileProvider", function($compileProvider) { $compileProvider.aHrefSanitizationWhitelist(/^blob:https:/); }, ]); var controller = app.controller( "hulkdWhitelistController", ["$rootScope", "$scope", "$filter", "$routeParams", "$uibModal", "HulkdDataSource", "growl", "PAGE", "growlMessages", "$timeout", function($rootScope, $scope, $filter, $routeParams, $uibModal, HulkdDataSource, growl, PAGE, growlMessages, $timeout) { $scope.whitelist_reverse = false; $scope.whitelist = []; $scope.whitelist_comments = {}; $scope.adding_batch_to_whitelist = false; $scope.new_whitelist_records = ""; $scope.ip_being_edited = false; $scope.current_ip = null; $scope.current_comment = ""; $scope.updating_comment = false; $scope.modal_instance = null; $scope.loading = false; $scope.downloadAllLink = ""; $scope.downloadSelectionLink = ""; $scope.meta = { sortDirection: "asc", sortBy: "white_ip", sortType: "", sortReverse: false, filter: "", maxPages: 0, totalItems: $scope.whitelist.length || 0, currentPage: 1, pageNumberStart: 0, pageNumberEnd: 0, pageSize: 20, pageSizes: [20, 50, 100], }; $scope.LOCALE = LOCALE; var filters = { filter: $filter("filter"), orderBy: $filter("orderBy"), startFrom: $filter("startFrom"), limitTo: $filter("limitTo"), }; $scope.delete_in_progress = false; $scope.ips_to_delete = []; // Handle auto-adding an ip from a query param or POST if (($routeParams["ip"] && $routeParams["ip"].length > 0) || PAGE.ipToAdd !== null) { var ip; var comment = ""; if ($routeParams["ip"] && $routeParams["ip"].length > 0) { // added via a query param ip = $routeParams["ip"]; } else if (PAGE.ipToAdd !== null) { // added via a POST and stuffed into PAGE ip = PAGE.ipToAdd; } // clear the ip so we don't add it again PAGE.ipToAdd = null; if (ip !== void 0) { $scope._add_to_whitelist([ { ip: ip, comment: comment } ]); } } $scope.growl_whitelist_warning = function(missing_ip) { // create a new growl to be displayed. var message_cache = LOCALE.maketext("Your current IP address “[_1]” is not on the whitelist.", _.escape(missing_ip)); $rootScope.whitelist_warning_message = growl.error(message_cache, { variables: { buttonLabel: LOCALE.maketext("Add to Whitelist"), showAction: true, action: function() { $rootScope.one_click_add_to_whitelist(missing_ip) .then(function() { $scope.downloadAllLink = $scope.generateDownloadAllLink(); }); }, }, onclose: function() { $rootScope.whitelist_warning_message = null; }, } ); }; $scope.edit_whitelist_ip = function(whitelist_ip) { $scope.current_ip = whitelist_ip; $scope.current_comment = $scope.whitelist_comments.hasOwnProperty(whitelist_ip) ? $scope.whitelist_comments[whitelist_ip] : ""; $scope.ip_being_edited = true; var whitelist_comment_field = $("#whitelist_current_comment"); var wait_id = setInterval( function() { if (whitelist_comment_field.is(":visible")) { whitelist_comment_field.focus(); whitelist_comment_field.select(); clearInterval(wait_id); } }, 250); }; $scope.cancel_whitelist_editing = function() { $scope.current_ip = null; $scope.current_comment = ""; $scope.ip_being_edited = false; $scope.focus_on_whitelist(); }; $scope.delete_tooltip = function(ip_address) { return LOCALE.maketext("Click to delete “[_1]” from the whitelist.", ip_address); }; $scope.edit_tooltip = function(ip_address) { return LOCALE.maketext("Click to edit the comment for “[_1]”.", ip_address); }; $scope.update_whitelist_comment = function() { if ($scope.updating_comment) { return; } $scope.updating_comment = true; HulkdDataSource.add_to_whitelist([ { ip: $scope.current_ip, comment: $scope.current_comment } ]) .then( function(results) { $scope.whitelist_comments = HulkdDataSource.whitelist_comments; // Growl out each success from the batch. results.updated.forEach(function(ip) { growl.success(LOCALE.maketext("You have successfully updated the comment for “[_1]”.", _.escape(ip))); }); // Report the failures from the batch. var rejectedMessages = []; Object.keys(results.rejected).forEach(function(ip) { rejectedMessages.push(_.escape(ip) + ": " + _.escape(results.rejected[ip])); }); if (rejectedMessages.length > 0) { var accumulatedMessages = LOCALE.maketext("Some records failed to update.") + "<br>"; accumulatedMessages += rejectedMessages.join("<br>"); growl.error(accumulatedMessages); } }, function(error) { growl.error(error); }) .finally(function() { $scope.updating_comment = false; $scope.cancel_whitelist_editing(); $scope.focus_on_whitelist(); }); }; var ipV6 = /^(([\da-fA-F]{1,4}:){4})(([\da-fA-F]{1,4}:){3})([\da-fA-F]{1,4})$/; var ipV4Range = /^((\d{1,3}.){3}\d{1,3})-((\d{1,3}.){3}\d{1,3})$/; var ipRangeTest = /-/; var ipV6Test = /:/; /** * Separates long ipv4 and ipv6 addresses with br tags. * Also, supports separating ipv4 and ipv6 address ranges. * * @param {string} ip - an ip address * @todo Implement this as an Angular Filter in a separate file */ $scope.splitLongIp = function(ip) { // ipv6? if (ipV6Test.test(ip)) { // is this a range? if (ipRangeTest.test(ip)) { // format the ipv6 addresses in range format var ipv6Addresses = ip.split(ipRangeTest); var ipv6AddressRange = ""; // get the first part of the range var match = ipV6.exec(ipv6Addresses[0]); if (match) { ipv6AddressRange += match[1] + "<br>" + match[3] + match[5]; } // add the range separator ipv6AddressRange += "-<br>"; // get the second part of the range match = ipV6.exec(ipv6Addresses[1]); if (match) { ipv6AddressRange += match[1] + "<br>" + match[3] + match[5]; } // if all we have is -<br>, then forget it if (ipv6AddressRange.length > 5) { return ipv6AddressRange; } } else { // format the ipv6 address var v6match = ipV6.exec(ip); if (v6match) { return v6match[1] + "<br>" + v6match[3] + v6match[5]; } } } else { // format the ipv4 range var v4rangeMatch = ipV4Range.exec(ip); if (v4rangeMatch) { return v4rangeMatch[1] + "-<br>" + v4rangeMatch[3]; } } // could not format it, just return it return ip; }; $scope.$watch(function() { return HulkdDataSource.enabled; }, function() { $scope.load_list(); }); $scope.$watch(function() { return $rootScope.ip_added_with_one_click === true; }, function() { $scope.applyFilters(); $rootScope.ip_added_with_one_click = false; }); $scope.$watchGroup([ "whitelist.length", "meta.filteredList.length" ], function() { if ($scope.whitelist.length === 0 || $scope.meta.filteredList.length === 0) { $("#whitelist_select_all_checkbox").prop("checked", false); } }); $scope.selectPage = function(page) { $("#whitelist_select_all_checkbox").prop("checked", false); // set the page if requested if (page && angular.isNumber(page)) { $scope.meta.currentPage = page; } $scope.load_list(); }; $scope.selectPageSize = function() { return $scope.load_list({ reset_focus: false }); }; /** * Filter the list by the `meta.filter`. */ $scope.filterList = function() { $scope.meta.currentPage = 1; $scope.load_list({ reset_focus: false }); }; /** * Clear the filter if it is set. */ $scope.toggleFilter = function() { $scope.meta.filter = ""; $scope.load_list({ reset_focus: false }); }; $scope.sortList = function(meta) { $scope.meta.sortReverse = (meta.sortDirection === "asc") ? false : true; $scope.applyFilters(); }; $scope.orderByComments = function(comment_object, ip_list) { var comments_as_pairs = _.toPairs(comment_object); var ips_as_pairs = []; // get the IPs that have no comments for (var i = 0; i < ip_list.length; i++) { if (!_.has(comment_object, ip_list[i] )) { var one_entry = [ip_list[i], "" ]; ips_as_pairs.push(one_entry); } } // sort the IPs that have no comments var sorted_pairs = _.sortBy(ips_as_pairs, function(pair) { return $scope.ip_padder(pair[0]); }); // sort the comments first by comment, then by IP address comments_as_pairs.sort(compareComments); // create an array of the IPs from the sorted comments var just_ips_comments = _.map(comments_as_pairs, function(pair) { return pair[0]; }); // create an array of the sorted IPs with no comments var just_ips = _.map(sorted_pairs, function(pair) { return pair[0]; }); // put the IPs with comments and the IPs without comments together var stuck_together = just_ips_comments.concat(just_ips); if ($scope.meta.sortDirection === "desc") { return stuck_together.reverse(); } return stuck_together; }; /** * Apply the sort, filter and pagination to the whitelist data. * * @returns {string[]} List of ips that pass the filters. */ $scope.applyFilters = function() { var filteredList = []; var start, limit; filteredList = $scope.whitelist; // Sort if ($scope.meta.sortDirection !== "" && $scope.meta.sortBy !== "") { if ($scope.meta.sortBy === "white_ip") { filteredList = filters.orderBy(filteredList, $scope.ip_padder, $scope.meta.sortReverse); } else { filteredList = $scope.orderByComments($scope.whitelist_comments, $scope.whitelist); } } // Totals $scope.meta.totalItems = $scope.whitelist.length; // Filter content var expected = $scope.meta.filter.toLowerCase(); if (expected) { filteredList = filters.filter(filteredList, function(actual) { return actual.indexOf(expected) !== -1 || ($scope.whitelist_comments[actual] && $scope.whitelist_comments[actual].toLowerCase().indexOf(expected) !== -1); }); } // Track the filtered size separatly $scope.meta.filteredItems = filteredList.length; // Pagination start = ($scope.meta.currentPage - 1) * $scope.meta.pageSize; limit = $scope.meta.pageSize; filteredList = filters.limitTo(filters.startFrom(filteredList, start), limit); $scope.meta.pageNumberStart = start + 1; $scope.meta.pageNumberEnd = ($scope.meta.currentPage * $scope.meta.pageSize); if ($scope.meta.totalItems === 0) { $scope.meta.pageNumberStart = 0; } if ($scope.meta.pageNumberEnd > $scope.meta.totalItems) { $scope.meta.pageNumberEnd = $scope.meta.totalItems; } $scope.meta.filteredList = filteredList; return filteredList; }; $scope.load_list = function(options) { if (HulkdDataSource.enabled && !$scope.loading) { $scope.loading = true; var reset_focus = typeof options !== "undefined" && options.hasOwnProperty("reset_focus") ? options.reset_focus : true; if (HulkdDataSource.whitelist_is_cached) { $scope.whitelist = HulkdDataSource.whitelist; $scope.whitelist_comments = HulkdDataSource.whitelist_comments; $scope.downloadAllLink = $scope.generateDownloadAllLink(); $scope.applyFilters(); if (reset_focus) { $scope.focus_on_whitelist(); } $scope.loading = false; } else { $scope.meta.filteredList = []; return HulkdDataSource.load_list("white") .then(function(results) { $scope.whitelist = HulkdDataSource.whitelist; $scope.whitelist_comments = HulkdDataSource.whitelist_comments; $scope.downloadAllLink = $scope.generateDownloadAllLink(); $scope.applyFilters(); // if the requester ip is not whitelisted and the growl does not exist or is not displayed, then display it if (results.hasOwnProperty("requester_ip_is_whitelisted") && results.requester_ip_is_whitelisted <= 0) { if (results.hasOwnProperty("requester_ip") && $rootScope.whitelist_warning_message === null) { $scope.growl_whitelist_warning(results.requester_ip); } } if (results.restart_ssh) { growl.warning(LOCALE.maketext("The system disabled the [asis,UseDNS] setting for [asis,SSHD] in order to add IP addresses to the whitelist. You must restart SSH through the [output,url,_1,Restart SSH Server,_2] page to implement the change.", PAGE.security_token + "/scripts/ressshd", { "target": "_blank" })); } else if (results.warning_ssh) { growl.warning(results.warning_ssh); } }, function(error) { growl.error(error); }) .finally(function() { if (reset_focus) { $scope.focus_on_whitelist(); } $scope.loading = false; }); } } return null; }; $scope.force_load_whitelist = function() { HulkdDataSource.whitelist_is_cached = false; $scope.whitelist = []; $scope.whitelist_comments = {}; $scope.meta.filteredList = []; return $scope.load_list(); }; $scope.delete_confirmation_message = function() { if ($scope.ips_to_delete.length === 1) { return LOCALE.maketext("Do you want to permanently delete “[_1]” from the whitelist?", $scope.ips_to_delete[0]); } else { return LOCALE.maketext("Do you want to permanently delete [quant,_1,record,records] from the whitelist?", $scope.ips_to_delete.length); } }; $scope.itemsAreChecked = function() { return $(".whitelist_select_item").filter(":checked").length > 0; }; $scope.check_whitelist_selection = function() { if ($(".whitelist_select_item").filter(":not(:checked)").length === 0) { $("#whitelist_select_all_checkbox").prop("checked", true); } else { $("#whitelist_select_all_checkbox").prop("checked", false); } $scope.downloadSelectionLink = $scope.generateDownloadSelectionLink(); }; /** * Get the list of ips selected in the UI. * * @returns <string[]> List if ips selected. */ $scope.getSelection = function() { var selected_items = [], $selected_dom_nodes = $(".whitelist_select_item:checked"); if ($selected_dom_nodes.length === 0) { return []; } $selected_dom_nodes.each( function() { selected_items.push($(this).data("ip")); }); return selected_items; }; $scope.confirm_whitelist_deletion = function(ip_to_delete) { if ($scope.whitelist.length === 0) { return false; } $scope.delete_in_progress = true; if (ip_to_delete !== undefined) { $scope.ips_to_delete = [ip_to_delete]; $scope.is_single_deletion = true; } else { var selected_items = $scope.getSelection(); if (selected_items.length === 0) { return false; } $scope.ips_to_delete = selected_items; $scope.is_single_deletion = false; } $scope.modal_instance = $uibModal.open({ templateUrl: "confirm_whitelist_deletion.html", scope: $scope, }); return true; }; $scope.clear_modal_instance = function() { if ($scope.modal_instance) { $scope.modal_instance.close(); $scope.modal_instance = null; } }; $scope.cancel_deletion = function() { $scope.delete_in_progress = false; $scope.ips_to_delete = []; $scope.clear_modal_instance(); $scope.focus_on_whitelist(); }; $scope.delete_whitelist_ips = function(is_single_deletion) { $scope.clear_modal_instance(); HulkdDataSource.remove_from_whitelist($scope.ips_to_delete) .then( function(results) { $scope.whitelist = HulkdDataSource.whitelist; $scope.whitelist_comments = HulkdDataSource.whitelist_comments; $scope.applyFilters(); $scope.focus_on_whitelist(); if (results.removed.length === 1) { growl.success(LOCALE.maketext("You have successfully deleted “[_1]” from the whitelist.", _.escape(results.removed[0]))); } else { growl.success(LOCALE.maketext("You have successfully deleted [quant,_1,record,records] from the whitelist.", results.removed.length)); } if ( results.hasOwnProperty("requester_ip_is_whitelisted") && results.requester_ip_is_whitelisted <= 0 && results.hasOwnProperty("requester_ip") ) { $scope.growl_whitelist_warning(results.requester_ip); } if (results.not_removed.keys && results.not_removed.keys.length > 0) { growl.warning(LOCALE.maketext("The system was unable to delete [quant,_1,record,records] from the whitelist.", results.not_removed.keys.length)); } }, function(error) { growl.error(error); }) .finally( function() { $scope.delete_in_progress = false; $scope.ips_to_delete = []; if (!is_single_deletion) { $scope.deselect_all_whitelist(); } // Since this is using JQuery/DOM, we have to wait another tick for the UI to update // before we try to get the selection. $timeout(function() { $scope.downloadAllLink = $scope.generateDownloadAllLink(); $scope.downloadSelectionLink = $scope.generateDownloadSelectionLink(); }); }); }; $scope.confirm_delete_all = function() { if ($scope.whitelist.length === 0) { return false; } $scope.delete_in_progress = true; $scope.modal_instance = $uibModal.open({ templateUrl: "confirm_whitelist_delete_all.html", scope: $scope, }); return true; }; $scope.cancel_delete_all = function() { $scope.delete_in_progress = false; $scope.clear_modal_instance(); $scope.focus_on_whitelist(); }; $scope.delete_all = function() { $scope.clear_modal_instance(); HulkdDataSource.remove_all_from_whitelist() .then( function(results) { $scope.whitelist = HulkdDataSource.whitelist; $scope.whitelist_comments = HulkdDataSource.whitelist_comments; $scope.applyFilters(); $scope.focus_on_whitelist(); if (results.not_removed.keys && results.not_removed.keys.length > 0) { growl.success(LOCALE.maketext("You have successfully deleted [quant,_1,record,records] from the whitelist.", results.removed.keys.length)); growl.warning(LOCALE.maketext("The system was unable to delete [quant,_1,record,records] from the whitelist.", results.not_removed.keys.length)); } else { growl.success(LOCALE.maketext("You have deleted all records from the whitelist.")); } if ( results.hasOwnProperty("requester_ip_is_whitelisted") && results.requester_ip_is_whitelisted <= 0 && results.hasOwnProperty("requester_ip") ) { $scope.growl_whitelist_warning(results.requester_ip); } }, function(error) { growl.error(error); }) .finally( function() { $scope.delete_in_progress = false; $scope.deselect_all_whitelist(); // Since this is using JQuery/DOM, we have to wait another tick for the UI to update // before we try to get the selection. $timeout(function() { $scope.downloadAllLink = $scope.generateDownloadAllLink(); $scope.downloadSelectionLink = $scope.generateDownloadSelectionLink(); }); }); }; $scope.select_all_whitelist = function() { if ($scope.whitelist.length === 0) { return false; } $(".whitelist_select_item").prop("checked", true); $("#whitelist_select_all_checkbox").prop("checked", true); $scope.downloadSelectionLink = $scope.generateDownloadSelectionLink(); return true; }; $scope.deselect_all_whitelist = function() { if ($scope.whitelist.length === 0) { return false; } $(".whitelist_select_item").prop("checked", false); $("#whitelist_select_all_checkbox").prop("checked", false); $scope.downloadSelectionLink = $scope.generateDownloadSelectionLink(); return true; }; $scope.toggle_whitelist_selection = function() { if ($("#whitelist_select_all_checkbox").prop("checked") === true) { $scope.select_all_whitelist(); } else { $scope.deselect_all_whitelist(); } }; $scope.focus_on_whitelist = function() { var whitelist_batch_field = $("#whitelist_batch_add"); var wait_id = setInterval( function() { if (whitelist_batch_field.is(":visible")) { whitelist_batch_field.focus(); whitelist_batch_field.select(); clearInterval(wait_id); } }, 250); }; /** * * @typedef Record * @property {string} ip * @property {string?} comment */ /** * Parse the batch of records. * * @param {string} text * @returns {Record[]} */ function parseBatch(text) { var lines = text.split("\n"); var records = []; for (var i = 0; i < lines.length; i++) { var line = lines[i]; if (line && line.length > 0) { var parts = line.split("#"); var ip = parts.shift().trim(); var comment = parts.join("#").trim(); records.push({ ip: ip, comment: comment, }); } } return records; } /** * Add a batch of records to the whitelist. * * @async * @returns */ $scope.add_to_whitelist = function() { if (!$scope.new_whitelist_records || $scope.adding_batch_to_whitelist) { return; } var records = parseBatch($scope.new_whitelist_records); return $scope._add_to_whitelist(records); }; /** * Add a batch of whitelist records. * * @private * @param {Record[]} batch * @returns */ $scope._add_to_whitelist = function(batch) { $scope.adding_batch_to_whitelist = true; return HulkdDataSource.add_to_whitelist(batch) .then( function(results) { $scope.whitelist = HulkdDataSource.whitelist; $scope.whitelist_comments = HulkdDataSource.whitelist_comments; $scope.downloadAllLink = $scope.generateDownloadAllLink(); $scope.applyFilters(); if (results.added.length === 1) { growl.success(LOCALE.maketext("You have successfully added “[_1]” to the whitelist.", _.escape(results.added[0]))); } else if (results.added.length > 1) { growl.success(LOCALE.maketext("You have successfully added [quant,_1,IP address,IP addresses] to the whitelist.", results.added.length)); } if (results.updated.length === 1) { growl.success(LOCALE.maketext("You have successfully updated the comment for “[_1]”.", _.escape(results.updated[0]))); } else if (results.updated.length > 1) { growl.success(LOCALE.maketext("You have successfully updated the [numerate,_1,comment,comments] for [quant,_1,IP address,IP addresses].", results.updated.length)); } // if requester ip is marked as being whitelisted in the last call, but the growl warning // is still displayed then hide the growl warning if (results.hasOwnProperty("requester_ip_is_whitelisted") && results.requester_ip_is_whitelisted > 0 && $rootScope.whitelist_warning_message !== null) { $rootScope.whitelist_warning_message.ttl = 0; $rootScope.whitelist_warning_message.promises = []; $rootScope.whitelist_warning_message.promises.push($timeout(angular.bind(growlMessages, function() { growlMessages.deleteMessage($rootScope.whitelist_warning_message); $rootScope.whitelist_warning_message = null; }), 200)); } var rejectedIps = Object.keys(results.rejected); if (rejectedIps.length > 0) { var accumulatedMessages = LOCALE.maketext("Some IP addresses were not added to the whitelist."); accumulatedMessages += "<br>\n"; // Put the rejected ips/comments back in the list. $scope.new_whitelist_records = rejectedIps.map(function(ip) { var record = batch.find(function(record) { return record.ip === ip; }); if (record && record.comment) { return ip + " # " + record.comment + "\n"; } return ip + "\n"; }).join(""); // Report the problems in the growl accumulatedMessages += "<ul>\n"; rejectedIps.forEach(function(ip) { if (results.rejected[ip]) { accumulatedMessages += "<li>" + _.escape(results.rejected[ip]) + "</li>\n"; } }); accumulatedMessages += "</ul>\n"; growl.error(accumulatedMessages); } else { $scope.new_whitelist_records = ""; } }, function(error_details) { var error = error_details.main_message; // Format the individual partial errors. var secondary_count = error_details.secondary_messages.length; if (secondary_count > 0) { error += "<ul>\n"; } error_details.secondary_messages.forEach(function(message) { error += "<li>" + _.escape(message) + "</li>\n"; }); if (secondary_count > 0) { error += "</ul>\n"; } growl.error(error); }) .finally( function() { $scope.adding_batch_to_whitelist = false; $scope.focus_on_whitelist(); }); }; // TODO: Make this a utility system: ip-comparison $scope.ip_padder = function(unpadded) { var padded_ip = ""; if (unpadded) { var split_ip = unpadded.split("."); for (var i = 0; i < split_ip.length; i++) { var this_section = split_ip[i]; while ( this_section.length < 3) { this_section = "0" + this_section; } padded_ip += this_section; } } return padded_ip; }; function compareComments(a, b) { // sort by comment if (a[1].toLowerCase() < b[1].toLowerCase()) { return -1; } if (a[1].toLowerCase() > b[1].toLowerCase()) { return 1; } // we have a duplicate comment, so sort by IP address if ($scope.ip_padder(a[0]) < $scope.ip_padder(b[0])) { return -1; } if ($scope.ip_padder(a[0]) > $scope.ip_padder(b[0])) { return 1; } // we have a duplicate comment and IP return 0; } /** * Generate the download name. * * @returns {string} - the name of the download. */ $scope.downloadName = function() { return Download.getDownloadName("whitelist"); }; /** * @typedef IpRecord * @property {string} ip - ip address or range. * @property {string?} comment - comment associated with the ip or range. */ /** * Package the ips and comments into a records structure * * @param {string[]} ips * @param {Dictionary<string,string>} comments * @returns {IpRecord[]} */ function getRecords(ips, comments) { var list = []; ips.forEach(function(ip) { var comment = comments[ip]; list.push({ ip: ip, comment: comment }); }); return list; } /** * Generate a data blob url that contains all the whitelist ips. * * @returns {string} - data url. */ $scope.generateDownloadAllLink = function() { if ($scope.downloadAllLink) { // Clean up the previous url Download.cleanupDownloadUrl($scope.downloadAllLink); $scope.downloadAllLink = null; } var ips = $scope.whitelist; if (!ips || ips.length === 0) { return ""; } var list = getRecords(ips, $scope.whitelist_comments); return Download.getTextDownloadUrl(Download.formatList(list)); }; /** * Generate a data blob url that contains the selected whitelist ips. * * @returns {string} - data url. */ $scope.generateDownloadSelectionLink = function() { if ($scope.downloadSelectionLink) { // Clean up the previous url Download.cleanupDownloadUrl($scope.downloadSelectionLink); $scope.downloadSelectionLink = null; } if (!$scope.whitelist || $scope.whitelist.length === 0) { return ""; } var selection = $scope.getSelection(); if (!selection || selection.length === 0) { return ""; } var list = getRecords(selection, $scope.whitelist_comments); return Download.getTextDownloadUrl(Download.formatList(list)); }; $scope.focus_on_whitelist(); }, ]); return controller; } ); /* # cpanel - whostmgr/docroot/templates/hulkd/views/hulkdBlacklistController.js # Copyright 2022 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 */ /* eslint camelcase: 0, no-prototype-builtins: 0 */ define( 'app/views/hulkdBlacklistController',[ "angular", "jquery", "lodash", "cjt/util/locale", "app/utils/download", "uiBootstrap", "cjt/directives/toggleSortDirective", "cjt/decorators/paginationDecorator", "cjt/decorators/growlDecorator", "cjt/filters/startFromFilter", "app/services/HulkdDataSource", ], function(angular, $, _, LOCALE, Download) { "use strict"; // Retrieve the current application var app = angular.module("App"); app.config([ "$compileProvider", function($compileProvider) { $compileProvider.aHrefSanitizationWhitelist(/^blob:https:/); }, ]); var controller = app.controller( "hulkdBlacklistController", ["$scope", "$filter", "$routeParams", "$uibModal", "HulkdDataSource", "growl", "PAGE", "$timeout", function($scope, $filter, $routeParams, $uibModal, HulkdDataSource, growl, PAGE, $timeout) { $scope.blacklist_reverse = false; $scope.blacklist = []; $scope.blacklist_comments = {}; $scope.adding_batch_to_blacklist = false; $scope.new_blacklist_records = ""; $scope.ip_being_edited = false; $scope.current_ip = null; $scope.current_comment = ""; $scope.updating_comment = false; $scope.modal_instance = null; $scope.loading = false; $scope.downloadAllLink = ""; $scope.downloadSelectionLink = ""; $scope.meta = { sortDirection: "asc", sortBy: "black_ip", sortType: "", sortReverse: false, filter: "", maxPages: 0, totalItems: $scope.blacklist.length || 0, currentPage: 1, pageNumberStart: 0, pageNumberEnd: 0, pageSize: 20, pageSizes: [20, 50, 100], }; $scope.LOCALE = LOCALE; var filters = { filter: $filter("filter"), orderBy: $filter("orderBy"), startFrom: $filter("startFrom"), limitTo: $filter("limitTo"), }; $scope.delete_in_progress = false; $scope.ips_to_delete = []; $scope.selecting_page_size = false; $scope.edit_blacklist_ip = function(blacklist_ip) { $scope.current_ip = blacklist_ip; $scope.current_comment = $scope.blacklist_comments.hasOwnProperty(blacklist_ip) ? $scope.blacklist_comments[blacklist_ip] : ""; $scope.ip_being_edited = true; var blacklist_comment_field = $("#blacklist_current_comment"); var wait_id = setInterval( function() { if (blacklist_comment_field.is(":visible")) { blacklist_comment_field.focus(); blacklist_comment_field.select(); clearInterval(wait_id); } }, 250); }; $scope.cancel_blacklist_editing = function() { $scope.current_ip = null; $scope.current_comment = ""; $scope.ip_being_edited = false; $scope.focus_on_blacklist(); }; $scope.delete_tooltip = function(ip_address) { return LOCALE.maketext("Click to delete “[_1]” from the blacklist.", ip_address); }; $scope.edit_tooltip = function(ip_address) { return LOCALE.maketext("Click to edit the comment for “[_1]”.", ip_address); }; $scope.update_blacklist_comment = function() { if ($scope.updating_comment) { return; } $scope.updating_comment = true; HulkdDataSource.add_to_blacklist([ { ip: $scope.current_ip, comment: $scope.current_comment } ]) .then( function(results) { $scope.blacklist_comments = HulkdDataSource.blacklist_comments; // Growl out each success from the batch. results.updated.forEach(function(ip) { growl.success(LOCALE.maketext("You have successfully updated the comment for “[_1]”.", _.escape(ip))); }); // Report the failures from the batch. var rejectedMessages = []; Object.keys(results.rejected).forEach(function(ip) { rejectedMessages.push(_.escape(ip) + ": " + _.escape(results.rejected[ip])); }); if (rejectedMessages.length > 0) { var accumulatedMessages = LOCALE.maketext("Some records failed to update.") + "<br>"; accumulatedMessages += rejectedMessages.join("<br>"); growl.error(accumulatedMessages); } }, function(error) { growl.error(error); }) .finally(function() { $scope.updating_comment = false; $scope.cancel_blacklist_editing(); $scope.focus_on_blacklist(); }); }; var ipV6 = /^(([\da-fA-F]{1,4}:){4})(([\da-fA-F]{1,4}:){3})([\da-fA-F]{1,4})$/; var ipV4Range = /^((\d{1,3}.){3}\d{1,3})-((\d{1,3}.){3}\d{1,3})$/; var ipRangeTest = /-/; var ipV6Test = /:/; /** * Separates long ipv4 and ipv6 addresses with br tags. * Also, supports separating ipv4 and ipv6 address ranges. * * @param {string} ip - an ip address * @todo Implement this as an Angular Filter in a separate file */ $scope.splitLongIp = function(ip) { // ipv6? if (ipV6Test.test(ip)) { // is this a range? if (ipRangeTest.test(ip)) { // format the ipv6 addresses in range format var ipv6Addresses = ip.split(ipRangeTest); var ipv6AddressRange = ""; // get the first part of the range var match = ipV6.exec(ipv6Addresses[0]); if (match) { ipv6AddressRange += match[1] + "<br>" + match[3] + match[5]; } // add the range separator ipv6AddressRange += "-<br>"; // get the second part of the range match = ipV6.exec(ipv6Addresses[1]); if (match) { ipv6AddressRange += match[1] + "<br>" + match[3] + match[5]; } // if all we have is -<br>, then forget it if (ipv6AddressRange.length > 5) { return ipv6AddressRange; } } else { // format the ipv6 address var v6match = ipV6.exec(ip); if (v6match) { return v6match[1] + "<br>" + v6match[3] + v6match[5]; } } } else { // format the ipv4 range var v4rangeMatch = ipV4Range.exec(ip); if (v4rangeMatch) { return v4rangeMatch[1] + "-<br>" + v4rangeMatch[3]; } } // could not format it, just return it return ip; }; $scope.$watch(function() { return HulkdDataSource.enabled; }, function() { $scope.load_list(); }); $scope.$watchGroup([ "blacklist.length", "meta.filteredList.length" ], function() { if ($scope.blacklist.length === 0 || $scope.meta.filteredList.length === 0) { $("#blacklist_select_all_checkbox").prop("checked", false); } }); $scope.selectPage = function(page) { $("#blacklist_select_all_checkbox").prop("checked", false); // set the page if requested if (page && angular.isNumber(page)) { $scope.meta.currentPage = page; } $scope.load_list(); }; $scope.selectPageSize = function() { return $scope.load_list({ reset_focus: false }); }; /** * Filter the list by the `meta.filter`. */ $scope.filterList = function() { $scope.meta.currentPage = 1; $scope.load_list({ reset_focus: false }); }; /** * Clear the filter if it is set. */ $scope.toggleFilter = function() { $scope.meta.filter = ""; $scope.load_list({ reset_focus: false }); }; $scope.sortList = function(meta) { $scope.meta.sortReverse = (meta.sortDirection === "asc") ? false : true; $scope.applyFilters(); }; $scope.orderByComments = function(comment_object, ip_list) { var comments_as_pairs = _.toPairs(comment_object); var ips_as_pairs = []; // get the IPs that have no comments for (var i = 0; i < ip_list.length; i++) { if (!_.has(comment_object, ip_list[i] )) { var one_entry = [ip_list[i], "" ]; ips_as_pairs.push(one_entry); } } // sort the IPs that have no comments var sorted_pairs = _.sortBy(ips_as_pairs, function(pair) { return $scope.ip_padder(pair[0]); }); // sort the comments first by comment, then by IP address comments_as_pairs.sort(compareComments); // create an array of the IPs from the sorted comments var just_ips_comments = _.map(comments_as_pairs, function(pair) { return pair[0]; }); // create an array of the sorted IPs with no comments var just_ips = _.map(sorted_pairs, function(pair) { return pair[0]; }); // put the IPs with comments and the IPs without comments together var stuck_together = just_ips_comments.concat(just_ips); if ($scope.meta.sortDirection === "desc") { return stuck_together.reverse(); } return stuck_together; }; /** * Apply the sort, filter and pagination to the blacklist data. * * @returns {string[]} List of ips that pass the filters. */ $scope.applyFilters = function() { var filteredList = []; var start, limit; filteredList = $scope.blacklist; // Sort if ($scope.meta.sortDirection !== "" && $scope.meta.sortBy !== "") { if ($scope.meta.sortBy === "black_ip") { filteredList = filters.orderBy(filteredList, $scope.ip_padder, $scope.meta.sortReverse); } else { filteredList = $scope.orderByComments($scope.blacklist_comments, $scope.blacklist); } } // Totals $scope.meta.totalItems = $scope.blacklist.length; // Filter content var expected = $scope.meta.filter.toLowerCase(); if (expected) { filteredList = filters.filter(filteredList, function(actual) { return actual.indexOf(expected) !== -1 || ($scope.blacklist_comments[actual] && $scope.blacklist_comments[actual].toLowerCase().indexOf(expected) !== -1); }); } // Track the filtered size separatly $scope.meta.filteredItems = filteredList.length; // Pagination start = ($scope.meta.currentPage - 1) * $scope.meta.pageSize; limit = $scope.meta.pageSize; filteredList = filters.limitTo(filters.startFrom(filteredList, start), limit); $scope.meta.pageNumberStart = start + 1; $scope.meta.pageNumberEnd = ($scope.meta.currentPage * $scope.meta.pageSize); if ($scope.meta.totalItems === 0) { $scope.meta.pageNumberStart = 0; } if ($scope.meta.pageNumberEnd > $scope.meta.totalItems) { $scope.meta.pageNumberEnd = $scope.meta.totalItems; } $scope.meta.filteredList = filteredList; return filteredList; }; $scope.load_list = function(options) { if (HulkdDataSource.enabled && !$scope.loading) { $scope.loading = true; var reset_focus = typeof options !== "undefined" && options.hasOwnProperty("reset_focus") ? options.reset_focus : true; if (HulkdDataSource.blacklist_is_cached) { $scope.blacklist = HulkdDataSource.blacklist; $scope.blacklist_comments = HulkdDataSource.blacklist_comments; $scope.downloadAllLink = $scope.generateDownloadAllLink(); $scope.applyFilters(); if (reset_focus) { $scope.focus_on_blacklist(); } $scope.loading = false; } else { $scope.meta.filteredList = []; return HulkdDataSource.load_list("black") .then(function() { $scope.blacklist = HulkdDataSource.blacklist; $scope.blacklist_comments = HulkdDataSource.blacklist_comments; $scope.downloadAllLink = $scope.generateDownloadAllLink(); $scope.applyFilters(); }, function(error) { growl.error(error); }) .finally(function() { if (reset_focus) { $scope.focus_on_blacklist(); } $scope.selecting_page_size = false; $scope.loading = false; }); } } return null; }; $scope.force_load_blacklist = function() { HulkdDataSource.blacklist_is_cached = false; $scope.blacklist = []; $scope.blacklist_comments = {}; $scope.meta.filteredList = []; return $scope.load_list(); }; $scope.delete_confirmation_message = function() { if ($scope.ips_to_delete.length === 1) { return LOCALE.maketext("Do you want to permanently delete “[_1]” from the blacklist?", $scope.ips_to_delete[0]); } else { return LOCALE.maketext("Do you want to permanently delete [quant,_1,record,records] from the backlist?", $scope.ips_to_delete.length); } }; $scope.itemsAreChecked = function() { return $(".blacklist_select_item").filter(":checked").length > 0; }; $scope.check_blacklist_selection = function() { if ($(".blacklist_select_item").filter(":not(:checked)").length === 0) { $("#blacklist_select_all_checkbox").prop("checked", true); } else { $("#blacklist_select_all_checkbox").prop("checked", false); } $scope.downloadSelectionLink = $scope.generateDownloadSelectionLink(); }; /** * Get the list of ips selected in the UI. * * @returns <string[]> List if ips selected. */ $scope.getSelection = function() { var selected_items = [], $selected_dom_nodes = $(".blacklist_select_item:checked"); if ($selected_dom_nodes.length === 0) { return []; } $selected_dom_nodes.each( function() { selected_items.push($(this).data("ip")); }); return selected_items; }; $scope.confirm_blacklist_deletion = function(ip_to_delete) { if ($scope.blacklist.length === 0) { return false; } $scope.delete_in_progress = true; if (ip_to_delete !== undefined) { $scope.ips_to_delete = [ip_to_delete]; $scope.is_single_deletion = true; } else { var selected_items = $scope.getSelection(); if (selected_items.length === 0) { return false; } $scope.ips_to_delete = selected_items; $scope.is_single_deletion = false; } $scope.modal_instance = $uibModal.open({ templateUrl: "confirm_blacklist_deletion.html", scope: $scope, }); return true; }; $scope.clear_modal_instance = function() { if ($scope.modal_instance) { $scope.modal_instance.close(); $scope.modal_instance = null; } }; $scope.cancel_deletion = function() { $scope.delete_in_progress = false; $scope.ips_to_delete = []; $scope.clear_modal_instance(); $scope.focus_on_blacklist(); }; $scope.delete_blacklist_ips = function(is_single_deletion) { $scope.clear_modal_instance(); HulkdDataSource.remove_from_blacklist($scope.ips_to_delete) .then( function(results) { $scope.blacklist = HulkdDataSource.blacklist; $scope.blacklist_comments = HulkdDataSource.blacklist_comments; $scope.applyFilters(); $scope.focus_on_blacklist(); if (results.removed.length === 1) { growl.success(LOCALE.maketext("You have successfully deleted “[_1]” from the blacklist.", _.escape(results.removed[0]))); } else { growl.success(LOCALE.maketext("You have successfully deleted [quant,_1,record,records] from the blacklist.", results.removed.length)); } if (results.not_removed.keys && results.not_removed.keys.length > 0) { growl.warning(LOCALE.maketext("The system was unable to delete [quant,_1,record,records] from the blacklist.", results.not_removed.keys.length)); } }, function(error) { growl.error(error); }) .finally( function() { $scope.delete_in_progress = false; $scope.ips_to_delete = []; if (!is_single_deletion) { $scope.deselect_all_blacklist(); } // Since this is using JQuery/DOM, we have to wait another tick for the UI to update // before we try to get the selection. $timeout(function() { $scope.downloadAllLink = $scope.generateDownloadAllLink(); $scope.downloadSelectionLink = $scope.generateDownloadSelectionLink(); }); }); }; $scope.confirm_delete_all = function() { if ($scope.blacklist.length === 0) { return false; } $scope.delete_in_progress = true; $scope.modal_instance = $uibModal.open({ templateUrl: "confirm_blacklist_delete_all.html", scope: $scope, }); return true; }; $scope.cancel_delete_all = function() { $scope.delete_in_progress = false; $scope.clear_modal_instance(); $scope.focus_on_blacklist(); }; $scope.delete_all = function() { $scope.clear_modal_instance(); HulkdDataSource.remove_all_from_blacklist() .then( function(results) { $scope.blacklist = HulkdDataSource.blacklist; $scope.blacklist_comments = HulkdDataSource.blacklist_comments; $scope.applyFilters(); $scope.focus_on_blacklist(); if (results.not_removed.keys && results.not_removed.keys.length > 0) { growl.success(LOCALE.maketext("You have successfully deleted [quant,_1,record,records] from the blacklist.", results.removed.keys.length)); growl.warning(LOCALE.maketext("The system was unable to delete [quant,_1,record,records] from the blacklist.", results.not_removed.keys.length)); } else { growl.success(LOCALE.maketext("You have deleted all records from the blacklist.")); } }, function(error) { growl.error(error); }) .finally( function() { $scope.delete_in_progress = false; $scope.deselect_all_blacklist(); // Since this is using JQuery/DOM, we have to wait another tick for the UI to update // before we try to get the selection. $timeout(function() { $scope.downloadAllLink = $scope.generateDownloadAllLink(); $scope.downloadSelectionLink = $scope.generateDownloadSelectionLink(); }); }); }; $scope.select_all_blacklist = function() { if ($scope.blacklist.length === 0) { return false; } $(".blacklist_select_item").prop("checked", true); $("#blacklist_select_all_checkbox").prop("checked", true); $scope.downloadSelectionLink = $scope.generateDownloadSelectionLink(); return true; }; $scope.deselect_all_blacklist = function() { if ($scope.blacklist.length === 0) { return false; } $(".blacklist_select_item").prop("checked", false); $("#blacklist_select_all_checkbox").prop("checked", false); $scope.downloadSelectionLink = $scope.generateDownloadSelectionLink(); return true; }; $scope.toggle_blacklist_selection = function() { if ($("#blacklist_select_all_checkbox").prop("checked") === true) { $scope.select_all_blacklist(); } else { $scope.deselect_all_blacklist(); } }; $scope.focus_on_blacklist = function() { var blacklist_batch_field = $("#blacklist_batch_add"); var wait_id = setInterval( function() { if (blacklist_batch_field.is(":visible")) { blacklist_batch_field.focus(); blacklist_batch_field.select(); clearInterval(wait_id); } }, 250); }; /** * * @typedef Record * @property {string} ip * @property {string?} comment */ /** * Parse the batch of records. * * @param {string} text * @returns {Record[]} */ function parseBatch(text) { var lines = text.split("\n"); var records = []; for (var i = 0; i < lines.length; i++) { var line = lines[i]; if (line && line.length > 0) { var parts = line.split("#"); var ip = parts.shift().trim(); var comment = parts.join("#").trim(); records.push({ ip: ip, comment: comment, }); } } return records; } /** * Add a batch of records to the blacklist. * * @async * @returns */ $scope.add_to_blacklist = function() { if (!$scope.new_blacklist_records || $scope.adding_batch_to_blacklist) { return; } var records = parseBatch($scope.new_blacklist_records); return $scope._add_to_blacklist(records); }; /** * Add a batch of blacklist records. * * @private * @param {Record[]} batch * @returns */ $scope._add_to_blacklist = function(batch) { $scope.adding_batch_to_blacklist = true; HulkdDataSource.add_to_blacklist(batch) .then( function(results) { $scope.blacklist = HulkdDataSource.blacklist; $scope.blacklist_comments = HulkdDataSource.blacklist_comments; $scope.downloadAllLink = $scope.generateDownloadAllLink(); $scope.applyFilters(); if (results.added.length === 1) { growl.success(LOCALE.maketext("You have successfully added “[_1]” to the blacklist.", _.escape(results.added[0]))); } else if (results.added.length > 1) { growl.success(LOCALE.maketext("You have successfully added [quant,_1,IP address,IP addresses] to the blacklist.", results.added.length)); } if (results.updated.length === 1) { growl.success(LOCALE.maketext("You have successfully updated the comment for “[_1]”.", _.escape(results.updated[0]))); } else if (results.updated.length > 1) { growl.success(LOCALE.maketext("You have successfully updated the [numerate,_1,comment,comments] for [quant,_1,IP address,IP addresses].", results.updated.length)); } var rejectedIps = Object.keys(results.rejected); if (rejectedIps.length > 0) { var accumulatedMessages = LOCALE.maketext("Some IP addresses were not added to the blacklist."); accumulatedMessages += "<br>\n"; // Put the rejected ips/comments back in the list. $scope.new_blacklist_records = rejectedIps.map(function(ip) { var record = batch.find(function(record) { return record.ip === ip; }); if (record && record.comment) { return ip + " # " + record.comment + "\n"; } return ip + "\n"; }).join(""); // Report the problems in the growl accumulatedMessages += "<ul>\n"; rejectedIps.forEach(function(ip) { if (results.rejected[ip]) { accumulatedMessages += "<li>" + _.escape(results.rejected[ip]) + "</li>\n"; } }); accumulatedMessages += "</ul>\n"; growl.error(accumulatedMessages); } else { $scope.new_blacklist_records = ""; } }, function(error_details) { var error = error_details.main_message; // Format the individual partial errors. var secondary_count = error_details.secondary_messages.length; if (secondary_count > 0) { error += "<ul>\n"; } error_details.secondary_messages.forEach(function(message) { error += "<li>" + _.escape(message) + "</li>\n"; }); if (secondary_count > 0) { error += "</ul>\n"; } growl.error(error); }) .finally(function() { $scope.adding_batch_to_blacklist = false; $scope.focus_on_blacklist(); }); }; // Handle auto-adding an ip from a query param or POST if (($routeParams["ip"] && $routeParams["ip"].length > 0) || PAGE.ipToAdd !== null) { var ip; var comment = ""; if ($routeParams["ip"] && $routeParams["ip"].length > 0) { // added via a query param ip = $routeParams["ip"]; } else if (PAGE.ipToAdd !== null) { // added via a POST and stuffed into PAGE ip = PAGE.ipToAdd; } // clear the ip so we don't add it again PAGE.ipToAdd = null; if (ip !== void 0) { $scope._add_to_blacklist([ { ip: ip, comment: comment } ]); } } // TODO: Make this a utility system: ip-comparison $scope.ip_padder = function(unpadded) { var padded_ip = ""; if (unpadded) { var split_ip = unpadded.split("."); for (var i = 0; i < split_ip.length; i++) { var this_section = split_ip[i]; while ( this_section.length < 3) { this_section = "0" + this_section; } padded_ip += this_section; } } return padded_ip; }; function compareComments(a, b) { // sort by comment if (a[1].toLowerCase() < b[1].toLowerCase()) { return -1; } if (a[1].toLowerCase() > b[1].toLowerCase()) { return 1; } // we have a duplicate comment, so sort by IP address if ($scope.ip_padder(a[0]) < $scope.ip_padder(b[0])) { return -1; } if ($scope.ip_padder(a[0]) > $scope.ip_padder(b[0])) { return 1; } // we have a duplicate comment and IP return 0; } /** * Generate the download name. * * @returns {string} - the name of the download. */ $scope.downloadName = function() { return Download.getDownloadName("blacklist"); }; /** * @typedef IpRecord * @property {string} ip - ip address or range. * @property {string?} comment - comment associated with the ip or range. */ /** * Package the ips and comments into a records structure * * @param {string[]} ips * @param {Dictionary<string,string>} comments * @returns {IpRecord[]} */ function getRecords(ips, comments) { var list = []; ips.forEach(function(ip) { var comment = comments[ip]; list.push({ ip: ip, comment: comment }); }); return list; } /** * Generate a data blob url that contains all the blacklist ips. * * @returns {string} - data url. */ $scope.generateDownloadAllLink = function() { if ($scope.downloadAllLink) { // Clean up the previous url Download.cleanupDownloadUrl($scope.downloadAllLink); $scope.downloadAllLink = null; } var ips = $scope.blacklist; if (!ips || ips.length === 0) { return ""; } var list = getRecords(ips, $scope.blacklist_comments); return Download.getTextDownloadUrl(Download.formatList(list)); }; /** * Generate a data blob url that contains the selected blacklist ips. * * @returns {string} - data url. */ $scope.generateDownloadSelectionLink = function() { if ($scope.downloadSelectionLink) { // Clean up the previous url Download.cleanupDownloadUrl($scope.downloadSelectionLink); $scope.downloadSelectionLink = null; } if (!$scope.blacklist || $scope.blacklist.length === 0) { return ""; } var selection = $scope.getSelection(); if (!selection || selection.length === 0) { return ""; } var list = getRecords(selection, $scope.blacklist_comments); return Download.getTextDownloadUrl(Download.formatList(list)); }; $scope.focus_on_blacklist(); }, ]); return controller; } ); /* global define: false */ define( 'app/services/FailedLoginService',[ // Libraries "angular", // Application // CJT "cjt/util/locale", "cjt/io/api", "cjt/io/whm-v1-request", "cjt/io/whm-v1" // IMPORTANT: Load the driver so its ready ], function(angular, LOCALE, API, APIREQUEST, APIDRIVER) { var app = angular.module("App"); app.factory("FailedLoginService", ["$q", function($q) { var exports = {}; function normalizeData(data) { // make the timeleft field an actual integer for sorting if (angular.isDefined(data.timeleft)) { data.timeleft = parseInt(data.timeleft, 10); } // make the authservice the same as the service if there is no authservice specified if (angular.isDefined(data.service) && angular.isDefined(data.authservice)) { if (data.authservice === "") { data.authservice = data.service; } } return data; } function convertResponseData(responseData) { var items = []; for (var i = 0, len = responseData.length; i < len; i++) { items.push(normalizeData(responseData[i])); } return items; } exports.getBrutes = function(meta) { var deferred = $q.defer(), apiCall = new APIREQUEST.Class(); apiCall.initialize("", "get_cphulk_brutes"); if (meta) { if (meta.filterBy && meta.filterValue !== null && meta.filterValue !== void 0) { apiCall.addFilter(meta.filterBy, meta.filterCompare, meta.filterValue); } if (meta.sortBy && meta.sortDirection) { apiCall.addSorting(meta.sortBy, meta.sortDirection, meta.sortType); } if (meta.pageNumber !== null && meta.pageNumber !== void 0) { apiCall.addPaging(meta.pageNumber, meta.pageSize || 20); } } API.promise(apiCall.getRunArguments()) .done(function(response) { response = response.parsedResponse; if (response.status) { var results = response; results.data = convertResponseData(results.data); deferred.resolve(results); } else { deferred.reject(response.error); } }); return deferred.promise; }; exports.getExcessiveBrutes = function(meta) { var deferred = $q.defer(), apiCall = new APIREQUEST.Class(); apiCall.initialize("", "get_cphulk_excessive_brutes"); if (meta) { if (meta.filterBy && meta.filterValue !== null && meta.filterValue !== void 0) { apiCall.addFilter(meta.filterBy, meta.filterCompare, meta.filterValue); } if (meta.sortBy && meta.sortDirection) { apiCall.addSorting(meta.sortBy, meta.sortDirection, meta.sortType); } if (meta.pageNumber !== null && meta.pageNumber !== void 0) { apiCall.addPaging(meta.pageNumber, meta.pageSize || 20); } } API.promise(apiCall.getRunArguments()) .done(function(response) { response = response.parsedResponse; if (response.status) { var results = response; results.data = convertResponseData(results.data); deferred.resolve(results); } else { deferred.reject(response.error); } }); return deferred.promise; }; exports.getFailedLogins = function(meta) { var deferred = $q.defer(), apiCall = new APIREQUEST.Class(); apiCall.initialize("", "get_cphulk_failed_logins"); if (meta) { if (meta.filterBy && meta.filterValue !== null && meta.filterValue !== void 0) { apiCall.addFilter(meta.filterBy, meta.filterCompare, meta.filterValue); } if (meta.sortBy && meta.sortDirection) { apiCall.addSorting(meta.sortBy, meta.sortDirection, meta.sortType); } if (meta.pageNumber !== null && meta.pageNumber !== void 0) { apiCall.addPaging(meta.pageNumber, meta.pageSize || 20); } } API.promise(apiCall.getRunArguments()) .done(function(response) { response = response.parsedResponse; if (response.status) { var results = response; results.data = convertResponseData(results.data); deferred.resolve(results); } else { deferred.reject(response.error); } }); return deferred.promise; }; exports.getBlockedUsers = function(meta) { var deferred = $q.defer(), apiCall = new APIREQUEST.Class(); apiCall.initialize("", "get_cphulk_user_brutes"); if (meta) { if (meta.filterBy && meta.filterValue !== null && meta.filterValue !== void 0) { apiCall.addFilter(meta.filterBy, meta.filterCompare, meta.filterValue); } if (meta.sortBy && meta.sortDirection) { apiCall.addSorting(meta.sortBy, meta.sortDirection, meta.sortType); } if (meta.pageNumber !== null && meta.pageNumber !== void 0) { apiCall.addPaging(meta.pageNumber, meta.pageSize || 20); } } API.promise(apiCall.getRunArguments()) .done(function(response) { response = response.parsedResponse; if (response.status) { var results = response; results.data = convertResponseData(results.data); deferred.resolve(results); } else { deferred.reject(response.error); } }); return deferred.promise; }; exports.clearHistory = function() { var deferred = $q.defer(), apiCall = new APIREQUEST.Class(); apiCall.initialize("", "flush_cphulk_login_history"); API.promise(apiCall.getRunArguments()) .done(function(response) { response = response.parsedResponse; if (response.status) { var results = response; results.data = convertResponseData(results.data); deferred.resolve(results); } else { deferred.reject(response.error); } }); return deferred.promise; }; exports.unBlockAddress = function(address) { var deferred = $q.defer(), apiCall = new APIREQUEST.Class(); apiCall.initialize("", "flush_cphulk_login_history_for_ips"); apiCall.addArgument("ip", address); API.promise(apiCall.getRunArguments()) .done(function(response) { // create items from the response response = response.parsedResponse; if (response.status) { var results = response; deferred.resolve(results.data); } else { deferred.reject(response.error); } }); return deferred.promise; }; return exports; }]); } ); /* # templates/hulkd/views/historyController.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 define: false */ define( 'app/views/historyController',[ "angular", "lodash", "cjt/util/locale", "uiBootstrap", "cjt/directives/toggleSortDirective", "cjt/directives/actionButtonDirective", "cjt/decorators/growlDecorator", "app/services/FailedLoginService", "app/services/HulkdDataSource" ], function(angular, _, LOCALE) { // Retrieve the current application var app = angular.module("App"); var controller = app.controller( "historyController", ["$scope", "$filter", "$q", "$timeout", "FailedLoginService", "HulkdDataSource", "growl", function($scope, $filter, $q, $timeout, FailedLoginService, HulkdDataSource, growl) { function updatePagination(scopeObj, apiResults) { var page_size = parseInt(apiResults.meta.paginate.page_size, 10); if (page_size === 0) { scopeObj.pageSize = $scope.meta.pageSizes[0]; } else { scopeObj.pageSize = page_size; } scopeObj.totalRows = apiResults.meta.paginate.total_records; scopeObj.pageNumber = apiResults.meta.paginate.current_page; scopeObj.pageNumberStart = apiResults.meta.paginate.current_record; if (scopeObj.totalRows === 0) { scopeObj.pageNumberStart = 0; } scopeObj.pageNumberEnd = (scopeObj.pageNumber * page_size); if (scopeObj.pageNumberEnd > scopeObj.totalRows) { scopeObj.pageNumberEnd = scopeObj.totalRows; } } $scope.showLowerPagination = function({pageNumberEnd, pageNumberStart}) { return pageNumberEnd - pageNumberStart >= 10; } $scope.changePageSize = function(type) { if (type === "logins") { if ($scope.logins.length > 0) { return $scope.fetchFailedLogins({ isUpdate: true }); } } else if (type === "users") { if ($scope.users.length > 0) { return $scope.fetchBlockedUsers({ isUpdate: true }); } } else if (type === "brutes") { if ($scope.brutes.length > 0) { return $scope.fetchBrutes({ isUpdate: true }); } } else if (type === "excessiveBrutes") { if ($scope.excessiveBrutes.length > 0) { return $scope.fetchExcessiveBrutes({ isUpdate: true }); } } }; $scope.fetchPage = function(type, page) { if (type === "logins") { if (page && angular.isNumber(page)) { $scope.meta.logins.currentPage = page; } return $scope.fetchFailedLogins({ isUpdate: true }); } else if (type === "users") { if (page && angular.isNumber(page)) { $scope.meta.users.currentPage = page; } return $scope.fetchBlockedUsers({ isUpdate: true }); } else if (type === "brutes") { if (page && angular.isNumber(page)) { $scope.meta.brutes.currentPage = page; } return $scope.fetchBrutes({ isUpdate: true }); } else if (type === "excessiveBrutes") { if (page && angular.isNumber(page)) { $scope.meta.excessiveBrutes.currentPage = page; } return $scope.fetchExcessiveBrutes({ isUpdate: true }); } }; $scope.sortBruteList = function(meta) { $scope.meta.brutes.sortReverse = (meta.sortDirection === "asc") ? false : true; return $scope.fetchBrutes({ isUpdate: true }); }; $scope.sortExcessiveBruteList = function(meta) { $scope.meta.excessiveBrutes.sortReverse = (meta.sortDirection === "asc") ? false : true; return $scope.fetchExcessiveBrutes({ isUpdate: true }); }; $scope.sortLoginList = function(meta) { $scope.meta.logins.sortReverse = (meta.sortDirection === "asc") ? false : true; return $scope.fetchFailedLogins({ isUpdate: true }); }; $scope.sortBlockedUsers = function(meta) { $scope.meta.users.sortReverse = (meta.sortDirection === "asc") ? false : true; return $scope.fetchBlockedUsers({ isUpdate: true }); }; $scope.search = function(type) { if (type === "logins") { return $scope.fetchFailedLogins({ isUpdate: true }); } else if (type === "users") { return $scope.fetchBlockedUsers({ isUpdate: true }); } else if (type === "brutes") { return $scope.fetchBrutes({ isUpdate: true }); } else if (type === "excessiveBrutes") { return $scope.fetchExcessiveBrutes({ isUpdate: true }); } }; $scope.loadTable = function() { $scope.loadingPageData = true; var table = $scope.selectedTable; if (table === "failedLogins") { $scope.logins = []; return $q.all([ $scope.fetchConfig(), $scope.fetchFailedLogins() ]).finally(function() { $scope.loadingPageData = false; }); } else if (table === "users") { $scope.users = []; return $q.all([ $scope.fetchConfig(), $scope.fetchBlockedUsers() ]).finally(function() { $scope.loadingPageData = false; }); } else if (table === "brutes") { $scope.brutes = []; return $q.all([ $scope.fetchConfig(), $scope.fetchBrutes() ]).finally(function() { $scope.loadingPageData = false; }); } else if (table === "excessiveBrutes") { $scope.excessiveBrutes = []; return $q.all([ $scope.fetchConfig(), $scope.fetchExcessiveBrutes() ]).finally(function() { $scope.loadingPageData = false; }); } else { $scope.logins = []; $scope.brutes = []; $scope.excessiveBrutes = []; $scope.users = []; return $q.all([ $scope.fetchConfig(), $scope.fetchFailedLogins(), $scope.fetchBlockedUsers(), $scope.fetchBrutes(), $scope.fetchExcessiveBrutes() ]).finally(function() { $scope.loadingPageData = false; }); } }; $scope.refreshLogins = function() { return $scope.loadTable(); }; $scope.clearHistory = function() { $scope.clearingHistory = true; return FailedLoginService.clearHistory() .then(function(results) { growl.success(LOCALE.maketext("The system cleared the tables.")); $scope.logins = []; $scope.brutes = []; $scope.excessiveBrutes = []; $scope.users = []; // update the pagination updatePagination($scope.meta.logins, results); updatePagination($scope.meta.brutes, results); updatePagination($scope.meta.excessiveBrutes, results); updatePagination($scope.meta.users, results); // clear the filter $scope.meta.logins.filterValue = ""; $scope.meta.brutes.filterValue = ""; $scope.meta.excessiveBrutes.filterValue = ""; $scope.meta.users.filterValue = ""; }, function(error) { growl.error(error); }) .finally(function() { $scope.clearingHistory = false; }); }; $scope.fetchConfig = function() { if (_.isEmpty(HulkdDataSource.config_settings)) { HulkdDataSource.load_config_settings() .then(function(data) { $scope.config_settings = data; }, function(error) { growl.error(error); }); } else { $scope.config_settings = HulkdDataSource.config_settings; } }; $scope.fetchFailedLogins = function(options) { if (options && options.isUpdate) { $scope.updatingPageData = true; } else { $scope.loadingPageData = true; } return FailedLoginService.getFailedLogins($scope.meta.logins) .then(function(results) { $scope.logins = results.data; updatePagination($scope.meta.logins, results); }, function(error) { growl.error(error); }) .finally(function() { $scope.updatingPageData = false; }); }; $scope.fetchBrutes = function(options) { if (options && options.isUpdate) { $scope.updatingPageData = true; } else { $scope.loadingPageData = true; } return FailedLoginService.getBrutes($scope.meta.brutes) .then(function(results) { $scope.brutes = results.data; updatePagination($scope.meta.brutes, results); }, function(error) { growl.error(error); }) .finally(function() { $scope.updatingPageData = false; }); }; $scope.fetchExcessiveBrutes = function(options) { if (options && options.isUpdate) { $scope.updatingPageData = true; } else { $scope.loadingPageData = true; } return FailedLoginService.getExcessiveBrutes($scope.meta.excessiveBrutes) .then(function(results) { $scope.excessiveBrutes = results.data; updatePagination($scope.meta.excessiveBrutes, results); }, function(error) { growl.error(error); }) .finally(function() { $scope.updatingPageData = false; }); }; $scope.fetchBlockedUsers = function(options) { if (options && options.isUpdate) { $scope.updatingPageData = true; } else { $scope.loadingPageData = true; } return FailedLoginService.getBlockedUsers($scope.meta.users) .then(function(results) { $scope.users = results.data; updatePagination($scope.meta.users, results); }, function(error) { growl.error(error); }) .finally(function() { $scope.updatingPageData = false; }); }; $scope.unBlockAddress = function(address, $event) { var element = $event.target; if (element) { if (/disabled/.test(element.className)) { // do not run this again if the link is disabled return; } else { element.className += " disabled"; } } return FailedLoginService.unBlockAddress(address) .then(function(results) { if (results.records_removed > 0) { // remove from the one day block and blocked ip address lists $scope.removeBrute(address); growl.success(LOCALE.maketext("The system removed the block for: [_1]", address)); } }, function(error) { growl.error(error); }); }; $scope.removeBrute = function(address) { var item = _.find($scope.brutes, { ip: address }); if (item) { $scope.brutes = _.difference($scope.brutes, [item]); return true; } item = _.find($scope.excessiveBrutes, { ip: address }); if (item) { $scope.excessiveBrutes = _.difference($scope.excessiveBrutes, [item]); return true; } return false; }; $scope.meta = { pageSizes: [20, 50, 100], maxPages: 0, "brutes": { sortDirection: "asc", sortBy: "logintime", sortType: "", filterBy: "*", filterCompare: "contains", filterValue: "", pageNumber: 1, pageNumberStart: 0, pageNumberEnd: 0, pageSize: 20, totalRows: 0 }, "excessiveBrutes": { sortDirection: "asc", sortBy: "logintime", sortType: "", filterBy: "*", filterCompare: "contains", filterValue: "", pageNumber: 1, pageNumberStart: 0, pageNumberEnd: 0, pageSize: 20, totalRows: 0 }, "logins": { sortDirection: "asc", sortBy: "user", sortType: "", filterBy: "*", filterCompare: "contains", filterValue: "", pageNumber: 1, pageNumberStart: 0, pageNumberEnd: 0, pageSize: 20, totalRows: 0 }, "users": { sortDirection: "asc", sortBy: "user", sortType: "", filterBy: "*", filterCompare: "contains", filterValue: "", pageNumber: 1, pageNumberStart: 0, pageNumberEnd: 0, pageSize: 20, totalRows: 0 } }; $scope.loadingPageData = true; $scope.updatingPageData = false; $scope.clearingHistory = false; // this is the default table that we will show first $scope.selectedTable = "failedLogins"; $scope.$on("$viewContentLoaded", function() { $timeout(function() { $scope.refreshLogins(); }); }); $scope.lookbackPeriodMinsDescription = function(config_settings) { if (typeof config_settings === "undefined") { return; } return LOCALE.maketext("The system counts Failed Logins for the duration of the specified period, which is currently set to [quant,_1,minute,minutes].", config_settings.lookback_period_min); }; $scope.blockedUsersDescription = function(config_settings) { if (typeof config_settings === "undefined") { return; } return LOCALE.maketext("The system blocks users for [quant,_1,minute,minutes]. You can configure this value with the “[_2]” option.", config_settings.brute_force_period_mins, LOCALE.maketext("Brute Force Protection Period (in minutes)")); }; $scope.blockedIPsDescription = function(config_settings) { if (typeof config_settings === "undefined") { return; } return LOCALE.maketext("The system blocks [asis,IP] addresses for [quant,_1,minute,minutes]. You can configure this value with the “[_2]” option.", config_settings.ip_brute_force_period_mins, LOCALE.maketext("IP Address-based Brute Force Protection Period (in minutes)")); }; } ]); return controller; } ); /* # templates/hulkd/views/hulkdEnableController.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 define, PAGE */ define( 'app/views/hulkdEnableController',[ "angular", "jquery", "cjt/util/locale", "cjt/util/parse", "uiBootstrap", "cjt/directives/toggleSortDirective", "cjt/directives/actionButtonDirective", "cjt/decorators/growlDecorator", "app/services/HulkdDataSource" ], function(angular, $, LOCALE, PARSE) { // Retrieve the current application var app = angular.module("App"); var controller = app.controller( "hulkdEnableController", ["$scope", "HulkdDataSource", "growl", "growlMessages", "PAGE", function($scope, HulkdDataSource, growl, growlMessages, PAGE) { $scope.hulkdEnabled = PARSE.parsePerlBoolean(PAGE.hulkd_status.is_enabled); $scope.knobLabel = "\u00a0"; $scope.changing_status = false; $scope.status_check_in_progress = false; $scope.handle_keydown = function(event) { // prevent the spacebar from scrolling the window if (event.keyCode === 32) { event.preventDefault(); } }; $scope.handle_keyup = function(event) { // bind to the spacebar and enter keys if (event.keyCode === 32 || event.keyCode === 13) { event.preventDefault(); $scope.toggle_status(); } }; $scope.toggle_status = function() { if ($scope.changing_status) { return; } $scope.changing_status = true; if ($scope.hulkdEnabled) { growlMessages.destroyAllMessages(); HulkdDataSource.disable_hulkd() .then( function() { $scope.hulkdEnabled = false; growl.success(LOCALE.maketext("[asis,cPHulk] is now disabled.")); }, function(error) { growl.error(error); }) .finally( function() { $scope.changing_status = false; }); } else { HulkdDataSource.enable_hulkd() .then( function(response) { $scope.hulkdEnabled = true; growl.success(LOCALE.maketext("[asis,cPHulk] is now enabled.")); if (response.data && response.data.restart_ssh) { growl.warning(LOCALE.maketext("The system disabled the [asis,UseDNS] setting for [asis,SSHD] in order to add IP addresses to the whitelist. You must restart SSH through the [output,url,_1,Restart SSH Server,_2] page to implement the change.", PAGE.security_token + "/scripts/ressshd", { "target": "_blank" })); } else if (response.data && response.data.warning) { growl.warning(response.data.warning); } }, function(error) { growl.error(error); }) .finally( function() { $scope.changing_status = false; }); } }; $scope.get_status = function() { if ($scope.status_check_in_progress) { return; } $scope.status_check_in_progress = true; return HulkdDataSource.hulkd_status() .then( function(results) { if (results !== $scope.hulkdEnabled) { // this test needs to run only if status has changed if (results === false) { growlMessages.destroyAllMessages(); } growl.warning(LOCALE.maketext("The status for [asis,cPHulk] has changed, possibly in another browser session.")); } $scope.hulkdEnabled = results; }, function(error) { growl.error(error); }) .finally(function() { $scope.status_check_in_progress = false; }); }; $scope.init = function() { $(document).ready(function() { // for window and tab changes $(window).on("focus", function() { $scope.get_status(); }); }); }; $scope.init(); } ]); return controller; } ); /* # cpanel - whostmgr/docroot/templates/hulkd/index.js # Copyright 2022 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 */ /* jshint -W100 */ /* eslint-disable camelcase */ define( 'app/index',[ "angular", "jquery", "lodash", "cjt/util/locale", "cjt/core", "cjt/modules", "ngRoute", "uiBootstrap", "ngSanitize", "ngAnimate", ], function(angular, $, _, LOCALE, CJT) { "use strict"; return function() { // First create the application angular.module("App", [ "cjt2.config.whm.configProvider", // This needs to load first "ngRoute", "ui.bootstrap", "ngSanitize", "ngAnimate", "angular-growl", "cjt2.whm", ]); // Then load the application dependencies var app = require( [ "cjt/bootstrap", // Application Modules "app/views/configController", "app/views/countriesController", "app/views/hulkdWhitelistController", "app/views/hulkdBlacklistController", "app/views/historyController", "app/views/hulkdEnableController", "app/services/HulkdDataSource", "angular-growl", ], function(BOOTSTRAP) { var app = angular.module("App"); app.value("PAGE", PAGE); app.value("COUNTRY_CONSTANTS", { WHITELISTED: "whitelisted", BLACKLISTED: "blacklisted", UNLISTED: "unlisted", }); // used to indicate that we are prefetching the following items app.firstLoad = { configs: true, }; app.controller("BaseController", ["$rootScope", "$scope", function($rootScope, $scope) { $scope.loading = false; $rootScope.$on("$routeChangeStart", function() { $scope.loading = true; }); $rootScope.$on("$routeChangeSuccess", function() { $scope.loading = false; }); $rootScope.$on("$routeChangeError", function() { $scope.loading = false; }); }, ]); app.config(["$routeProvider", "$animateProvider", function($routeProvider, $animateProvider) { $animateProvider.classNameFilter(/^((?!no-animate).)*$/); // Setup the routes $routeProvider.when("/config", { controller: "configController", templateUrl: CJT.buildFullPath("hulkd/views/configView.ptt"), }); $routeProvider.when("/whitelist", { controller: "hulkdWhitelistController", templateUrl: CJT.buildFullPath("hulkd/views/hulkdWhitelistView.ptt"), }); $routeProvider.when("/blacklist", { controller: "hulkdBlacklistController", templateUrl: CJT.buildFullPath("hulkd/views/hulkdBlacklistView.ptt"), }); $routeProvider.when("/history", { controller: "historyController", templateUrl: CJT.buildFullPath("hulkd/views/historyView.ptt"), }); $routeProvider.when("/countries", { controller: "countriesController", templateUrl: CJT.buildFullPath("hulkd/views/countriesView.ptt"), resolve: { "COUNTRY_CODES": ["HulkdDataSource", function($service) { return $service.get_countries_with_known_ip_ranges(); }], "XLISTED_COUNTRIES": ["HulkdDataSource", function($service) { return $service.load_xlisted_countries(); }], }, }); $routeProvider.otherwise({ "redirectTo": "/config", }); }, ]); app.run(["$rootScope", "$timeout", "$location", "HulkdDataSource", "growl", "growlMessages", function($rootScope, $timeout, $location, HulkdDataSource, growl, growlMessages) { // register listener to watch route changes $rootScope.$on( "$routeChangeStart", function() { $rootScope.currentRoute = $location.path(); }); $rootScope.whitelist_warning_message = null; $rootScope.ip_added_with_one_click = false; $rootScope.one_click_add_to_whitelist = function(missing_ip) { return HulkdDataSource.add_to_whitelist([{ ip: missing_ip } ]) .then(function(results) { growl.success(LOCALE.maketext("You have successfully added “[_1]” to the whitelist.", results.added[0])); // check if the client ip is in the whitelist and if our growl is still shown, remove it if ((Object.prototype.hasOwnProperty.call(results, "requester_ip") && results.added.indexOf(results.requester_ip) > -1) && ($rootScope.whitelist_warning_message !== null)) { // remove is handled in this manner because it was not removing the growl in the right sequence // when shown with other growls $rootScope.whitelist_warning_message.ttl = 0; $rootScope.whitelist_warning_message.promises = []; $rootScope.whitelist_warning_message.promises.push($timeout(angular.bind(growlMessages, function() { growlMessages.deleteMessage($rootScope.whitelist_warning_message); $rootScope.whitelist_warning_message = null; }), 200)); $rootScope.ip_added_with_one_click = true; } }, function(error_details) { var combined_message = error_details.main_message; var secondary_count = error_details.secondary_messages.length; for (var z = 0; z < secondary_count; z++) { if (z === 0) { combined_message += "<br>"; } combined_message += "<br>"; combined_message += error_details.secondary_messages[z]; } growl.error(combined_message); }); }; }]); BOOTSTRAP(document); }); return app; }; } );
Save