Viewing File: /usr/local/cpanel/base/frontend/jupiter/security/tls_wizard/views/PendingCertificatesController.js
/*
* base/frontend/jupiter/security/tls_wizard/views/PendingCertificatesController.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 */
/* jshint -W100 */
// Then load the application dependencies
define(
[
"lodash",
"angular",
"cjt/util/locale",
"app/views/Certificate",
"cjt/modules",
"cjt/directives/spinnerDirective",
"app/services/CertificatesService",
"app/services/LocationService",
"cjt/directives/actionButtonDirective",
],
function(_, angular, LOCALE) {
"use strict";
var app = angular.module("App");
function PendingCertificatesController(
$scope,
$location,
$routeParams,
$anchorScroll,
$timeout,
$window,
CertificatesService,
LocationService,
Certificate,
alertService
) {
var providerDisplayName = {};
CPANEL.PAGE.products.forEach(function(p) {
providerDisplayName[p.provider] = p.provider_display_name;
});
$scope.show_introduction_block = CertificatesService.show_introduction_block;
$scope.get_provider_display_name = function(provider) {
return providerDisplayName[provider] || provider;
};
$scope.html_escape = _.escape;
$scope.get_time = function() {
return parseInt(Date.now() / 1000, 10);
};
$scope.LOCALE = LOCALE;
$scope.checking_pending_queue = false;
// Needed pending fix of CPANEL-4645
$scope.cjt1_LOCALE = window.LOCALE;
$scope.pending_certificates = CertificatesService.get_pending_certificates();
$scope.expanded_cert = null;
$scope.get_product_by_id = function(providerName, providerID) {
return CertificatesService.get_product_by_id(providerName, providerID);
};
$scope.get_cert_title = function(cert) {
var sortedDomains = cert.domains.sort(function(a, b) {
if (a.length === b.length) {
return 0;
}
return a.length > b.length ? 1 : -1;
});
if (sortedDomains.length === 1) {
return sortedDomains[0];
} else {
return LOCALE.maketext("“[_1]” and [quant,_2,other domain,other domains]", sortedDomains[0], sortedDomains.length - 1);
}
};
$scope.check_pending_queue = function() {
return CertificatesService.process_ssl_pending_queue().then(function(result) {
// ----------------------------------------
// The intent here is to show at least one notification, always:
//
// - notify (info) for each canceled cert
//
// - notify (success) for each installed cert
//
// - If we canceled nor installed any certificates,
// notify (info) about no-op.
// ----------------------------------------
var installed = [];
var deletedCount = 0;
result.data.forEach(function(oi) {
deletedCount += (1 * oi.deleted);
if (oi.installed) {
installed.push(oi);
} else {
var msg, alertType;
var domains = oi.domains;
var providerDisplayName = $scope.get_provider_display_name(oi.provider);
var providerNameHtml = _.escape(providerDisplayName);
var domain0Html = _.escape(domains[0]);
var xtraDomainsCount = domains.length - 1;
/* jshint indent: false */
switch (oi.last_status_code) {
case "OrderCanceled":
case "OrderItemCanceled":
alertType = "info";
if (xtraDomainsCount) {
msg = LOCALE.maketext("“[_1]” reports that the certificate for “[_2]” and [quant,_3,other domain,other domains] has been canceled.", providerNameHtml, domain0Html, xtraDomainsCount);
} else {
msg = LOCALE.maketext("“[_1]” reports that the certificate for “[_2]” has been canceled.", providerNameHtml, domain0Html);
}
break;
case "CA:revoked":
alertType = "danger";
if (xtraDomainsCount) {
msg = LOCALE.maketext("“[_1]” reports that the certificate authority issued but then revoked the certificate for “[_2]” and [quant,_3,other domain,other domains].", providerNameHtml, domain0Html, xtraDomainsCount);
} else {
msg = LOCALE.maketext("“[_1]” reports that the certificate authority issued but then revoked “[_2]”’s certificate.", providerNameHtml, domain0Html);
}
break;
case "CA:rejected":
alertType = "danger";
if (xtraDomainsCount) {
msg = LOCALE.maketext("“[_1]” reports that the certificate authority rejected the request for a certificate for “[_2]” and [quant,_3,other domain,other domains].", providerNameHtml, domain0Html, xtraDomainsCount);
} else {
msg = LOCALE.maketext("“[_1]” reports that the certificate authority rejected the request for a certificate for “[_2]”.", providerNameHtml, domain0Html);
}
}
/* jshint indent: 4 */
if (oi.last_status_message) {
msg += " (" + _.escape(oi.last_status_message) + ")";
}
alertService.add({
type: alertType,
message: msg,
closeable: true,
replace: false,
group: "tlsWizard",
});
}
});
if (installed.length) {
var vhosts = [];
angular.forEach(installed, function(orderItem) {
vhosts = vhosts.concat(orderItem.vhost_names);
});
alertService.add({
type: "success",
message: LOCALE.maketext("[numerate,_2,A certificate,Certificates] for the following [numerate,_2,website was,websites were] available, and the system has installed [numerate,_2,it,them]: [list_and_quoted,_1]", vhosts, installed.length),
closeable: true,
replace: false,
autoClose: 10000,
group: "tlsWizard",
});
} else if (!deletedCount) {
// We mentioned canceled and installed certificates earlier.
alertService.add({
type: "info",
message: LOCALE.maketext("The system processed the pending certificate queue successfully, but [numerate,_1,your pending certificate was not,none of your pending certificates were] available.", result.data.length),
closeable: true,
replace: false,
group: "tlsWizard",
});
}
return CertificatesService.fetch_pending_certificates().then(function() {
$scope.pending_certificates = CertificatesService.get_pending_certificates();
if ($scope.pending_certificates.length === 0) {
alertService.add({
type: "info",
message: LOCALE.maketext("You have no more pending [asis,SSL] certificates.") + " " + LOCALE.maketext("You will now return to the beginning of the wizard."),
closeable: true,
replace: false,
group: "tlsWizard",
});
CertificatesService.reset();
/* clear page-loaded domains and installed hosts to ensure we show the latests when we redirect to the purchase wizard */
CPANEL.PAGE.installed_hosts = null;
CPANEL.PAGE.domains = null;
$scope.get_new_certs();
} else {
$scope.prepare_pending_certificates();
// If one is expanded, recheck the details of it
if ($scope.expanded_cert) {
angular.forEach($scope.pending_certificates, function(cert) {
if (cert.order_item_id === $scope.expanded_cert) {
$scope.load_certificate_details(cert);
}
});
}
}
});
});
};
$scope.reset_and_create = function() {
CertificatesService.hard_reset();
$scope.get_new_certs();
};
$scope.get_new_certs = function() {
LocationService.go_to_last_create_route().search("");
};
$scope.cancel_purchase = function(cert) {
CertificatesService.cancel_pending_ssl_certificate_and_poll(cert.provider, cert.order_item_id).then(function(response) {
var payload = response.data[1].parsedResponse.data;
var certificatePEM = payload.certificate_pem;
var providerHTML = _.escape($scope.get_provider_display_name(cert.provider));
if (certificatePEM) {
alertService.add({
type: "info",
message: LOCALE.maketext("You have canceled this order, but “[_1]” already issued the certificate. The system will now install it. ([output,url,_2,Do you need help with this order?])", providerHTML, cert.support_uri),
closeable: true,
replace: false,
group: "tlsWizard",
});
CertificatesService.install_certificate(
certificatePEM,
cert.vhost_names
).then(
function() {
alertService.add({
type: "success",
message: LOCALE.maketext("The system has installed the new [asis,SSL] certificate on to the [numerate,_1,website,websites] [list_and_quoted,_2].", cert.vhost_names.length, cert.vhost_names),
closeable: true,
replace: false,
autoClose: 10000,
group: "tlsWizard",
});
},
function(errorHTML) {
alertService.add({
type: "danger",
message: LOCALE.maketext("The system failed to install the new [asis,SSL] certificate because of an error: [_1]", errorHTML),
group: "tlsWizard",
});
}
);
} else if (payload.status_code === "RequiresApproval") {
alertService.add({
type: "info",
message: LOCALE.maketext("The system has canceled the request for this certificate; however, “[_1]” was already waiting on approval before processing your order. To ensure that this certificate order is canceled, you must [output,url,_2,contact support directly].", providerHTML, cert.support_uri),
closeable: true,
replace: false,
group: "tlsWizard",
});
} else if (payload.status_code === "OrderCanceled") {
alertService.add({
type: "info",
message: LOCALE.maketext("This certificate’s order (ID “[_1]”) was already canceled directly via “[_2]”.", _.escape(cert.order_id), providerHTML),
closeable: true,
replace: false,
group: "tlsWizard",
});
} else if (payload.status_code === "OrderItemCanceled") {
alertService.add({
type: "info",
message: LOCALE.maketext("This certificate (order item ID “[_1]”) was already canceled directly via “[_2]”.", _.escape(cert.order_item_id), providerHTML),
closeable: true,
replace: false,
group: "tlsWizard",
});
} else {
alertService.add({
type: "success",
message: LOCALE.maketext("The system has canceled this certificate. Your credit card should not be charged for this order."),
closeable: true,
replace: false,
autoClose: 10000,
group: "tlsWizard",
});
}
CPANEL.PAGE.pending_certificates = null;
return CertificatesService.fetch_pending_certificates().then(function() {
/* refresh existing list */
$scope.pending_certificates = CertificatesService.get_pending_certificates();
if ($scope.pending_certificates.length === 0) {
$scope.get_new_certs();
} else {
$scope.prepare_pending_certificates();
}
}, function(errorHTML) {
alertService.add({
type: "danger",
message: LOCALE.maketext("The system encountered an error as it attempted to refresh your pending certificates: [_1]", errorHTML),
group: "tlsWizard",
});
});
}, function(errorHTML) {
alertService.add({
type: "danger",
message: LOCALE.maketext("The system encountered an error as it attempted to cancel your transaction: [_1]", errorHTML),
group: "tlsWizard",
});
});
};
var _addOrderDetailsToDisplayedDomain = function(certificate, displayedDomain) {
if (!certificate.domainDetails) {
return;
}
displayedDomain.orderDetails = certificate.domainDetails[displayedDomain.domain];
};
$scope.get_displayed_domains = function(pcert) {
var domains = pcert.domains;
var start = pcert.display_meta.items_per_page * (pcert.display_meta.current_page - 1);
var limit = Math.min(domains.length, start + pcert.display_meta.items_per_page);
// Domains displayed are the same domains that will be displayed.
if (pcert.displayed_domains && pcert.display_meta.start === start && pcert.display_meta.limit === limit && pcert.displayed_domains.length) {
return pcert.displayed_domains;
}
pcert.display_meta.start = start;
pcert.display_meta.limit = limit;
var displayDomains = [];
for (var i = pcert.display_meta.start; i < pcert.display_meta.limit; i++) {
var domainObject = {
domain: domains[i],
};
_addOrderDetailsToDisplayedDomain(pcert, domainObject);
displayDomains.push(domainObject);
}
pcert.displayed_domains = displayDomains;
return pcert.displayed_domains;
};
function _getStringForStatusCode(statusCode, provider) {
var str;
if (statusCode === "RequiresApproval") {
var providerDisplayName = $scope.get_providerDisplayName(provider);
str = LOCALE.maketext("Waiting for “[_1]” to approve your order …", providerDisplayName);
}
return str;
}
$scope.get_cert_status = function(pendingCertificate) {
var statusCodeStr = _getStringForStatusCode(pendingCertificate.last_status_code, pendingCertificate.provider);
if (statusCodeStr) {
return statusCodeStr;
}
var status = pendingCertificate.status;
if (status === "unconfirmed") {
return LOCALE.maketext("Pending Completion of Payment");
} else if (status === "confirmed") {
if (pendingCertificate.statusDetails && pendingCertificate.statusDetails.loaded) {
var incompleteThings = pendingCertificate.statusDetails.details.filter(function(item) {
if (item.rawStatus === "not-completed") {
return true;
}
return false;
});
if (incompleteThings.length === 0) {
return LOCALE.maketext("Payment Completed.") + " " + LOCALE.maketext("Awaiting Validation …");
} else if (incompleteThings.length === 1) {
return LOCALE.maketext("Payment Completed.") + " " + incompleteThings[0].status;
} else {
return LOCALE.maketext("Payment Completed.") + " " + LOCALE.maketext("Multiple validation items pending …");
}
} else {
return LOCALE.maketext("Payment Completed.") + " " + LOCALE.maketext("Waiting for the provider to issue the certificate …");
}
} else {
return LOCALE.maketext("Status Unknown");
}
};
$scope.toggle_cert_collapse = function(cert) {
if ($scope.expanded_cert === cert.order_item_id) {
$scope.collapse_cert(cert);
} else {
$scope.expand_cert(cert);
}
};
$scope.expand_cert = function(cert) {
$location.search("orderItemID", cert.order_item_id);
$scope.expanded_cert = cert.order_item_id;
if (!cert.statusDetails) {
$scope.load_certificate_details(cert);
}
$anchorScroll($scope.expanded_cert);
};
$scope.load_certificate_details = function(certificate) {
certificate.domainDetails = {};
certificate.statusDetails = { loaded: false, loading: true, details: [] };
function _succeed(details) {
certificate.statusDetails.loaded = true;
certificate.statusDetails.details = details.statusDetails;
certificate.domainDetails = {};
angular.forEach(certificate.statusDetails.details, function(detail) {
if (detail.rawStatus === "completed") {
detail.rowStatusClass = "success";
// Unset the url for completed things so we don't get a button
delete detail.actionURL;
} else {
detail.rowStatusClass = "warning";
}
});
angular.forEach(certificate.domains, function(domain) {
var status = details.domainDetails[domain];
if (status) {
certificate.hasDomainDetails = true;
}
certificate.domainDetails[domain] = {};
if (status === "NOTVALIDATED") {
certificate.domainDetails[domain].rowStatusClass = "warning";
certificate.domainDetails[domain].rowStatusLabel = LOCALE.maketext("Not Validated");
certificate.domainDetails[domain].domainDetailDescription = LOCALE.maketext("The [output,abbr,CA,Certificate Authority] received the request but has not yet performed a [output,abbr,DCV,Domain Control Validation] check.");
} else if (status === "VALIDATED") {
certificate.domainDetails[domain].rowStatusClass = "success";
certificate.domainDetails[domain].rowStatusLabel = LOCALE.maketext("Validated");
certificate.domainDetails[domain].domainDetailDescription = LOCALE.maketext("The [output,abbr,CA,Certificate Authority] validated the certificate.");
} else if (status === "AWAITINGBRANDING") {
certificate.domainDetails[domain].rowStatusClass = "info";
certificate.domainDetails[domain].rowStatusLabel = LOCALE.maketext("Awaiting Branding …");
certificate.domainDetails[domain].domainDetailDescription = LOCALE.maketext("The [output,abbr,CA,Certificate Authority] received the request and must now process the brand verification approval.");
} else {
certificate.domainDetails[domain].rowStatusClass = "info";
certificate.domainDetails[domain].rowStatusLabel = LOCALE.maketext("Unknown");
certificate.domainDetails[domain].domainDetailDescription = LOCALE.maketext("Unknown.");
}
});
// Manually add details to currently displayed domain (since it's cached)
angular.forEach(certificate.displayed_domains, function(displayedDomain) {
_addOrderDetailsToDisplayedDomain(certificate, displayedDomain);
});
}
function _finally() {
certificate.statusDetails.loading = false;
}
CertificatesService.getCertificateStatusDetails(certificate.provider, certificate.order_item_id).then(_succeed).finally(_finally);
};
$scope.collapse_cert = function() {
$location.search();
$scope.expanded_cert = null;
};
$scope.continue_purchase = function(pcert) {
var domains = CertificatesService.get_all_domains();
// Ensure no other purchasing certs exist
CertificatesService.reset_purchasing_certificates();
// rebuild purchasing certificate
var cert = new Certificate();
var certificateDomains = [];
var certificateProduct = CertificatesService.get_product_by_id(pcert.provider, pcert.product_id);
var totalPrice = 0;
cert.set_domains(certificateDomains);
cert.set_virtual_hosts(pcert.vhost_names);
cert.set_product(certificateProduct);
angular.forEach(pcert.domains, function(certificateDomain) {
angular.forEach(domains, function(domain) {
if (domain.domain === certificateDomain) {
certificateDomains.push(domain);
totalPrice += domain.is_wildcard ? certificateProduct.wildcard_price : certificateProduct.price;
}
});
});
cert.set_price(totalPrice);
CertificatesService.add_new_certificate(cert);
// Removes purchasing certificates that might be saved in local storage.
// These don't reappear until returning from logging in.
CertificatesService.save();
//
$location.path("/purchase/" + pcert.provider + "/login/").search({
order_id: pcert.order_id,
});
};
$scope.rebuild_local_storage = function() {
// Repair Orders
var orders = {};
var domains = CertificatesService.get_all_domains();
var virtualHosts = CertificatesService.get_virtual_hosts();
angular.forEach($scope.pending_certificates, function(orderItem) {
// build new order
orders[orderItem.order_id] = orders[orderItem.order_id] || {
access_token: "",
certificates: [],
order_id: orderItem.order_id,
checkout_url: orderItem.checkout_url,
};
orders[orderItem.order_id].certificates.push(orderItem);
// re select the domains
angular.forEach(orderItem.domains, function(certificateDomain) {
angular.forEach(domains, function(domain) {
if (domain.domain === certificateDomain) {
domain.selected = true;
}
});
});
// re select a product
angular.forEach(orderItem.vhost_names, function(vHostName) {
var vHostID = CertificatesService.get_virtual_host_by_display_name(vHostName);
var vhost = virtualHosts[vHostID];
var product = CertificatesService.get_product_by_id(
orderItem.provider,
orderItem.product_id
);
/* in case someone deletes the vhost while the certificate is pending */
if (vhost) {
vhost.set_product(product);
}
});
});
// add each new order
angular.forEach(orders, function(order) {
CertificatesService.add_order(order);
});
// Then Save
CertificatesService.save();
};
$scope.restore_orders = function() {
// Rebuild to prevent doubling up
CertificatesService.clear_stored_settings();
/* add in missing orders
we need to always do this in case a
localStorage exists that doesn't
contain *this* set of orders */
var fetRet = CertificatesService.fetch_domains();
if (_.isFunction(fetRet["finally"])) {
fetRet.then($scope.rebuild_local_storage);
} else if (fetRet) {
$scope.rebuild_local_storage();
}
};
$scope.prepare_pending_certificates = function() {
$scope.pending_certificates.forEach(function(cert) {
cert.support_uri_is_http = /^http/.test(cert.support_uri);
cert.display_meta = cert.display_meta || {
items_per_page: 10,
current_page: 1,
};
});
};
$scope.init = function() {
$scope.restore_orders();
$scope.prepare_pending_certificates();
if ($routeParams.orderItemID) {
$scope.expanded_cert = $routeParams.orderItemID;
angular.forEach($scope.pending_certificates, function(cert) {
if (cert.order_item_id === $scope.expanded_cert) {
$scope.load_certificate_details(cert);
}
});
$timeout(function() {
$anchorScroll($scope.expanded_cert);
}, 500);
}
};
$scope.init();
}
app.controller(
"PendingCertificatesController",
[
"$scope",
"$location",
"$routeParams",
"$anchorScroll",
"$timeout",
"$window",
"CertificatesService",
"LocationService",
"Certificate",
"alertService",
PendingCertificatesController,
]
);
}
);
Back to Directory
File Manager