Viewing File: /usr/local/cpanel/base/frontend/jupiter/security/tls_wizard/index.cmb.js
/*
base/frontend/jupiter/security/tls_wizard/services/HasIdVerMix.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 */
/* eslint-disable camelcase */
define(
'app/services/HasIdVerMix',[
"angular",
"lodash",
],
function(_) {
"use strict";
var app;
try {
app = angular.module("App"); // For runtime
} catch (e) {
app = angular.module("App", []); // Fall-back for unit testing
}
return app.factory("HasIdVerMix", [
"$log",
function the_factory($log) {
return {
set_identity_verification: function(identity_verification) {
if (typeof identity_verification !== "object") {
$log.error("bad idver", identity_verification);
throw new Error();
}
this.identity_verification = identity_verification;
},
get_identity_verification: function() {
return this.identity_verification;
},
};
},
]);
}
);
/*
* base/frontend/jupiter/security/tls_wizard/views/Certificate.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 */
define(
'app/views/Certificate',[
"angular",
"lodash",
"app/services/HasIdVerMix",
],
function(angular, _) {
"use strict";
angular.module("App").factory("Certificate", [
"HasIdVerMix",
function(HasIdVerMix) {
function Certificate(initObj) {
this.reset();
if (initObj) {
this.init(initObj);
}
}
_.assign(Certificate.prototype, HasIdVerMix);
_.assign(Certificate.prototype, {
get_product: function() {
return this.selected_product;
},
set_product: function(product_obj) {
this.selected_product = product_obj;
},
set_price: function(price) {
this.total_price = price;
},
get_price: function() {
return this.total_price;
},
set_domains: function(domains) {
this.domains = domains;
},
get_domains: function() {
return this.domains;
},
get_subject_names: function() {
return this.domains.map(function(domain) {
// Prefer DNS-based DCV. (We only would
// have done DNS if there was a problem
// with HTTP.)
return {
type: "dNSName",
name: domain.domain,
dcv_method: domain.dcvPassed.dns ? "dns" : "http",
};
});
},
set_virtual_hosts: function(vhost_names) {
this.virtual_hosts = vhost_names;
},
get_virtual_hosts: function() {
return this.virtual_hosts;
},
get_validity_period: function() {
return this.selected_product.validity_period || [1, "year"];
},
reset: function() {
this.domains = [];
this.total_price = null;
this.virtual_hosts = [];
this.selected_product = null;
this.product_price = 0;
this.product_wildcard_price = 0;
this.identity_verification = null;
},
toJSON: function() {
return {
domains: this.domains,
total_price: this.total_price,
virtual_hosts: this.virtual_hosts,
selected_product: this.selected_product,
product_price: this.product_price,
product_wildcard_price: this.product_wildcard_price,
identity_verification: this.get_identity_verification(),
};
},
init: function(initObj) {
_.assign(this, initObj);
},
});
return Certificate;
},
]);
}
);
/*
* base/frontend/jupiter/security/tls_wizard/services/VirtualHost.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 */
/* eslint-disable camelcase */
define(
'app/services/VirtualHost',[
"angular",
"lodash",
"app/services/HasIdVerMix",
],
function(angular, _, HasIdVerMix) {
"use strict";
var app;
try {
app = angular.module("App"); // For runtime
} catch (e) {
app = angular.module("App", []); // Fall-back for unit testing
}
app.factory("VirtualHost", [
"HasIdVerMix",
function(HasIdVerMix) {
function VirtualHost(vhost_obj) {
var self = this;
self.display_name = "";
self.domains = [];
self.selected_domains = [];
self.filtered_domains = {};
self.show_wildcards = true;
self.show_domains = true;
self.display_meta = {
items_per_page: 10,
current_page: 1,
};
self.displayed_domains = [];
self.selected_product = null;
self.calculated_price = null;
self.is_ssl = 0;
self.product_price = 0;
self.product_wildcard_price = 0;
self.added_to_cart = false;
self.current_step = "domains";
angular.extend(self, vhost_obj);
}
angular.extend( VirtualHost.prototype, HasIdVerMix );
angular.extend( VirtualHost.prototype, {
get_display_name: function() {
return this.display_name;
},
reset: function() {
this.current_step = "domains";
this.set_product(null);
this.set_product_price(0);
this.calculated_price = null;
this.selected_product = null;
angular.forEach(this.domains, function(domain) {
domain.selected = false;
});
this.get_selected_domains();
},
get_step: function() {
return this.current_step;
},
go_step: function(new_step) {
this.current_step = new_step;
return this.current_step;
},
get_price: function() {
var selected_domains = this.get_selected_domains();
var wildcard_domains = selected_domains.filter(function(domain) {
if (domain.is_wildcard) {
return true;
}
return false;
});
this.calculated_price = (this.product_price * (selected_domains.length - wildcard_domains.length)) + (wildcard_domains.length * this.product_wildcard_price);
return this.calculated_price;
},
set_product_price: function(price, wildcard_price) {
this.product_price = price || 0;
this.product_wildcard_price = wildcard_price || 0;
},
get_price_string: function() {
return "$" + this.get_price().toFixed(2) + " USD";
},
get_product: function() {
return this.selected_product;
},
set_product: function(product_obj) {
this.selected_product = product_obj;
},
get_domains: function() {
return this.domains;
},
set_displayed_domains: function(domains) {
this.displayed_domains = domains;
},
get_filtered_domains: function() {
var domains = this.get_domains();
var key;
if (this.show_wildcards && this.show_domains) {
key = "all";
} else if (this.show_wildcards) {
key = "wildcards";
} else if (this.show_domains) {
key = "domains";
} else {
return [];
}
if (this.filtered_domains[key]) {
return this.filtered_domains[key];
}
this.filtered_domains[key] = [];
var self = this;
angular.forEach(domains, function(domain) {
if (!self.show_wildcards && domain.is_wildcard) {
return false;
}
if (!self.show_domains && !domain.is_wildcard) {
return false;
}
self.filtered_domains[key].push(domain);
});
return this.filtered_domains[key];
},
get_domain_count: function(include_wildcards) {
if (include_wildcards) {
return this.domains.length;
}
return this.domains.length / 2;
},
get_displayed_domains: function() {
this.displayed_domains = [];
var filtered_domains = this.get_filtered_domains();
this.display_meta.start = this.display_meta.items_per_page * (this.display_meta.current_page - 1);
this.display_meta.limit = Math.min(filtered_domains.length, this.display_meta.start + this.display_meta.items_per_page);
for (var i = this.display_meta.start; i < this.display_meta.limit; i++) {
/* don't display wildcards*/
/* function is only used in 'advanced' mode */
if (filtered_domains[i].is_wildcard) {
continue;
}
this.displayed_domains.push(filtered_domains[i]);
}
return this.displayed_domains;
},
add_domain: function(domain) {
if (this.get_domain_by_domain(domain.domain)) {
return;
}
domain.resolved = -1; // Tri-state check (-1 = unchecked, 0/false = doesn't resolve, 1/true = resolves locally)
domain.resolving = false; // While a resolve is occuring.
var domain_id = this.domains.length;
this.domains.push(domain);
return domain_id;
},
get_domain_by_domain: function(domain) {
var match;
angular.forEach(this.domains, function(value) {
if (value.domain === domain) {
match = value;
}
});
return match;
},
// This doesn't actually remove a domain, it deselects it
remove_domain: function(domain) {
domain.selected = 0;
this.get_selected_domains();
},
// This doesn't actually remove domains, it deselects them
remove_all_domains: function() {
for (var i = 0; i < this.domains.length; i++) {
this.remove_domain(this.domains[i]);
}
},
is_ready: function() {
if (this.get_domains().length === 0) {
return false;
}
if (!this.get_product()) {
return false;
}
return true;
},
toJSON: function() {
var temp_data = {};
temp_data.display_name = this.display_name;
temp_data.selected_domains = this.selected_domains;
temp_data.selected_product = this.selected_product;
temp_data.calculated_price = this.calculated_price;
temp_data.product_price = this.product_price;
temp_data.domains = this.get_domains();
temp_data.identity_verification = this.get_identity_verification();
return temp_data;
},
get_selected_domains: function get_selected_domains() {
var selected_domains = _.filter( this.get_domains(), "selected" );
this.selected_domains = selected_domains;
return selected_domains;
},
has_selected_domains: function get_selected_domains() {
return _.some( this.get_domains(), "selected" );
},
} );
return VirtualHost;
},
] );
}
);
/*
* base/frontend/jupiter/security/tls_wizard/services/CertificatesService.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 Promise: false */
/* jshint -W100 */
/* eslint-disable camelcase */
define(
'app/services/CertificatesService',[
"angular",
"lodash",
"cjt/util/locale",
"cjt/util/html",
"cjt/util/parse",
"cjt/io/api",
"cjt/io/uapi-request",
"app/views/Certificate",
"app/services/VirtualHost",
"cjt/io/uapi", // IMPORTANT: Load the driver so its ready
"cjt/services/alertService",
],
function(angular, _, LOCALE, cjt2Html, cjt2Parse, API, APIREQUEST) {
"use strict";
var ACTION_URL_LABELS = {
"evClickThroughStatus": LOCALE.maketext("Sign the Agreement"),
"ovCallbackStatus": LOCALE.maketext("Schedule a Call"),
DEFAULT: LOCALE.maketext("Complete this Now"),
};
var ACTION_URL_ICONS = {
"ovCallbackStatus": "fas fa-phone-square",
DEFAULT: "fas fa-external-link-alt",
};
var STATUS_DETAIL_STRINGS = {
"csrStatus": {
label: LOCALE.maketext("[output,abbr,CSR,Certificate Signing Request] Status:"),
inProgress: LOCALE.maketext("Validating the [output,abbr,CSR,Certificate Signing Request] status …"),
},
"dcvStatus": {
label: LOCALE.maketext("[output,abbr,DCV,Domain Control Validation] Status:"),
inProgress: LOCALE.maketext("Validating the [output,abbr,DCV,Domain Control Validation] status …"),
},
"evClickThroughStatus": {
label: LOCALE.maketext("[output,abbr,EV,Extended Validation] Click-Through Status:"),
inProgress: LOCALE.maketext("Validating the [output,abbr,EV,Extended Validation] click-through status …"),
},
"freeDVUPStatus": {
label: LOCALE.maketext("Free [output,abbr,DV,Domain Validated] Up Status:"),
inProgress: LOCALE.maketext("Validating the free [output,abbr,DV,Domain Validated] up status …"),
},
"organizationValidationStatus": {
label: LOCALE.maketext("[output,abbr,OV,Organization Validation] Status:"),
inProgress: LOCALE.maketext("Validating the [output,abbr,OV,Organization Validation] status …"),
},
"ovCallbackStatus": {
label: LOCALE.maketext("[output,abbr,OV,Organization Validation] Callback Status:"),
inProgress: LOCALE.maketext("Validating the [output,abbr,OV,Organization Validation] callback status …"),
},
"validationStatus": {
label: LOCALE.maketext("Validation Status:"),
inProgress: LOCALE.maketext("Checking the validation status …"),
},
};
function _isWildcard(domain) {
return /^\*/.test(domain);
}
// Curious that JS doesn’t expose sprintf(). Anyway.
// http://www.codigomanso.com/en/2010/07/simple-javascript-formatting-zero-padding/
function _sprintf02D(n) {
return ("0" + n).slice(-2);
}
var app;
try {
app = angular.module("App"); // For runtime
} catch (e) {
app = angular.module("App", ["cjt2.services.alert"]); // Fall-back for unit testing
}
function CertificatesServiceFactory(VirtualHost, Certificate, $q, $log, alertService) {
var CertificatesService = {};
var virtualHosts = [];
var allDomains = [];
var selectedDomains = [];
var products = [];
var orders = [];
var pendingCertificates = [];
var installedHosts = null;
var purchasingCerts = [];
var sslDomains = {};
var installedHostsMap = {};
var productsSearchOptions;
var wildcardMap = {};
// A lookup of “www.” domains. We don’t display these in the
// UI, but we want to know about them so we avoid trying to DCV them.
var wwwDomainsLookup = {};
var domainSearchOptions;
var currentDate = new Date();
var introductionDismissed = false;
function _apiError(whichAPI, errorMsgHTML) {
var error = LOCALE.maketext("The “[_1]” [asis,API] failed due to the following error: [_2]", _.escape(whichAPI), errorMsgHTML);
alertService.add({
type: "danger",
message: error,
group: "tlsWizard",
});
}
CertificatesService.add_new_certificate = function(cert) {
purchasingCerts.push(cert);
return purchasingCerts;
};
CertificatesService.get_purchasing_certs = function() {
return purchasingCerts;
};
CertificatesService.get_order_by_id = function(orderID) {
for (var i = 0; i < orders.length; i++) {
if (orders[i].order_id === orderID) {
return orders[i];
}
}
};
CertificatesService.add_order = function(order) {
var existingOrder = CertificatesService.get_order_by_id(order.order_id);
if (existingOrder) {
// update existing order
angular.extend(existingOrder, order);
} else {
// add orer
orders.push(order);
}
return orders;
};
CertificatesService.restore = function() {
if (CertificatesService.get_virtual_hosts().length) {
return false;
}
var storedSettings = _getStoredSettingsJSON();
if (!storedSettings) {
return false;
}
var storage = JSON.parse(storedSettings);
angular.forEach(storage.virtual_hosts, function(vhost) {
virtualHosts.push(new VirtualHost(vhost));
});
angular.forEach(storage.purchasing_certs, function(cert) {
CertificatesService.add_new_certificate(new Certificate(cert));
});
storage.orders = storage.orders ? storage.orders : [];
orders = storage.orders;
return virtualHosts.length === storage.virtual_hosts.length && orders.length === storage.orders.length;
};
CertificatesService.add_virtual_host = function(virtualHost, isSSL) {
var newVHost = new VirtualHost({
display_name: virtualHost,
is_ssl: isSSL,
});
var vhostID = virtualHosts.length;
virtualHosts.push(newVHost);
return vhostID;
};
CertificatesService.get_virtual_hosts = function() {
return virtualHosts;
};
CertificatesService.doesDomainMatchOneOf = function(domain, domains) {
if (domains === null || domain === null) {
return false;
}
return domains.some(function(domainOne) {
var domainTwo = domain;
if (domainOne === domainTwo) {
return true;
}
var possibleWildcard;
var domainToMatch;
if (_isWildcard(domainOne)) {
possibleWildcard = domainOne;
domainToMatch = domainTwo;
} else if (_isWildcard(domainTwo)) {
possibleWildcard = domainTwo;
domainToMatch = domainOne;
} else {
return false;
}
possibleWildcard = possibleWildcard.replace(/^\*\./, "");
domainToMatch = domainToMatch.replace(/^[^.]+\./, "");
if (possibleWildcard === domainToMatch) {
return true;
}
return false;
});
};
// for testing
CertificatesService._getWWWDomainsLookup = function() {
return wwwDomainsLookup;
};
CertificatesService.add_raw_domain = function(rawDomain) {
if (/^www\./.test(rawDomain.domain)) {
wwwDomainsLookup[ rawDomain.domain ] = true;
return;
}
rawDomain.virtual_host = rawDomain.vhost_name;
rawDomain.order_by_name = rawDomain.domain;
/* for consistency and ease of filtering */
rawDomain.is_wildcard = rawDomain.domain.indexOf("*.") === 0;
rawDomain.is_proxy = rawDomain.is_proxy && rawDomain.is_proxy.toString() === "1";
rawDomain.stripped_domain = rawDomain.domain;
CertificatesService.add_domain(rawDomain);
// Adding this check here, but should probably check to make sure these weren't manually created (in a later version)
var matchesAutoGenerated = rawDomain.domain.match(/^(mail|ipv6)\./);
if (!rawDomain.is_wildcard && !rawDomain.is_proxy && !matchesAutoGenerated) {
CertificatesService.add_domain(angular.extend({}, rawDomain, {
domain: "*." + rawDomain.domain,
is_wildcard: true,
}));
}
};
// for testing
CertificatesService._getWildcardMap = function() {
return wildcardMap;
};
CertificatesService.domain_covered_by_wildcard = function(domain) {
return wildcardMap[domain];
};
CertificatesService.compare_wildcard_domain = function(wildcardDomain, compareDomain) {
return wildcardMap[compareDomain] === wildcardDomain.domain;
};
/* map these for faster lookup */
CertificatesService.build_wildcard_map = function() {
wildcardMap = {};
var domains = CertificatesService.get_all_domains();
var re;
domains.forEach(function(domain) {
// only need to map wildcards
if (domain.is_wildcard === false) {
return false;
}
// The “stripped_domain” isn’t stripped in the case of
// wildcard domains that actually exist in Apache vhosts.
re = new RegExp("^[^\\.]+\\." + _.escapeRegExp(domain.stripped_domain.replace(/^\*\./, "")) + "$");
domains.forEach(function(matchDomain) {
if (domain.domain !== matchDomain.domain && re.test(matchDomain.domain)) {
wildcardMap[matchDomain.domain] = domain;
}
});
});
};
CertificatesService.get_domain_certificate_status = function(domain) {
var ihost = CertificatesService.get_domain_certificate(domain.domain);
if (ihost && ihost.certificate) {
var expirationDate = new Date(ihost.certificate.not_after * 1000);
var daysUntilExpiration = (expirationDate - currentDate) / 1000 / 60 / 60 / 24;
if (expirationDate < currentDate) {
return "expired";
} else if (daysUntilExpiration < 30 && daysUntilExpiration > 0) {
return "expiring_soon";
} else {
return "active";
}
}
return "unsecured";
};
CertificatesService._getSSLDomains = function() {
return sslDomains;
};
CertificatesService._getInstalledHostsMap = function() {
return installedHostsMap;
};
CertificatesService._getInstalledHosts = function() {
return installedHosts;
};
CertificatesService.add_domain = function(domainObject) {
var vhostID = CertificatesService.get_virtual_host_by_display_name(domainObject.virtual_host);
if (vhostID !== 0 && !vhostID) {
vhostID = CertificatesService.add_virtual_host(domainObject.virtual_host, 1);
}
virtualHosts[vhostID].is_ssl = 1;
/* prevent adding of duplicates */
if (CertificatesService.get_domain_by_domain(domainObject.domain)) {
return;
}
// assume installed hosts is there, we will ensure this later
sslDomains[domainObject.domain] = null;
// domain certificate finding
var ihost = installedHostsMap[domainObject.virtual_host];
if (ihost && ihost.certificate) {
// vhost has certificate, but does it cover this domain
angular.forEach(ihost.certificate.domains, function(domain) {
if (domainObject.domain === domain) {
sslDomains[domainObject.domain] = ihost;
return;
}
var wildcardDomain = domainObject.domain.replace(/^[^.]+\./, "*.");
if (wildcardDomain === domain) {
sslDomains[domainObject.domain] = ihost;
}
});
}
domainObject.type = domainObject.is_wildcard ? "wildcard_domain" : "main_domain";
domainObject.proxy_type = domainObject.is_proxy ? "proxy_domain" : "main_domain";
domainObject.certificate_status = CertificatesService.get_domain_certificate_status(domainObject);
return virtualHosts[vhostID].add_domain(domainObject);
};
// This function should potentially be renamed
// It actually just deselects all the domains in a specific VHost
CertificatesService.remove_virtual_host = function(displayName) {
var index = CertificatesService.get_virtual_host_by_display_name(displayName);
if (!_.isNil(index)) {
virtualHosts[index].remove_all_domains();
}
};
CertificatesService.get_virtual_host_by_display_name = function(displayName) {
for (var i = 0; i < virtualHosts.length; i++) {
if (virtualHosts[i].display_name === "*") {
/* There can be only one if we requested an all-vhosts install */
return 0;
} else if (virtualHosts[i].display_name === displayName) {
return i;
}
}
};
CertificatesService._runUAPI = function(apiCall) {
var deferred = $q.defer();
API.promise(apiCall.getRunArguments())
.done(function(response) {
response = response.parsedResponse;
if (response.status) {
deferred.resolve(response);
} else {
deferred.reject(response.error);
}
});
return deferred.promise;
};
function _dnsDcvPromise(dnsDcvDomainObjs) {
var apiCall = (new APIREQUEST.Class()).initialize(
"DCV",
"check_domains_via_dns",
{
domain: dnsDcvDomainObjs.map( function(d) {
return d.domain;
} ),
}
);
return CertificatesService._runUAPI(apiCall).then(
function(results) {
for (var d = 0; d < dnsDcvDomainObjs.length; d++) {
var domain = dnsDcvDomainObjs[d];
domain.resolving = false;
domain.dcvPassed.dns = cjt2Parse.parsePerlBoolean(results.data[d].succeeded);
if (domain.dcvPassed.dns) {
domain.resolved = 1;
} else {
// What to do here?? If HTTP passed but
// DNS fails, let’s assume that whatever
// DCV logic the CA does will fail.
domain.resolved = 0;
// TODO: Make it so we can inject
// the raw HTML.
if (domain.resolution_failure_reason) {
domain.resolution_failure_reason += " " + cjt2Html.decode( LOCALE.maketext("[asis,DNS]-based [output,abbr,DCV,Domain Control Validation] also failed.") );
} else {
domain.resolution_failure_reason = cjt2Html.decode( LOCALE.maketext("[asis,DNS]-based [output,abbr,DCV,Domain Control Validation] failed.") );
}
}
}
},
function(error) {
_apiError("DCV::check_domains_via_dns", error);
}
);
}
CertificatesService.set_confirmed_status_for_ssl_certificates = function(provider, order) {
var deferred = $q.defer();
var apiCall = new APIREQUEST.Class();
var orderItemIDs = [];
angular.forEach(order.certificates, function(item) {
orderItemIDs.push(item.order_item_id);
});
apiCall.initialize("Market", "set_status_of_pending_queue_items");
apiCall.addArgument("provider", provider);
apiCall.addArgument("status", "confirmed");
apiCall.addArgument("order_item_id", orderItemIDs);
API.promise(apiCall.getRunArguments())
.done(function(response) {
response = response.parsedResponse;
// This specific case is unusual because we want to
// give the error handler the entire object so that it
// can check for “data” in the response. See the
// documentation for Market::set_status_of_pending_queue_items.
var method = response.status ? "resolve" : "reject";
deferred[method](response);
});
return deferred.promise;
};
// Might return a promise, or it might return a boolean,
// which indicates that there’s no work to be done.
// (Could ideally do this with Promise.resolve()?)
CertificatesService.fetch_domains = function() {
var ret = CertificatesService.fetch_installed_hosts();
if (_.isFunction(ret.then) !== false) {
return ret.then(function() {
return CertificatesService.fetch_domains();
});
}
if (CPANEL.PAGE.domains) {
angular.forEach(CPANEL.PAGE.domains, function(domain) {
CertificatesService.add_raw_domain(domain);
});
if (CertificatesService.get_all_domains().length) {
return true;
}
}
var deferred = $q.defer();
var apiCall = new APIREQUEST.Class();
apiCall.initialize("WebVhosts", "list_ssl_capable_domains");
API.promise(apiCall.getRunArguments())
.done(function(response) {
response = response.parsedResponse;
if (response.status) {
deferred.resolve(response);
} else {
deferred.reject(response.error);
}
});
deferred.promise.then(function(result) {
angular.forEach(result.data, function(domain) {
CertificatesService.add_raw_domain(domain);
});
}, function(error) {
_apiError("WebVHosts::list_ssl_capable_domains", error);
});
return deferred.promise;
};
CertificatesService.get_store_login_url = function(provider, escapedURL) {
var deferred = $q.defer();
var apiCall = new APIREQUEST.Class();
apiCall.initialize("Market", "get_login_url");
apiCall.addArgument("provider", provider);
apiCall.addArgument("url_after_login", escapedURL);
API.promise(apiCall.getRunArguments())
.done(function(response) {
response = response.parsedResponse;
if (response.status) {
deferred.resolve(response);
} else {
deferred.reject(response.error);
}
});
return deferred.promise;
};
function _getStoredSettingsJSON() {
return localStorage.getItem("tls_wizard_data");
}
CertificatesService.store_settings = function(extras) {
var storableSettings = CertificatesService.get_storable_settings(extras);
localStorage.setItem("tls_wizard_data", storableSettings);
var retrievedData = _getStoredSettingsJSON();
return retrievedData === storableSettings;
};
CertificatesService.save = CertificatesService.store_settings;
// Returns at least an empty object.
CertificatesService.get_stored_extra_settings = function() {
var settings = _getStoredSettingsJSON();
if (settings) {
settings = JSON.parse(settings).extras;
}
return settings || {};
};
CertificatesService.clear_stored_settings = function() {
return localStorage.removeItem("tls_wizard_data");
};
CertificatesService.get_storable_settings = function(extras) {
// Preserve the “extras”, which contains things like
// identity verification for OV and EV certs.
//
var storage = _getStoredSettingsJSON();
storage = storage ? JSON.parse(storage) : {};
if (!storage.extras) {
storage.extras = {};
}
if (extras) {
_.assign(storage.extras, extras);
}
// Clobber everything else.
_.assign(storage, {
orders: orders,
// Used in the “Advanced” screen
// NB: Each one has a .toJSON() method defined.
virtual_hosts: virtualHosts,
// Used in the “Simple” screen
// NB: Each one has a .toJSON() method defined.
purchasing_certs: CertificatesService.get_purchasing_certs(),
});
return JSON.stringify(storage);
};
CertificatesService.get_all_domains = function() {
allDomains = [];
angular.forEach(virtualHosts, function(vhost) {
allDomains = allDomains.concat(vhost.get_domains());
});
return allDomains;
};
CertificatesService.get_all_selected_domains = function() {
selectedDomains = [];
angular.forEach(virtualHosts, function(vhost) {
selectedDomains = selectedDomains.concat(vhost.get_selected_domains());
});
return selectedDomains;
};
CertificatesService.get_products = function() {
return products;
};
CertificatesService.fetch_products = function() {
if (CertificatesService.get_products().length) {
return true;
}
if (CPANEL.PAGE.products) {
angular.forEach(CPANEL.PAGE.products, function(product) {
CertificatesService.add_raw_product(product);
});
if (CertificatesService.get_products().length) {
return true;
}
}
products = [];
var deferred = $q.defer();
var apiCall = new APIREQUEST.Class();
apiCall.initialize("Market", "get_all_products");
apiCall.addFilter("enabled", "eq", "1");
apiCall.addFilter("product_group", "eq", "ssl_certificate");
apiCall.addSorting("recommended", "dsc", "numeric");
apiCall.addSorting("x_price_per_domain", "asc", "numeric");
API.promise(apiCall.getRunArguments())
.done(function(response) {
response = response.parsedResponse;
if (response.status) {
deferred.resolve(response);
} else {
deferred.reject(response.error);
}
});
deferred.promise.then(function(results) {
angular.forEach(results.data, function(product) {
// typecasts
product.product_id += "";
["x_warn_after", "x_price_per_domain", "x_max_http_redirects"].forEach(function(attr) {
if (product[attr]) {
product[attr] = cjt2Parse.parseNumber(product[attr]);
}
});
CertificatesService.add_raw_product(product);
});
}, function(error) {
_apiError("Market::get_all_products", error);
});
return deferred.promise;
};
CertificatesService._make_certificate_term_label = function(termUnit, termValue) {
var unitStrings = {
"year": LOCALE.maketext("[quant,_1,Year,Years]", termValue),
"month": LOCALE.maketext("[quant,_1,Month,Months]", termValue),
"day": LOCALE.maketext("[quant,_1,Day,Days]", termValue),
};
return unitStrings[termUnit] || termValue + " " + termUnit;
};
CertificatesService._make_validation_type_label = function(validationType) {
var validationTypeLabels = {
"dv": LOCALE.maketext("[output,abbr,DV,Domain Validated] Certificate"),
"ov": LOCALE.maketext("[output,abbr,OV,Organization Validated] Certificate"),
"ev": LOCALE.maketext("[output,abbr,EV,Extended Validation] Certificate"),
};
return validationTypeLabels[validationType] || validationType;
};
CertificatesService.add_raw_product = function(rawProduct) {
rawProduct.id = rawProduct.product_id;
rawProduct.provider = rawProduct.provider_name;
rawProduct.provider_display_name = rawProduct.provider_display_name || rawProduct.provider;
rawProduct.price = Number(rawProduct.x_price_per_domain);
rawProduct.wildcard_price = Number(rawProduct.x_price_per_wildcard_domain);
rawProduct.wildcard_parent_domain_included = rawProduct.x_wildcard_parent_domain_free && rawProduct.x_wildcard_parent_domain_free.toString() === "1";
rawProduct.icon_mime_type = rawProduct.icon_mime_type ? rawProduct.icon_mime_type : "image/png";
rawProduct.is_wildcard = !isNaN(rawProduct.wildcard_price) ? true : false;
rawProduct.x_certificate_term = rawProduct.x_certificate_term || [1, "year"];
rawProduct.x_certificate_term_display_name = CertificatesService._make_certificate_term_label(rawProduct.x_certificate_term[1], rawProduct.x_certificate_term[0]);
rawProduct.x_certificate_term_key = rawProduct.x_certificate_term.join("_");
rawProduct.x_validation_type_display_name = CertificatesService._make_validation_type_label(rawProduct.x_validation_type);
rawProduct.x_supports_dns_dcv = cjt2Parse.parsePerlBoolean(rawProduct.x_supports_dns_dcv);
rawProduct.validity_period = rawProduct.x_certificate_term;
products.push(rawProduct);
};
CertificatesService.get_domain_search_options = function() {
if (domainSearchOptions) {
return domainSearchOptions;
}
domainSearchOptions = {
domainType: {
label: LOCALE.maketext("Domain Types:"),
item_key: "type",
options: [{
"value": "main_domain",
"label": LOCALE.maketext("Non-Wildcard"),
"description": LOCALE.maketext("Only list Non-Wildcard domains."),
}, {
"value": "wildcard_domain",
"label": LOCALE.maketext("Wildcard"),
"description": LOCALE.maketext("Only list Wildcard domains."),
}],
},
proxyDomainType: {
label: LOCALE.maketext("Service Subdomain Types:"),
item_key: "proxy_type",
options: [{
"value": "proxy_domain",
"label": LOCALE.maketext("[asis,cPanel] Service Subdomains"),
"description": LOCALE.maketext("Only list Service Subdomains."),
}, {
"value": "main_domain",
"label": LOCALE.maketext("Other Domains"),
"description": LOCALE.maketext("Only list non-Service Subdomains."),
}],
},
sslType: {
label: LOCALE.maketext("[asis,SSL] Types:"),
item_key: "certificate_type",
options: [{
"value": "unsecured",
"label": LOCALE.maketext("Unsecured or Self-signed"),
"description": LOCALE.maketext("Only list unsecured or self-signed domains."),
}, {
"value": "dv",
"label": CertificatesService._make_validation_type_label("dv"),
"description": LOCALE.maketext("Only list domains with [asis,DV] Certificates."),
}, {
"value": "ov",
"label": CertificatesService._make_validation_type_label("ov"),
"description": LOCALE.maketext("Only list domains with [asis,OV] Certificates."),
}, {
"value": "ev",
"label": CertificatesService._make_validation_type_label("ev"),
"description": LOCALE.maketext("Only list domains with [asis,EV] Certificates."),
}],
},
sslStatus: {
label: LOCALE.maketext("[asis,SSL] Statuses:"),
item_key: "certificate_status",
options: [{
"value": "unsecured",
"label": LOCALE.maketext("Unsecured"),
"description": LOCALE.maketext("Only list unsecured domains."),
}, {
"value": "active",
"label": LOCALE.maketext("Active"),
"description": LOCALE.maketext("Only list domains with an active certificate."),
}, {
"value": "expired",
"label": LOCALE.maketext("Expired"),
"description": LOCALE.maketext("Only list domains whose certificate is expiring soon."),
}, {
"value": "expiring_soon",
"label": LOCALE.maketext("Expiring Soon"),
"description": LOCALE.maketext("Only list domains with certificates that expire soon."),
}],
},
};
return CertificatesService.get_domain_search_options();
};
CertificatesService.get_product_search_options = function() {
if (productsSearchOptions) {
return productsSearchOptions;
}
productsSearchOptions = {
validationType: {
label: LOCALE.maketext("[asis,SSL] Validation Types"),
item_key: "x_validation_type",
options: [],
},
sslProvider: {
label: LOCALE.maketext("[asis,SSL] Providers"),
item_key: "provider",
options: [],
},
certTerms: {
label: LOCALE.maketext("Certificate Terms"),
item_key: "x_certificate_term_key",
options: [],
},
};
var products = CertificatesService.get_products();
var certTerms = {},
providers = {},
validationTypes = {};
angular.forEach(products, function(product) {
certTerms[product.x_certificate_term_key] = {
"value": product.x_certificate_term_key,
"label": product.x_certificate_term_display_name,
"description": LOCALE.maketext("Only list products with a term of ([_1]).", product.x_certificate_term_display_name),
};
providers[product.provider] = {
"value": product.provider,
"label": product.provider_display_name,
"description": LOCALE.maketext("Only list products from the “[_1]” provider.", product.provider_display_name),
};
validationTypes[product.x_validation_type] = {
"value": product.x_validation_type,
"label": product.x_validation_type_display_name,
"description": LOCALE.maketext("Only list products that use the “[_1]” validation type.", product.x_validation_type_display_name),
};
});
angular.forEach(certTerms, function(item) {
productsSearchOptions.certTerms.options.push(item);
});
angular.forEach(providers, function(item) {
productsSearchOptions.sslProvider.options.push(item);
});
angular.forEach(validationTypes, function(item) {
productsSearchOptions.validationType.options.push(item);
});
for (var key in productsSearchOptions) {
if (productsSearchOptions.hasOwnProperty(key)) {
if (productsSearchOptions[key].options.length <= 1) {
delete productsSearchOptions[key];
}
}
}
return CertificatesService.get_product_search_options();
};
CertificatesService.get_product_by_id = function(providerName, productID) {
for (var i = 0; i < products.length; i++) {
if (products[i].id === productID && products[i].provider === providerName) {
return products[i];
}
}
return;
};
var _ensureDomainCanPassDCV = function(domains, dcvConstraints) {
// A lookup of objects for domains that will be DCVed
// in this function. (The list will exclude, e.g., domains
// that are already DCVed.) Do not confuse with
// dnsDcvDomainObjs, which is specific to DNS DCV.
var allDcvDomainObjs = {};
var httpDcvDomainNames = [];
// A lookup of objects for domains that will be DCVed
// via DNS in this function. Do not confuse with
// allDcvDomainObjs, which includes domains that will not
// undergo DNS DCV (e.g., because they passed HTTP DCV).
var dnsDcvDomainObjs = {};
angular.forEach(domains, function(domain) {
if (domain.resolved === -1) {
allDcvDomainObjs[domain.domain] = domain;
if (_isWildcard(domain.domain)) {
dnsDcvDomainObjs[domain.domain] = domain;
} else {
httpDcvDomainNames.push(domain.domain);
}
domain.dcvPassed = {};
domain.resolving = true;
}
});
if (Object.keys(allDcvDomainObjs).length === 0) {
return;
}
var httpPromise;
if (httpDcvDomainNames.length) {
var productForbidsRedirects = function(p) {
// Compare against 0 to accommodate providers that
// don’t define this particular product attribute.
return 0 === p.x_max_http_redirects;
};
var apiCall = (new APIREQUEST.Class()).initialize(
"DCV",
"check_domains_via_http", {
domain: httpDcvDomainNames,
dcv_file_allowed_characters: JSON.stringify(dcvConstraints.dcv_file_allowed_characters),
dcv_file_random_character_count: dcvConstraints.dcv_file_random_character_count,
dcv_file_extension: dcvConstraints.dcv_file_extension,
dcv_file_relative_path: dcvConstraints.dcv_file_relative_path,
dcv_user_agent_string: dcvConstraints.dcv_user_agent_string,
}
);
// TODO: Currently we always fall back to DNS DCV
// if there are any HTTP redirects, even if the number
// of redirects is within every product’s redirection
// limit. Ideally we shouldn’t fall back to DNS DCV
// in that case; as a practical matter, though, as of
// 2021 cPStore remains the only provider we have ever
// used, and cPStore (i.e., Sectigo) forbids all HTTP
// redirects, so our current implementation happens to
// be correct for now (and the foreseeable future).
//
var prodsThatForbidRedirects = products.filter(productForbidsRedirects);
httpPromise = CertificatesService._runUAPI(apiCall).then(
function(results) {
for (var d = 0; d < httpDcvDomainNames.length; d++) {
var domainName = httpDcvDomainNames[d];
var domain = allDcvDomainObjs[domainName];
domain.resolution_failure_reason = results.data[d].failure_reason;
domain.redirects_count = cjt2Parse.parseNumber(results.data[d].redirects_count);
// Success with redirects likely means that even
// rebuilding .htaccess didn’t fix the issue,
// so the customer will need to investigate manually.
if (domain.redirects_count && !domain.resolution_failure_reason) {
if (prodsThatForbidRedirects.length) {
var message = LOCALE.maketext("“[_1]”’s [output,abbr,DCV,Domain Control Validation] check completed correctly, but the check required an [asis,HTTP] redirection. The system tried to exclude such redirections from this domain by editing the website document root’s “[_2]” file, but the redirection persists. You should investigate further.", _.escape(domain.domain), ".htaccess");
alertService.add({
type: "danger",
message: message,
group: "tlsWizard",
});
}
}
domain.dcvPassed.http = !domain.resolution_failure_reason;
// Send this batch of domains to DNS DCV if the
// current domain’s DCV failed or redirected.
if (!domain.dcvPassed.http || domain.redirects_count) {
dnsDcvDomainObjs[domain.domain] = domain;
} else {
domain.resolved = domain.dcvPassed.http ? 1 : 0;
}
}
},
function(error) {
_apiError("DCV::check_domains_via_http", error);
}
);
} else {
httpPromise = Promise.resolve();
}
return httpPromise.then(
function() {
var domainObjs = Object.values(dnsDcvDomainObjs);
if (domainObjs.length) {
return _dnsDcvPromise(domainObjs);
}
}
).finally( function() {
Object.values(allDcvDomainObjs).forEach( function(domainObj) {
domainObj.resolving = false;
} );
} );
};
CertificatesService.get_default_provider_name = function() {
var product;
var products = CertificatesService.get_products();
/* if it's set, use that */
var cpStoreProducts = products.filter(function(product) {
if (product.provider_name === "cPStore") {
return true;
}
return false;
});
if (cpStoreProducts.length) {
/* if cPStore exists, use that */
product = cpStoreProducts[0];
} else {
/* otherwise use first */
product = products[0];
}
return product.provider_name;
};
CertificatesService.get_provider_specific_dcv_constraints = function(providerName) {
var apiCall = (new APIREQUEST.Class()).initialize(
"Market",
"get_provider_specific_dcv_constraints", {
provider: providerName,
}
);
return CertificatesService._runUAPI(apiCall);
};
CertificatesService.ensure_domains_can_pass_dcv = function(domains, providerName) {
return CertificatesService.get_provider_specific_dcv_constraints(providerName).then(function(results) {
return _ensureDomainCanPassDCV(domains, results.data);
}, function(error) {
_apiError("Market::get_provider_specific_dcv_constraints", error);
});
};
CertificatesService.verify_login_token = function(provider, loginToken, urlAfterLogin) {
var deferred = $q.defer();
var apiCall = new APIREQUEST.Class();
apiCall.initialize("Market", "validate_login_token");
apiCall.addArgument("login_token", loginToken);
apiCall.addArgument("url_after_login", urlAfterLogin);
apiCall.addArgument("provider", provider);
API.promise(apiCall.getRunArguments())
.done(function(response) {
response = response.parsedResponse;
if (response.status) {
deferred.resolve(response);
} else {
deferred.reject(response.error);
}
});
return deferred.promise;
};
CertificatesService.set_url_after_checkout = function(provider, accessToken, orderID, urlAfterCheckout) {
var deferred = $q.defer();
var apiCall = new APIREQUEST.Class();
apiCall.initialize("Market", "set_url_after_checkout");
apiCall.addArgument("provider", provider);
apiCall.addArgument("access_token", accessToken);
apiCall.addArgument("order_id", orderID);
apiCall.addArgument("url_after_checkout", urlAfterCheckout);
API.promise(apiCall.getRunArguments())
.done(function(response) {
response = response.parsedResponse;
// This specific case is unusual because we want to
// give the error handler the entire object so that it
// can check for “data” in the response. See the
// documentation for Market::set_url_after_checkout.
var method = response.status ? "resolve" : "reject";
deferred[method](response);
});
return deferred.promise;
};
// Returns a YYYY-MM-DD string
//
// AngularJS sets all date models as Date objects,
// so we convert those to YYYY-MM-DD for the order.
// It’s a bit hairy because we can’t use
// .toISOString() since that date will be UTC, while
// the numbers we want are the ones the user gave.
function _dateToYYYYMMDD(theDate) {
return [
theDate.getFullYear(),
_sprintf02D(1 + theDate.getMonth()),
_sprintf02D(theDate.getDate()),
].join("-");
}
var _requestCertificates = function(provider, accessToken, certificates, urlAfterCheckout) {
var deferred = $q.defer();
var apiCall = new APIREQUEST.Class();
apiCall.initialize("Market", "request_ssl_certificates");
apiCall.addArgument("provider", provider);
apiCall.addArgument("access_token", accessToken);
apiCall.addArgument("url_after_checkout", urlAfterCheckout);
var jsonCertificates = certificates.map(function(cert) {
var newCertificate = {
product_id: cert.get_product().id,
subject_names: cert.get_subject_names(),
vhost_names: cert.get_virtual_hosts(),
price: cert.get_price(),
validity_period: cert.get_validity_period(),
};
if (cert.get_product().x_identity_verification) {
var identityVerification = cert.get_identity_verification();
newCertificate.identity_verification = {};
cert.get_product().x_identity_verification.forEach(function(idv) {
var k = idv.name;
// If the form didn’t give us any data for it,
// then don’t submit it.
if (!identityVerification[k]) {
return;
}
// “date” items come from AngularJS as Date objects,
// but they come from JSON as ISO 8601 strings.
if (idv.type === "date") {
var dateObject;
try {
dateObject = new Date(identityVerification[k]);
} catch (e) {
$log.warn("new Date() failed; ignoring", identityVerification[k], e);
}
if (dateObject) {
newCertificate.identity_verification[k] = _dateToYYYYMMDD(dateObject);
}
} else {
newCertificate.identity_verification[k] = identityVerification[k];
}
});
}
// A lookup map of the wildcard subject names.
var wildcardDomainMap = {};
newCertificate.subject_names.forEach(function(subject_name) {
var domain = subject_name.name;
if (domain.indexOf("*.") === 0) {
wildcardDomainMap[domain] = true;
}
});
// An array of objects that describe subject name
// entries to add for www. subdomains.
var validWWWDomains = [];
newCertificate.subject_names.forEach(function(subject_name) {
var domain = subject_name.name;
// Don’t add www. if we already have the wildcard
// for the domain. For example, if the cert will
// secure foo.com and *.foo.com, there’s no need
// for www.foo.com, so we leave it off.
var addWwwYn = !wildcardDomainMap["*." + domain];
// Only add www. if that domain actually exists.
addWwwYn = addWwwYn && wwwDomainsLookup["www." + domain];
if ( addWwwYn ) {
validWWWDomains.push( {
type: "dNSName",
name: "www." + domain,
dcv_method: subject_name.dcv_method,
});
}
});
newCertificate.subject_names = newCertificate.subject_names.concat(validWWWDomains);
return JSON.stringify(newCertificate);
});
apiCall.addArgument("certificate", jsonCertificates);
API.promise(apiCall.getRunArguments())
.done(function(response) {
response = response.parsedResponse;
if (response.status) {
deferred.resolve(response);
} else {
deferred.reject(response.error);
}
});
deferred.promise.catch(CertificatesService.reset.bind(CertificatesService));
return deferred.promise;
};
CertificatesService.request_certificates = function(provider, accessToken, certificates, urlAfterCheckout) {
// This now requires that wwwDomainsLookup be populated.
// We ensure that by calling fetch_domains().
var domains_fetch = CertificatesService.fetch_domains();
var callback = function() {
return _requestCertificates(provider, accessToken, certificates, urlAfterCheckout);
};
if (domains_fetch.then) {
return domains_fetch.then(callback);
}
return callback();
};
CertificatesService.get_pending_certificates = function() {
return pendingCertificates;
};
var _assignPendingCertificates = function(newPending) {
pendingCertificates = newPending;
pendingCertificates.forEach(function(pcert) {
// Typecasts
pcert.order_id += "";
pcert.order_item_id += "";
pcert.product_id += "";
});
};
CertificatesService.fetch_pending_certificates = function() {
if (CPANEL.PAGE.pending_certificates) {
_assignPendingCertificates(CPANEL.PAGE.pending_certificates);
/* if exists on page load use it, but if view switching, we want to reload, so clear this variable */
CPANEL.PAGE.pending_certificates = null;
if (pendingCertificates.length) {
return true;
}
}
var deferred = $q.defer();
var apiCall = new APIREQUEST.Class();
apiCall.initialize("Market", "get_pending_ssl_certificates");
API.promise(apiCall.getRunArguments())
.done(function(response) {
response = response.parsedResponse;
if (response.status) {
deferred.resolve(response);
} else {
deferred.reject(response.error);
}
});
deferred.promise.then(function(result) {
_assignPendingCertificates(result.data);
}, function(error) {
_apiError("Market::pending_certificates", error);
});
return deferred.promise;
};
CertificatesService.add_raw_installed_host = function(ihost) {
if (!installedHosts) {
installedHosts = [];
}
ihost.certificate.is_self_signed = parseInt(ihost.certificate.is_self_signed, 10) === 1;
installedHosts.push(ihost);
installedHostsMap[ihost.servername] = ihost;
};
CertificatesService.get_domain_certificate = function(domain) {
return sslDomains[domain];
};
CertificatesService.get_domain_by_domain = function(domain) {
var domains = CertificatesService.get_all_domains();
for (var i = 0; i < domains.length; i++) {
if (domains[i].domain === domain) {
return domains[i];
}
}
return;
};
CertificatesService.get_virtual_host_certificate = function(virtualHost) {
if (!installedHosts) {
return;
}
for (var i = 0; i < installedHosts.length; i++) {
if (installedHosts[i].servername === virtualHost.display_name) {
return installedHosts[i];
}
}
return installedHosts[0] ? installedHosts[0] : undefined;
};
CertificatesService.fetch_installed_hosts = function() {
if (installedHosts) {
return true;
}
if (CPANEL.PAGE.installed_hosts) {
if (!CPANEL.PAGE.installed_hosts.length) {
return true; /* Defined, but no installed hosts */
}
installedHosts = [];
installedHostsMap = {};
sslDomains = {};
angular.forEach(CPANEL.PAGE.installed_hosts, function(ihost) {
CertificatesService.add_raw_installed_host(ihost);
});
if (installedHosts.length) {
return true;
}
}
var deferred = $q.defer();
var apiCall = new APIREQUEST.Class();
apiCall.initialize("SSL", "installed_hosts");
API.promise(apiCall.getRunArguments())
.done(function(response) {
response = response.parsedResponse;
if (response.status) {
deferred.resolve(response);
} else {
deferred.reject(response.error);
}
});
deferred.promise.then(function(result) {
installedHosts = [];
installedHostsMap = {};
sslDomains = {};
angular.forEach(result.data, function(ihost) {
CertificatesService.add_raw_installed_host(ihost);
});
}, function(error) {
_apiError("SSL::installed_hosts", error);
});
return deferred.promise;
};
var _makeBatch = function(calls) {
var apiCall = new APIREQUEST.Class();
apiCall.initialize("Batch", "strict");
apiCall.addArgument("command", calls.map(JSON.stringify, JSON));
return apiCall;
};
CertificatesService.install_certificate = function(cert, vhostNames) {
var apiCall = _makeBatch(vhostNames.map(function(vh) {
return [
"SSL",
"install_ssl", {
cert: cert,
domain: vh,
},
];
}));
return CertificatesService._runUAPI(apiCall);
};
CertificatesService.get_ssl_certificate_if_available = function(provider, orderItemID) {
var apiCall = new APIREQUEST.Class();
apiCall.initialize("Market", "get_ssl_certificate_if_available");
apiCall.addArgument("provider", provider);
apiCall.addArgument("order_item_id", orderItemID);
return CertificatesService._runUAPI(apiCall);
};
CertificatesService.get_installed_ssl_for_domain = function(domain) {
var apiCall = new APIREQUEST.Class();
apiCall.initialize("SSL", "installed_host");
apiCall.addArgument("domain", domain);
return CertificatesService._runUAPI(apiCall);
};
CertificatesService.cancel_pending_ssl_certificate_and_poll = function(provider, orderItemID) {
var apiCall = _makeBatch([
[
"Market",
"cancel_pending_ssl_certificate", {
provider: provider,
order_item_id: orderItemID,
},
],
[
"Market",
"get_ssl_certificate_if_available", {
provider: provider,
order_item_id: orderItemID,
},
],
]);
return CertificatesService._runUAPI(apiCall);
};
CertificatesService.cancel_pending_ssl_certificates = function(provider, orderItemIDs) {
var apiCall = _makeBatch(orderItemIDs.map(function(oiid) {
return [
"Market",
"cancel_pending_ssl_certificate", {
provider: provider,
order_item_id: oiid,
},
];
}));
return CertificatesService._runUAPI(apiCall);
};
CertificatesService.cancel_certificate = function(virtualHost, provider, orderItemID) {
CertificatesService.cancel_pending_ssl_certificate(provider, orderItemID).then(function() {
angular.forEach(virtualHost.get_selected_domains(), function(domain) {
domain.selected = false;
});
});
};
CertificatesService.process_ssl_pending_queue = function() {
var deferred = $q.defer();
var apiCall = new APIREQUEST.Class();
apiCall.initialize("Market", "process_ssl_pending_queue");
API.promise(apiCall.getRunArguments())
.done(function(response) {
response = response.parsedResponse;
if (response.status) {
deferred.resolve(response);
} else {
deferred.reject(response.error);
}
});
return deferred.promise;
};
CertificatesService.hard_reset = function() {
CertificatesService.reset();
CPANEL.PAGE.domains = null;
};
CertificatesService.reset = function() {
virtualHosts = [];
allDomains = [];
products = [];
installedHosts = null;
purchasingCerts = [];
sslDomains = {};
orders = [];
wildcardMap = {};
};
CertificatesService.reset_purchasing_certificates = function() {
purchasingCerts = [];
};
CertificatesService.dismiss_introduction = function() {
introductionDismissed = true;
};
CertificatesService.show_introduction_block = function() {
return !introductionDismissed && !alertService.getAlerts().length;
};
CertificatesService.parseCertificateDomainDetails = function(rawDomainDetails) {
var domainDetails = {};
angular.forEach(rawDomainDetails, function(value) {
domainDetails[value.domain] = value.status;
});
return domainDetails;
};
CertificatesService.parseCertificateStatusDetails = function(rawStatusDetails, rawActionUrls) {
var statusDetails = [];
if (!rawStatusDetails) {
return statusDetails;
}
rawActionUrls = rawActionUrls ? rawActionUrls : {};
angular.forEach(rawStatusDetails, function(detail, key) {
var detailString = STATUS_DETAIL_STRINGS[key];
if (!detailString) {
detailString = {
label: key,
inProgress: "",
};
}
if (detail === "not-applicable" || key === "certificateStatus" || key === "csrStatus") {
return;
}
if (detail) {
var status;
if (detail === "not-completed") {
status = detailString.inProgress;
} else if (detail === "completed") {
status = LOCALE.maketext("Complete.");
} else {
status = detail;
}
var detailItem = {
label: detailString.label,
status: status,
rawLabel: key,
rawStatus: detail,
};
if (rawActionUrls[key]) {
detailItem.actionLabel = ACTION_URL_LABELS[key] || ACTION_URL_LABELS.DEFAULT;
detailItem.actionURL = rawActionUrls[key];
detailItem.actionIcon = ACTION_URL_ICONS[key] || ACTION_URL_ICONS.DEFAULT;
}
statusDetails.push(detailItem);
}
});
return statusDetails;
};
CertificatesService.getCertificateStatusDetails = function(provider, orderItemID) {
var apiCall = new APIREQUEST.Class();
apiCall.initialize("Market", "get_certificate_status_details", {
"provider": provider,
"order_item_id": orderItemID,
});
return CertificatesService._runUAPI(apiCall).then(function(result) {
return {
statusDetails: CertificatesService.parseCertificateStatusDetails(result.data.status_details, result.data.action_urls),
domainDetails: CertificatesService.parseCertificateDomainDetails(result.data.domain_details),
};
});
};
return CertificatesService;
}
return app.factory("CertificatesService", ["VirtualHost", "Certificate", "$q", "$log", "alertService", CertificatesServiceFactory]);
});
/*
* base/frontend/jupiter/security/tls_wizard/services/LocationService.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 */
define(
'app/services/LocationService',[
"angular",
],
function(angular) {
"use strict";
return angular.module("App").factory( "LocationService", [
"$location",
function the_factory($location) {
return {
go_to_last_create_route: function() {
return $location.path(this.last_create_route());
},
last_create_route: function() {
if (!localStorage.getItem("tls_wizard_create_route")) {
localStorage.setItem("tls_wizard_create_route", "/create");
}
return localStorage.getItem("tls_wizard_create_route");
},
go_to_simple_create_route: function() {
localStorage.removeItem("tls_wizard_create_route");
return this.go_to_last_create_route();
},
go_to_advanced_create_route: function() {
localStorage.setItem("tls_wizard_create_route", "/create-advanced");
return this.go_to_last_create_route();
},
};
},
] );
}
);
/*
* base/frontend/jupiter/security/tls_wizard/services/IdVerDefaults.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 */
define(
'app/services/IdVerDefaults',[
"angular",
"lodash",
"app/services/CertificatesService",
],
function(angular, _) {
"use strict";
return angular.module("App").factory( "IdVerDefaults", [
"CertificatesService",
function the_factory(CertificatesService) {
return {
restore_previous: function( id_ver, saved_idver ) {
_.assign( id_ver, saved_idver );
CertificatesService.get_products().forEach( function(p) {
if (!p.x_identity_verification) {
return;
}
p.x_identity_verification.forEach( function(id_v) {
if (id_v.type === "date") {
// This was doing an “in” check, but
// there were null values getting put into
// the Date constructor, which yields
// 1 Jan 1970 00:00:00 UTC.
if (id_ver[id_v.name]) {
id_ver[id_v.name] = new Date(id_ver[id_v.name]);
}
}
} );
} );
},
set_defaults: function set_defaults(id_ver) {
// Each Set will store identity verification variable names,
// such as “date_of_incorporation”. Those variables then
// will receive default values.
//
// The idea is that, if one provider calls a field
// “countryCode”, and another calls it “country_name”, both
// will still receive the same reasonable default.
//
// It is ASSUMED that different providers will use sensible
// names that don’t clobber each other’s products, e.g.,
// “Hank’s SSL” won’t have a field named “localityName” that
// actually takes a “date” or something, which would put in
// a nonsensical default for that value in “Sal’s SSL”’s
// products.
//
var type_defaults = {};
// We could put date in here, but since the field is
// optional let’s let it lie.
["country_code"].forEach( function(t) {
type_defaults[t] = new Set();
} );
CertificatesService.get_products().forEach( function(p) {
if (!p.x_identity_verification) {
return;
}
p.x_identity_verification.forEach( function(id_v) {
if (id_v.type in type_defaults) {
type_defaults[id_v.type].add(id_v.name);
}
} );
} );
type_defaults.country_code.forEach( function(v) {
id_ver[v] = CPANEL.PAGE.guessed_country_code;
} );
},
};
},
] );
}
);
/*
* base/frontend/jupiter/security/tls_wizard/services/CountriesService.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
*/
/* jshint -W100 */
/* eslint-disable camelcase */
define(
'app/services/CountriesService',[
"angular",
],
function(angular) {
"use strict";
return angular.module("App").factory( "CountriesService", [
function the_factory() {
return CPANEL.PAGE.countries;
},
] );
}
);
/*
* base/frontend/jupiter/security/tls_wizard/views/VirtualHostsController.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
*/
/* global define: false */
/* jshint -W100 */
/* eslint-disable camelcase */
// Then load the application dependencies
define(
'app/views/VirtualHostsController',[
"angular",
"cjt/util/locale",
"jquery",
"cjt/modules",
"ngSanitize",
"app/services/CertificatesService",
"app/services/IdVerDefaults",
"cjt/filters/qaSafeIDFilter",
"cjt/directives/cpanel/searchSettingsPanel",
"cjt/directives/triStateCheckbox",
"cjt/directives/spinnerDirective",
"cjt/decorators/growlDecorator",
"app/services/CountriesService",
"app/services/LocationService",
],
function(angular, LOCALE, $) {
"use strict";
var app = angular.module("App");
function VirtualHostsController(
$rootScope,
$scope,
$controller,
$location,
$filter,
$timeout,
$sce,
$routeParams,
$window,
CertificatesService,
IdVerDefaults,
SpinnerAPI,
growl,
COUNTRIES,
LocationService,
SearchSettingsModel,
alertService) {
$scope.show_introduction_block = CertificatesService.show_introduction_block;
$scope.domains = CertificatesService.get_all_domains();
$scope.virtual_hosts = CertificatesService.get_virtual_hosts();
$scope.pending_certificates = CertificatesService.get_pending_certificates();
$scope.showExistingCertificates = false;
$scope.working_virtual_host = null;
$scope.LOCALE = LOCALE;
$scope.resolution_timeout = 0;
$scope.cart_items = [];
$scope.filterValue = null;
$scope.checkout_mode = false;
$scope.filteredProducts = [];
$scope.showAdvancedSettings = true;
$rootScope.addToCartGrowl = null;
$scope.COUNTRIES = COUNTRIES;
var identityVerification = {};
$scope.identity_verification = identityVerification;
var savedIDVer = CertificatesService.get_stored_extra_settings().advanced_identity_verification;
for (var vh = 0; vh < $scope.virtual_hosts.length; vh++) {
var vHostName = $scope.virtual_hosts[vh].get_display_name();
identityVerification[vHostName] = {};
if (savedIDVer && savedIDVer[vHostName]) {
IdVerDefaults.restore_previous(identityVerification[vHostName], savedIDVer[vHostName]);
} else {
IdVerDefaults.set_defaults(identityVerification[vHostName]);
}
}
// reset on visit to purchase certs
angular.forEach($scope.virtual_hosts, function(virtualHost) {
virtualHost.reset();
/* don't show wildcards in this interface */
virtualHost.show_wildcards = false;
});
/* to reset after reset */
$scope.domains = CertificatesService.get_all_domains();
$scope.domains = $filter("filter")($scope.domains, {
is_wildcard: false,
});
$scope.virtual_hosts = CertificatesService.get_virtual_hosts();
$scope.virtual_hosts = $filter("filter")($scope.virtual_hosts, function(vhost) {
return !vhost.display_name.match(/^\*\./);
});
var defaultSearchValues = {
"certTerms": {
"1_year": true,
"2_year": false,
"3_year": false,
},
};
$scope.searchFilterOptions = new SearchSettingsModel(CertificatesService.get_product_search_options(), defaultSearchValues);
$scope.filter_products = function() {
var filteredProducts = CertificatesService.get_products();
filteredProducts = $scope.searchFilterOptions.filter(filteredProducts);
$scope.filteredProducts = filteredProducts;
};
$scope.slow_scroll_to_top = function() {
$("body,html").animate({
"scrollTop": 0,
}, 2000);
};
$scope.go_to_product_filters = function() {
$scope.showAdvancedSettings = true;
$scope.slow_scroll_to_top();
};
var buildSteps = ["domains", "providers", "cert-info"];
var qaFilter = $filter("qaSafeID");
$scope.get_cart_certs_title = function() {
return LOCALE.maketext("[quant,_1,Certificate,Certificates]", $scope.get_cart_items().length);
};
$scope.get_vhost_showing_text = function() {
var vhosts = $filter("filter")($scope.get_virtual_hosts(), $scope.filterValue);
return LOCALE.maketext("[output,strong,Showing] [numf,_1] of [quant,_2,website,websites]", vhosts.length, $scope.get_virtual_hosts().length);
};
$scope.get_domains_showing_text = function(virtualHost) {
var numStart = 1 + virtualHost.display_meta.start;
var numLimit = virtualHost.display_meta.limit;
var numOf = virtualHost.get_domain_count(true);
return LOCALE.maketext("[output,strong,Showing] [numf,_1] - [numf,_2] of [quant,_3,domain,domains].", numStart, numLimit, numOf);
};
$scope.deselect_unresolved_msg = function(virtualHost) {
var unresolvedCount = virtualHost.get_selected_domains().filter(function(domain) {
return domain.resolved === 0;
}).length;
return LOCALE.maketext("Deselect all unresolved domains ([numf,_1]).", unresolvedCount);
};
$scope.go_to_pending = function(orderItemID) {
if (orderItemID) {
$location.path("/pending-certificates/").search("orderItemID", orderItemID);
} else {
$location.path("/pending-certificates");
}
};
$scope.pending_certificate = function(virtualHost) {
var result = false;
angular.forEach($scope.pending_certificates, function(pcert) {
angular.forEach(pcert.vhost_names, function(vhostName) {
if (vhostName === virtualHost.display_name) {
result = pcert.order_item_id;
}
});
});
return result;
};
$scope.get_certpanel_class = function(virtualHost) {
if (!$scope.pending_certificate(virtualHost)) {
return "panel-primary";
} else {
return "panel-default";
}
};
$scope.view_pending_certificate = function(virtualHost) {
var orderItemID = $scope.pending_certificate(virtualHost);
$scope.go_to_pending(orderItemID);
};
$scope.get_currency_string = function(num, priceUnit) {
num += 0.001;
var str = LOCALE.numf(num);
str = "$" + str.substring(0, str.length - 1);
if (priceUnit) {
str += " " + priceUnit;
}
return str;
};
$scope.get_virtual_hosts = function() {
var virtualHosts = $scope.virtual_hosts;
if ($scope.filterValue) {
virtualHosts = $filter("filter")(virtualHosts, $scope.filterValue);
}
if ($scope.checkout_mode) {
virtualHosts = $filter("filter")(virtualHosts, {
added_to_cart: true,
});
}
return virtualHosts;
};
$scope.get_virtual_host_classes = function(virtualHost) {
return {
"col-lg-4": $scope.virtual_hosts.length > 2,
"col-lg-6": $scope.virtual_hosts.length <= 2,
"panel-success": virtualHost.is_ssl,
};
};
$scope.get_step_panel_classes = function(virtualHost, current) {
var classes = ["col-sm-12", "col-xs-12"];
// add step type specific classes
if ($scope.working_virtual_host === virtualHost.display_name) {
classes.push("col-md-4");
classes.push("col-lg-4");
} else {
classes.push("col-md-12");
classes.push("col-lg-12");
}
if (current) {
classes.push("cert-step-panel-current");
}
return classes;
};
$scope.get_cart_price = function() {
var price = 0;
angular.forEach($scope.get_cart_items(), function(virtualHost) {
price += virtualHost.get_price();
});
return price;
};
$scope.get_cart_items = function() {
$scope.cart_items = $filter("filter")($scope.virtual_hosts, {
added_to_cart: true,
});
return $scope.cart_items;
};
$scope.checkout = function() {
$scope.checkout_mode = true;
};
$scope.get_product_form_fields = function() {
return [];
};
$scope.get_step = function(virtualHost) {
return virtualHost.get_step();
};
$scope.go_step = function(virtualHost, step) {
if ($scope.can_step(virtualHost, step)) {
return virtualHost.go_step(step);
}
};
$scope.focus_virtual_host = function() {
// $scope.working_virtual_host = virtual_host.display_name;
};
$scope.check_selected_domains = function(virtualHost) {
if ($scope.resolution_timeout) {
$timeout.cancel($scope.resolution_timeout);
}
if (virtualHost && virtualHost.added_to_cart) {
var domains = $filter("filter")(virtualHost.get_selected_domains(), function(domain) {
if (domain.resolved !== 1) {
return true;
}
});
if (domains.length) {
alertService.add({
type: "danger",
message: LOCALE.maketext("You have altered an item in your cart. The system has removed that item. After you make the necessary changes, add that item back to your cart."),
group: "tlsWizard",
});
$scope.remove_from_cart(virtualHost);
}
}
$scope.resolution_timeout = $timeout(function(domains) {
$scope.ensure_dns(domains);
}, 850, true, CertificatesService.get_all_selected_domains()); // JNK: Lowered wait time since I keep missing it when testing
};
$scope.deselect_domains = function(domains) {
angular.forEach(domains, function(domain) {
domain.selected = false;
});
};
$scope.get_current_or_default_provider = function() {
return CertificatesService.get_default_provider_name();
};
$scope.ensure_dns = function(domains) {
domains = $filter("filter")(domains, {
selected: true,
resolved: -1,
});
if (!domains.length) {
return false;
}
angular.forEach(domains, function(domain) {
domain.resolving = true;
SpinnerAPI.start($scope.get_spinner_id(domain.domain));
});
var providerName = $scope.get_current_or_default_provider();
return CertificatesService.ensure_domains_can_pass_dcv(domains, providerName).finally(function() {
var toFocusElement;
angular.forEach(domains, function(domain) {
if (domain.resolved === 0 && domain.selected) {
/* checked domain doesn't resolve */
var vhostIndex = CertificatesService.get_virtual_host_by_display_name(domain.vhost_name);
var vhost = $scope.virtual_hosts[vhostIndex];
if (vhost && vhost.get_step() === "providers") {
/* if we are on the providers section, send them back to the domains section to see errors */
$scope.go_step(vhost, "domains");
/* set focus to top domain in domains list */
var element = $window.document.getElementById($scope.get_domain_id(domain));
if (element && !toFocusElement) {
/* only focus first element */
toFocusElement = element;
$timeout(function() {
toFocusElement.focus();
});
}
}
}
SpinnerAPI.stop($scope.get_spinner_id(domain.domain));
});
});
};
$scope.get_domain_id = function(domainObject) {
return qaFilter(domainObject.vhost_name + "_" + domainObject.domain);
};
$scope.check_product_match = function(productA, productB) {
if (!productA || !productB) {
return false;
}
if (productA.id === productB.id && productA.provider === productB.provider) {
return true;
}
};
$scope.can_step = function(virtualHost, step) {
if (step === buildSteps[0]) {
return true;
} else if (step === buildSteps[1]) {
// providers
/* can progress if domains are selected, after they are resolved they user is kicked back to domains if there is an error */
return virtualHost.get_selected_domains().length ? true : false;
} else if (step === buildSteps[2]) {
// cert-info
var product = virtualHost.get_product();
if (!product) {
return false;
}
product = CertificatesService.get_product_by_id(product.provider, product.id);
if (!product) {
return false;
}
if (!$scope.get_product_form_fields(product)) {
return false;
}
}
return false;
};
$scope.get_product_by_id = function(providerName, productID) {
return CertificatesService.get_product_by_id(providerName, productID);
};
$scope.can_next_step = function(virtualHost) {
var currentStep = virtualHost.get_step();
var nextStep;
angular.forEach(buildSteps, function(step, index) {
if (step === currentStep) {
nextStep = buildSteps[index + 1];
}
});
return $scope.can_step(virtualHost, nextStep);
};
$scope.next_step = function(virtualHost) {
var currentStep = virtualHost.get_step();
var nextStep;
angular.forEach(buildSteps, function(step, index) {
if (step === currentStep) {
nextStep = buildSteps[index + 1];
}
});
if ($scope.can_step(virtualHost, nextStep)) {
$scope.focus_virtual_host(virtualHost);
virtualHost.go_step(nextStep);
}
};
$scope.get_spinner_id = function(domain) {
return qaFilter("dns_resolving_" + domain);
};
$scope.get_products = function(virtualHost) {
return $scope.filteredProducts;
};
$scope.set_product = function(virtualHost, product) {
virtualHost.set_product_price(product.price);
virtualHost.set_product(product);
};
$scope.all_domains_resolved = function(virtualHost) {
var domains = virtualHost.get_selected_domains();
domains = $filter("filter")(domains, function(domain) {
if (domain.resolved !== 1) {
return false;
}
return true;
});
if (domains.length === 0) {
// No Resolved and Selected Domains
return false;
}
return true;
};
$scope.can_add_to_cart = function(virtualHost) {
var product = virtualHost.get_product();
if (!product) {
return false;
}
product = CertificatesService.get_product_by_id(product.provider, product.id);
if (!product) {
// No Valid Product Selected
return false;
}
return true;
};
$scope.add_to_cart = function(virtualHost) {
if (!$scope.can_add_to_cart(virtualHost) || !$scope.all_domains_resolved(virtualHost)) {
return false;
}
virtualHost.added_to_cart = true;
virtualHost.go_step("added-to-cart");
virtualHost.set_identity_verification($scope.identity_verification[virtualHost.display_name]);
$scope.working_virtual_host = null;
// REFACTOR:: Should find a way to do this with CJT2/alertService and remove
// growl usage here.
if ($rootScope.addToCartGrowl) {
$rootScope.addToCartGrowl.ttl = 0;
$rootScope.addToCartGrowl = null;
}
var options = {
ttl: -1,
variables: {
buttonLabel: LOCALE.maketext("Proceed to checkout."),
showAction: true,
action: function() {
$scope.purchase();
},
},
};
$rootScope.addToCartGrowl = growl.success(LOCALE.maketext("Item Successfully Added to Cart."), options);
};
$scope.get_domain_certificate = function(domain) {
return CertificatesService.get_domain_certificate(domain);
};
$scope.view_existing_certificate = function() {
};
$scope.get_virtual_host_certificate = function(virtualHost) {
return CertificatesService.get_virtual_host_certificate(virtualHost);
};
$scope.build_csr_url = function(virtualHost) {
var ihost = $scope.get_virtual_host_certificate(virtualHost);
if (ihost && ihost.certificate) {
var url = "";
url += "../../ssl/install.html?id=";
url += encodeURIComponent(ihost.certificate.id);
return url;
}
};
$scope.get_existing_certificate_name = function(virtualHost) {
var ihost = $scope.get_virtual_host_certificate(virtualHost);
var name;
if (ihost && ihost.certificate) {
var cert = ihost.certificate;
if (cert.validation_type === "dv") {
name = LOCALE.maketext("A [output,abbr,DV,Domain Validated] certificate is installed.");
} else if (cert.validation_type === "ov") {
name = LOCALE.maketext("An [output,abbr,OV,Organization Validated] certificate is installed.");
} else if (cert.validation_type === "ev") {
name = LOCALE.maketext("An [output,abbr,EV,Extended Validation] certificate is installed.");
} else if (cert.is_self_signed) {
name = LOCALE.maketext("A self-signed certificate is installed.");
}
}
if (!name) {
name = LOCALE.maketext("A certificate of unknown type is installed.");
}
return name;
};
$scope.get_domain_lock_classes = function(virtualHost) {
var ihost = $scope.get_virtual_host_certificate(virtualHost);
if (ihost && ihost.certificate) {
if (ihost.certificate.is_self_signed) {
return "grey-padlock";
} else {
return "green-padlock";
}
}
};
$scope.remove_from_cart = function(virtualHost) {
if ($rootScope.addToCartGrowl) {
$rootScope.addToCartGrowl.ttl = 0;
$rootScope.addToCartGrowl.destroy();
$rootScope.addToCartGrowl = null;
}
virtualHost.added_to_cart = false;
};
$scope.go_to_simple = function() {
CertificatesService.hard_reset();
LocationService.go_to_simple_create_route().search("");
};
$scope.purchase = function() {
/* storing on and removing from rootscope due to scope change */
if ($rootScope.addToCartGrowl) {
$rootScope.addToCartGrowl.ttl = 0;
$rootScope.addToCartGrowl.destroy();
$rootScope.addToCartGrowl = null;
}
var success = CertificatesService.save({
advanced_identity_verification: identityVerification,
});
if (!success) {
alertService.add({
type: "danger",
message: LOCALE.maketext("Failed to save information to browser cache."),
group: "tlsWizard",
});
} else {
$location.path("/purchase");
}
};
if ($routeParams["domain"]) {
angular.forEach($filter("filter")($scope.domains, {
domain: $routeParams["domain"],
}, true), function(domain) {
domain.selected = true;
$scope.check_selected_domains(domain.vhost_name);
});
/* refresh virtual_hosts */
$scope.virtual_hosts = CertificatesService.get_virtual_hosts();
$scope.filterValue = $routeParams["domain"];
}
}
app.controller("VirtualHostsController",
[
"$rootScope",
"$scope",
"$controller",
"$location",
"$filter",
"$timeout",
"$sce",
"$routeParams",
"$window",
"CertificatesService",
"IdVerDefaults",
"spinnerAPI",
"growl",
"CountriesService",
"LocationService",
"SearchSettingsModel",
"alertService",
VirtualHostsController,
]);
});
/*
* base/frontend/jupiter/security/tls_wizard/views/PurchaseSimpleController.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
*/
/* global define: false */
/* jshint -W100 */
/* eslint-disable camelcase */
// Then load the application dependencies
define(
'app/views/PurchaseSimpleController',[
"angular",
"lodash",
"cjt/util/locale",
"app/views/Certificate",
"app/services/CountriesService",
"cjt/modules",
"ngSanitize",
"app/services/CertificatesService",
"app/services/LocationService",
"app/services/IdVerDefaults",
"cjt/directives/cpanel/searchSettingsPanel",
"cjt/directives/triStateCheckbox",
"cjt/filters/qaSafeIDFilter",
"cjt/directives/spinnerDirective",
"cjt/directives/quickFiltersDirective",
],
function(angular, _, LOCALE) {
"use strict";
var app = angular.module("App");
app.controller("PurchaseSimpleController", [
"$rootScope",
"$scope",
"$controller",
"$location",
"$filter",
"$timeout",
"$sce",
"$routeParams",
"$window",
"CertificatesService",
"IdVerDefaults",
"CountriesService",
"Certificate",
"LocationService",
"SearchSettingsModel",
"alertService",
function($rootScope,
$scope,
$controller,
$location,
$filter,
$timeout,
$sce,
$routeParams,
$window,
CertificatesService,
IdVerDefaults,
COUNTRIES,
Certificate,
LocationService,
SearchSettingsModel,
alertService) {
$scope.show_introduction_block = CertificatesService.show_introduction_block;
$scope.domains = CertificatesService.get_all_domains(true);
$scope.virtual_hosts = CertificatesService.get_virtual_hosts();
$scope.pending_certificates = CertificatesService.get_pending_certificates();
$scope.showExistingCertificates = false;
$scope.selected_domains = [];
$scope.working_virtual_host = null;
$scope.LOCALE = LOCALE;
$scope.resolution_timeout = 0;
$scope.show_wildcard_domains = true;
$scope.cart_items = [];
$scope.checkout_mode = false;
$scope.missing_base_domains = [];
$scope.showAdvancedProductSettings = true;
$scope.panels = {};
$rootScope.addToCartGrowl = null;
$scope.COUNTRIES = COUNTRIES;
$scope.identity_verification = {};
var savedIDVer = CertificatesService.get_stored_extra_settings().simple_identity_verification;
if (savedIDVer) {
IdVerDefaults.restore_previous($scope.identity_verification, savedIDVer);
} else {
IdVerDefaults.set_defaults($scope.identity_verification);
}
// reset on visit to purchase certs
angular.forEach($scope.virtual_hosts, function(virtualHost) {
virtualHost.reset();
/* ensure wildcards are shown in this interface */
virtualHost.show_wildcards = true;
});
/* build map for lookup later. */
CertificatesService.build_wildcard_map();
// meta information
$scope.meta = {
// sort settings
sortReverse: false,
sortBy: "label",
sortDirection: "asc",
// pager settings
maxPages: 5,
totalItems: $scope.domains.length,
currentPage: 1,
pageSize: 20,
pageSizes: [20, 50, 100, 250],
start: 0,
limit: 20,
filterValue: "",
productFilterValue: "",
};
$scope.cart_price_strings = null;
var defaultSearchValues = {
"certTerms": {
"1_year": true,
"2_year": false,
"3_year": false,
},
};
$scope.searchFilterOptions = new SearchSettingsModel(CertificatesService.get_domain_search_options());
$scope.productSearchFilterOptions = new SearchSettingsModel(CertificatesService.get_product_search_options(), defaultSearchValues);
$scope.displayProxySubdomains = true;
$scope.filter_domains = function(domains) {
var filteredDomains = domains;
if ($scope.meta.filterValue) {
filteredDomains = $filter("filter")(filteredDomains, $scope.meta.filterValue);
}
filteredDomains = $scope.searchFilterOptions.filter(filteredDomains);
return filteredDomains;
};
$scope.filter_products = function(products) {
var filteredProducts = products;
var selectedDomains = $scope.selected_domains;
var wildcardDomains = $filter("filter")(selectedDomains, {
is_wildcard: true,
});
filteredProducts = $filter("filter")(filteredProducts, function(product) {
if (!product.wildcard_price && wildcardDomains.length) {
return;
} else if (!product.price && selectedDomains.length - wildcardDomains.length > 0) {
return;
}
return true;
});
if ($scope.meta.productFilterValue) {
filteredProducts = $filter("filter")(filteredProducts, $scope.meta.productFilterValue);
}
filteredProducts = $scope.productSearchFilterOptions.filter(filteredProducts);
return filteredProducts;
};
$scope.toggle_values = function(items, att, value) {
angular.forEach(items, function(item) {
item[att] = value;
});
};
$scope.get_showing_text = function() {
return LOCALE.maketext("[output,strong,Showing] [numf,_1] - [numf,_2] of [quant,_3,domain,domains]", $scope.meta.start, $scope.meta.limit, $scope.meta.totalItems);
};
// We used to call it “resolution” in the UI, but
// that’s confusing because it could sound like a
// DNS “resolution”. So now we say “validate”, which
// is consistent with the “DCV” initialism.
$scope.get_resolution_text = function(domain) {
if (domain.resolving) {
return LOCALE.maketext("Running Domain Control Validation …");
} else if (domain.resolved === 0) {
return LOCALE.maketext("Domain Control Validation failed: [_1]", domain.resolution_failure_reason);
} else if (domain.resolved === 1) {
if (domain.dcvPassed.dns) {
var whyNotHttp;
if (domain.redirects_count) {
whyNotHttp = LOCALE.maketext("[asis,HTTP]-based Domain Control Validation required [quant,_1,redirection,redirections].", domain.redirects_count);
} else {
whyNotHttp = LOCALE.maketext("[asis,HTTP]-based Domain Control Validation failed.");
}
return LOCALE.maketext("Validated via [asis,DNS]-based Domain Control Validation.") + " (" + whyNotHttp + ")";
} else {
return LOCALE.maketext("Validated via [asis,HTTP]-based Domain Control Validation.");
}
}
};
$scope.get_domain_badge_color = function(domain) {
if (domain.resolving) {
return "info";
} else if (domain.resolved === 1) {
return "success";
} else if (domain.resolved === 0) {
return "danger";
}
return "default";
};
$scope.get_cert_status_color = function(domain) {
var cert = $scope.get_domain_certificate(domain.domain).certificate;
if (!cert) {
return;
}
if (domain.certificate_status === "active" && !cert.is_self_signed) {
return "label-success";
}
if (domain.certificate_status === "expired") {
return "label-danger";
}
if (cert.is_self_signed || domain.certificate_status === "expiring_soon") {
return "label-warning";
}
};
$scope.select_domain = function(selectedDomain) {
if (selectedDomain.selected && selectedDomain.is_wildcard) {
// select domains covered by this wildcard
var coveredDomains = $filter("filter")($scope.domains, function(domain) {
return CertificatesService.compare_wildcard_domain(selectedDomain, domain.domain);
});
$scope.toggle_values(coveredDomains, "selected", true);
}
$scope.update_selected_domains();
$scope.check_selected_domains();
};
$scope.check_selected_domains = function() {
if ($scope.resolution_timeout) {
$timeout.cancel($scope.resolution_timeout);
}
$scope.resolution_timeout = $timeout(function(domains) {
$scope.ensure_dns(domains);
}, 850, true, CertificatesService.get_all_selected_domains()); // JNK: Lowered wait time since I keep missing it when testing
};
$scope.update_selected_domains = function() {
$scope.selected_domains = $filter("filter")($scope.domains, function(domain) {
if (!domain.selected) {
return false;
}
/* while technically selected, not included in the cert */
if ($scope.domain_covered_by_wildcard(domain)) {
return false;
}
return true;
});
$scope.current_certificate.set_domains($scope.selected_domains);
$scope.current_certificate.set_virtual_hosts($scope.get_selected_vhosts());
$scope.update_baseless_wildcard_domains();
$scope.fetch_products();
$scope.update_cart_strings();
};
$scope.get_domain_certificate = function(domain) {
return CertificatesService.get_domain_certificate(domain);
};
$scope.get_domain_certificate_type = function(domain) {
if (domain.certificate_type) {
return domain.certificate_type;
}
domain.certificate_type = "unsecured";
var ihost = $scope.get_domain_certificate(domain.domain);
if (ihost && ihost.certificate) {
if (!ihost.certificate.is_self_signed && ihost.certificate.validation_type) {
domain.certificate_type = ihost.certificate.validation_type;
}
}
return $scope.get_domain_certificate_type(domain);
};
$scope.get_domain_cert_msg = function(domain) {
if (domain.certificate_status_msg) {
return domain.certificate_status_msg;
}
var ihost = $scope.get_domain_certificate(domain.domain);
var name;
if (ihost && ihost.certificate) {
var cert = ihost.certificate;
if (cert.validation_type === "dv") {
name = LOCALE.maketext("A [output,abbr,DV,Domain Validated] certificate already secures this domain.");
} else if (cert.validation_type === "ov") {
name = LOCALE.maketext("An [output,abbr,OV,Organization Validated] certificate already secures this domain.");
} else if (cert.validation_type === "ev") {
name = LOCALE.maketext("An [output,abbr,EV,Extended Validation] certificate already secures this domain.");
} else if (cert.is_self_signed) {
name = LOCALE.maketext("A self-signed certificate already secures this domain.");
}
}
if (!name) {
name = LOCALE.maketext("A certificate of unknown type already secures this domain.");
}
if (domain.certificate_status === "expired") {
name += " " + LOCALE.maketext("The certificate has expired.");
} else if (domain.certificate_status === "expiring_soon") {
name += " " + LOCALE.maketext("It will expire soon.");
}
domain.certificate_status_msg = name;
return $scope.get_domain_cert_msg(domain);
};
$scope.pending_certificate = function(domain) {
var result = $scope.pendingCertificateObject(domain);
return result ? result.order_item_id : false;
};
$scope.pendingCertificateObject = function(domain) {
var result = false;
angular.forEach($scope.pending_certificates, function(pcert) {
angular.forEach(pcert.vhost_names, function(vhostName) {
if (vhostName === domain.virtual_host) {
result = pcert;
}
});
});
return result;
};
$scope.view_pending_certificate = function(domain) {
var orderItemID = $scope.pending_certificate(domain);
$scope.go_to_pending(orderItemID);
};
$scope.go_to_pending = function(orderItemID) {
if (orderItemID) {
$location.path("/pending-certificates/").search("orderItemID", orderItemID);
} else {
$location.path("/pending-certificates");
}
};
$scope.get_domain_lock_classes = function(domain) {
var ihost = $scope.get_domain_certificate(domain.domain);
if (ihost && ihost.certificate) {
if (ihost.certificate.is_self_signed || domain.certificate_status === "expired") {
return "grey-padlock";
} else {
return "green-padlock";
}
}
};
$scope.build_csr_url = function(domain) {
var ihost = $scope.get_virtual_host_certificate(domain);
if (ihost && ihost.certificate) {
var url = "";
url += "../../ssl/install.html?id=";
url += encodeURIComponent(ihost.certificate.id);
return url;
}
};
$scope.get_domain_msg_state = function(domain) {
var certObj = $scope.pendingCertificateObject(domain);
if (certObj) {
var domainExistsOnCert = CertificatesService.doesDomainMatchOneOf(domain.domain, certObj.domains);
if (domainExistsOnCert) {
return "cert-pending";
} else {
return "cert-pending-other-domains";
}
} else if ($scope.domain_covered_by_wildcard(domain)) {
return "covered-by-wildcard";
} else {
if ($scope.get_domain_certificate(domain.domain) && domain.resolved === -1 && !domain.resolving) {
return "ssl-exists";
} else {
return "default";
}
}
};
$scope.get_virtual_host_by_display_name = function(vhostName) {
var vhostIndex = CertificatesService.get_virtual_host_by_display_name(vhostName);
return $scope.virtual_hosts[vhostIndex];
};
$scope.get_virtual_host_certificate = function(domain) {
return $scope.get_domain_certificate(domain.domain);
};
$scope.ensure_dns = function(domains) {
domains = $filter("filter")(domains, {
selected: true,
resolved: -1,
});
if (!domains.length) {
return false;
}
var providerName = $scope.get_current_or_default_provider();
return CertificatesService.ensure_domains_can_pass_dcv(domains, providerName);
};
$scope.get_current_or_default_provider = function() {
var productObject = $scope.get_product();
/* if it's set, use that */
if (productObject) {
var product = $scope.get_product_by_id(productObject.provider, productObject.id);
if (product) {
return product.provider_name;
}
}
return CertificatesService.get_default_provider_name();
};
$scope.get_dcv_class = function(domain) {
var classes = [];
if (domain.resolving) {
classes.push("fa-spinner");
classes.push("fa-spin");
classes.unshift("fa");
classes.push("fa-sm");
}
return classes;
};
$scope.get_other_vhost_domains = function(matchDomain) {
return $filter("filter")($scope.domains, function(domain) {
if (domain.is_wildcard) {
return false;
}
if (domain.selected) {
return false;
}
if (domain.resolved === 0) {
return false;
}
if (domain.domain === matchDomain.domain) {
return false;
}
if (domain.virtual_host !== matchDomain.virtual_host) {
return false;
}
return true;
});
};
$scope.get_selected_vhosts = function() {
var coveredDomains = $scope.get_covered_domains();
var coveredSelectedDomains = _.filter( coveredDomains, { selected: true } );
return _.uniq(coveredSelectedDomains.map(function(domain) {
return domain.virtual_host;
}));
};
function _domainIsOnPartialVhost(domain) {
if (domain.selected) {
return false;
}
if (domain.is_wildcard) {
return false;
}
if (domain.resolved === 0) {
return false;
}
if ($scope.domain_covered_by_wildcard(domain)) {
return false;
}
/* isn't a selected vhost */
if ($scope.get_selected_vhosts().indexOf(domain.virtual_host) === -1) {
return false;
}
return true;
}
$scope.has_partial_vhosts = function() {
return $scope.domains.some(_domainIsOnPartialVhost);
};
$scope.get_partial_vhost_domains = function() {
return $scope.domains.filter(_domainIsOnPartialVhost);
};
$scope.get_undercovered_vhost_message = function(otherDomains) {
var flatDomains = otherDomains.map(function(domain) {
return domain.domain;
});
var msg = "";
msg += "<p>" + LOCALE.maketext("The certificate will secure some, but not all, of the domains on websites on which they exist.") + "</p>";
msg += "<p>" + LOCALE.maketext("If you choose to continue, the certificate will not secure the following [numerate,_1,domain,domains], and because a certificate will exist on their website, you may have to purchase a new certificate to secure all of these domains later. [list_and_quoted,_2]", flatDomains.length, flatDomains) + "</p>";
return msg;
};
$scope.add_partial_vhost_domains = function(domains) {
$scope.toggle_values(domains, "selected", true);
$scope.update_selected_domains();
$scope.check_selected_domains();
/* send them back to domains to watch for failures */
$scope.goto("domains");
};
$scope.get_other_domains_msg = function(domain, otherDomains) {
var flatDomains = otherDomains.map(function(domain) {
return domain.domain;
});
var msg = "";
msg += "<p>" + LOCALE.maketext("This certificate will not secure [quant,_2,other domain,other domains] on the same website as “[_1]”.", domain.domain, flatDomains.length) + "</p>";
msg += "<p>" + LOCALE.maketext("Because you cannot secure a single website with multiple certificates, in order to secure any unselected [numerate,_1,domain,domains] in the future, you would need to purchase a new certificate to secure all of these domains.", flatDomains.length) + "</p>";
msg += "<p>" + LOCALE.maketext("Would you like to secure the following additional [numerate,_2,domain,domains] with this certificate? [list_and_quoted,_1]", flatDomains, flatDomains.length) + "</p>";
return msg;
};
$scope.get_covered_domains = function() {
return $filter("filter")($scope.domains, function(domain) {
if (domain.selected || $scope.domain_covered_by_wildcard(domain)) {
return true;
}
});
};
$scope.get_other_wildcard_domains = function(matchDomain) {
if (!matchDomain.is_wildcard) {
return false;
}
return $filter("filter")($scope.domains, function(domain) {
if (domain.selected) {
return false;
}
if (domain.is_wildcard) {
return false;
}
if (domain.domain === matchDomain.domain) {
return false;
}
if (CertificatesService.compare_wildcard_domain(matchDomain, domain.domain) === false) {
return false;
}
return true;
});
};
function _isFailedDCVDomain(domain) {
return domain.resolved !== 1;
}
$scope.has_failed_dcv_domains = function() {
return $scope.selected_domains.some(_isFailedDCVDomain);
};
$scope.get_failed_dcv_domains = function() {
return $scope.selected_domains.filter(_isFailedDCVDomain);
};
$scope.get_failed_dcv_message = function(failedDCVDomains) {
var flatDomains = failedDCVDomains.map(function(domain) {
return domain.domain;
});
var msg = "";
msg += "<p>" + LOCALE.maketext("The following [numerate,_2,domain,domains] failed [numerate,_2,its,their] [output,abbr,DCV,Domain Control Validation] check: [list_and_quoted,_1]", flatDomains, flatDomains.length) + "</p>";
return msg;
};
$scope.clear_failed_domains = function(domains) {
$scope.toggle_values(domains, "selected", false);
$scope.update_selected_domains();
if ($scope.selected_domains.length === 0) {
$scope.goto("domains");
} else if ($scope.check_unresolved_issues() === false) {
$scope.goto("review");
}
};
$scope.domain_covered_by_wildcard = function(domain) {
if (domain.is_wildcard) {
return false;
}
var coverageDomain = CertificatesService.domain_covered_by_wildcard(domain.domain);
if (coverageDomain && coverageDomain.selected) {
return coverageDomain;
}
return false;
};
$scope.get_covered_by_wildcard_message = function(domain) {
var wildcard = $scope.domain_covered_by_wildcard(domain);
if (!wildcard) {
return "";
}
return LOCALE.maketext("The certificate for wildcard domain “[_1]” will secure this domain.", wildcard.domain);
};
$scope.is_domain_disabled = function(domain) {
if ($scope.pending_certificate(domain)) {
return true;
}
if ($scope.domain_covered_by_wildcard(domain)) {
return true;
}
};
$scope.get_currency_string = function(num, priceUnit) {
num += 0.001;
var str = LOCALE.numf(num);
str = "$" + str.substring(0, str.length - 1);
if (priceUnit) {
str += " " + priceUnit;
}
return str;
};
$scope.get_products = function() {
return CertificatesService.get_products();
};
$scope.cant_checkout_msg = function() {
alertService.add({
type: "warn",
message: LOCALE.maketext("You cannot check out until you resolve all errors (in red)."),
closeable: true,
replace: false,
group: "tlsWizard",
});
};
$scope.cant_products_msg = function() {
alertService.add({
type: "warn",
message: LOCALE.maketext("You need to select at least one domain before you can select a product."),
closeable: true,
replace: false,
group: "tlsWizard",
});
};
$scope.clear_cloud_domain = function(domain) {
domain.selected = false;
$scope.update_selected_domains();
/* if no domains remain selected but we are on teh review panel, send them back to the domains panel */
if ($scope.selected_domains.length === 0 && $scope.panels.review) {
$scope.goto("domains");
}
};
$scope.goto = function(panel) {
/* can't go to review if things are unresolved */
if (panel === "review" && $scope.blocker_issues_exist()) {
$scope.cant_checkout_msg();
return;
}
/* can't go to products if things are no domains */
if (panel === "products" && !$scope.selected_domains.length) {
$scope.cant_products_msg();
panel = "domains";
}
angular.forEach($scope.panels, function(value, key) {
$scope.panels[key] = false;
});
$scope.panels[panel] = true;
};
$scope.get_product_name = function(product) {
if (!product) {
return "";
}
var productObject = $scope.get_product_by_id(product.provider, product.id);
if (!productObject) {
return "";
}
return productObject.display_name;
};
$scope.get_product_by_id = function(providerName, productID) {
return CertificatesService.get_product_by_id(providerName, productID);
};
$scope.check_product_match = function(productA) {
var productB = $scope.get_product();
if (!productA || !productB) {
return false;
}
if (productA.id === productB.id && productA.provider === productB.provider) {
return true;
}
};
$scope.get_per_price_string = function(product) {
var prices = [];
if (product.price) {
prices[prices.length] = LOCALE.maketext("[_1] per domain", $scope.get_currency_string(product.price, product.price_unit));
}
if (product.wildcard_price) {
prices[prices.length] = LOCALE.maketext("[_1] per wildcard domain", $scope.get_currency_string(product.wildcard_price));
}
return prices.join(", ");
};
$scope.get_product_estimate_string = function(product) {
var priceString = $scope.get_currency_string($scope.calculate_product_price(product));
return "(" + LOCALE.maketext("[_1] total", priceString, product.price_unit) + ")";
};
var _calculateProductPrice = function(product, nonwildcards, wildcards) {
// No product, possible during transition to other page.
if (!product) {
return;
}
var totalPrice = 0;
if (wildcards.length && product.wildcard_price) {
totalPrice += wildcards.length * product.wildcard_price;
}
if (nonwildcards.length && product.price) {
totalPrice += nonwildcards.length * product.price;
}
// product includes main domain free
if (product.wildcard_parent_domain_included) {
// adjust for main domains that are covered by wildcard domains
// subtract the price of the main domain for each wildcard domain
var nonWildcardKeys = {};
nonwildcards.forEach(function(domain) {
nonWildcardKeys[domain.domain] = domain;
});
wildcards.forEach(function(domain) {
// for each wildcard domain that is selected
// and has its non-wildcard equivelent selected
// reduce the price
// “stripped_domain” doesn’t strip
// the wildcard from a domain that’s actually
// installed in Apache. Because there might be
// things that depend on that, we’ll leave that
// “faux”-strip logic in place and, for here,
// manually ensure that we’re matching against
// the wildcard’s parent domain.
var trulyStripped = domain.domain.replace(/^\*\./, "");
if (nonWildcardKeys[trulyStripped]) {
totalPrice -= product.price;
}
});
}
return totalPrice;
};
$scope.calculate_product_price = function(product) {
var selectedDomains = $scope.selected_domains;
var wildcardDomains = $filter("filter")(selectedDomains, {
is_wildcard: true,
});
var nonWildcardDomains = $filter("filter")(selectedDomains, {
is_wildcard: false,
});
return _calculateProductPrice(product, nonWildcardDomains, wildcardDomains);
};
$scope.set_product = function(product) {
$scope.current_certificate.set_product(product);
};
$scope.get_product = function() {
return $scope.current_certificate.get_product();
};
$scope.get_product_prices = function() {
var prices = [];
var selectedDomains = $scope.selected_domains;
var wildcardDomains = selectedDomains.filter(function(domain) {
if (domain.is_wildcard) {
return true;
}
return false;
});
var nonWildcardDomains = selectedDomains.filter(function(domain) {
if (!domain.is_wildcard) {
return true;
}
return false;
});
$scope.filteredProductList.forEach(function(product) {
var price = _calculateProductPrice(product, nonWildcardDomains, wildcardDomains);
prices.push(price);
});
return _.sortBy(prices);
};
$scope.get_min_price = function() {
return $scope.get_product_prices().shift();
};
$scope.get_max_price = function() {
return $scope.get_product_prices().pop();
};
$scope.check_unresolved_issues = function() {
if ($scope.has_partial_vhosts()) {
return true;
}
if ($scope.has_failed_dcv_domains()) {
return true;
}
return false;
};
$scope.update_baseless_wildcard_domains = function() {
$scope.missing_base_domains = [];
$scope.selected_domains.forEach(function(domain) {
if (domain.is_wildcard === false) {
return;
}
var mainDomain = CertificatesService.get_domain_by_domain(domain.stripped_domain);
if (!mainDomain.selected) {
$scope.missing_base_domains.push(mainDomain);
}
});
if ($scope.missing_base_domains.length) {
var flatDomains = $scope.missing_base_domains.map(function(domain) {
return domain.domain;
});
alertService.add({
type: "info",
message: LOCALE.maketext("Because wildcard certificates require their parent domains, the system added the following [numerate,_1,domain,domains] for you: [list_and_quoted,_2]", flatDomains.length, flatDomains),
closeable: true,
replace: false,
group: "tlsWizard",
});
$scope.select_baseless_wildcard_domains($scope.missing_base_domains);
}
};
$scope.select_baseless_wildcard_domains = function(missingDomains) {
$scope.toggle_values(missingDomains, "selected", true);
$scope.update_selected_domains();
};
$scope.get_resolve_panel_color = function() {
var color = "warning";
/* keep these in order of danger level! */
if ($scope.has_partial_vhosts()) {
// stay warning
color = "warning";
}
if ($scope.has_failed_dcv_domains()) {
color = "danger";
}
return color;
};
$scope.blocker_issues_exist = function() {
return $scope.get_resolve_panel_color() === "danger";
};
$scope.get_cart_price = function() {
var price = 0;
if (!$scope.get_product()) {
return price;
}
var product = $scope.get_product_by_id($scope.get_product().provider, $scope.get_product().id);
var totalPrice = $scope.calculate_product_price(product);
$scope.current_certificate.set_price(totalPrice);
return totalPrice;
};
$scope.get_cart_strings = function() {
return $scope.cart_price_strings;
};
$scope.update_cart_strings = function() {
var product = $scope.get_product();
var productPrices = $scope.get_product_prices();
var selectedDomains = $scope.selected_domains;
var cartPrice = {
min: 0,
max: 0,
};
if (product && selectedDomains.length) {
cartPrice.min = $scope.get_currency_string($scope.get_cart_price(), "USD");
} else if (selectedDomains.length) {
cartPrice.min = $scope.get_currency_string($scope.get_min_price(), "USD");
if (productPrices.length > 1) {
cartPrice.max = $scope.get_currency_string($scope.get_max_price(), "USD");
}
} else {
// If no other value, ensure that it is not empty so it does not jump when a domain is selected
$scope.cart_price_strings = false;
}
$scope.cart_price_strings = cartPrice;
};
$scope.get_cart_items = function() {
$scope.cart_items = [$scope.current_certificate];
return $scope.cart_items;
};
$scope.purchase = function() {
/* can't go to review if things are unresolved */
if ($scope.blocker_issues_exist()) {
$scope.cant_checkout_msg();
return;
}
$scope.current_certificate.set_identity_verification($scope.identity_verification);
CertificatesService.add_new_certificate($scope.current_certificate);
var success = CertificatesService.save({
simple_identity_verification: $scope.identity_verification,
});
if (!success) {
alertService.add({
type: "danger",
message: LOCALE.maketext("Failed to save information to browser cache."),
group: "tlsWizard",
});
} else {
$location.path("/purchase");
}
};
$scope.selectFilterType = function(type) {
if (type === "all") {
$scope.meta.quickFilterValue = "";
} else {
$scope.meta.quickFilterValue = type;
}
$scope.fetch();
};
$scope.go_to_advanced = function() {
CertificatesService.hard_reset();
LocationService.go_to_advanced_create_route().search("");
};
$scope.get_wildcard_base_domain_msg = function() {
return LOCALE.maketext("This certificate includes the parent domain in the price of the certificate.");
};
// update table
$scope.fetch = function() {
var filteredList = [];
// filter list based on search text
filteredList = $scope.filter_domains($scope.domains);
angular.forEach(filteredList, function(domain) {
$scope.get_domain_certificate_type(domain);
});
if ($scope.meta.filterValue !== "") {
/* sort by percentage match if a filterValue exists */
filteredList = filteredList.sort(function(a, b) {
if (a.domain.length === b.domain.length) {
return 0;
}
var aPer = $scope.meta.filterValue.length / a.domain.length;
var bPer = $scope.meta.filterValue.length / b.domain.length;
return aPer < bPer ? -1 : 1;
});
} else {
filteredList = filteredList.sort(function(a, b) {
// Sort <*.foo.com> immediately after <foo.com>.
if (a.domain === "*." + b.domain) {
return -1;
}
if ("*." + a.domain === b.domain) {
return 1;
}
return b.stripped_domain.localeCompare(a.stripped_domain);
});
}
// sort the filtered list
if ($scope.meta.sortDirection !== "" && $scope.meta.sortBy !== "") {
filteredList = $filter("orderBy")(filteredList, $scope.meta.sortBy, $scope.meta.sortDirection === "asc" ? true : false);
}
// update the total items after search
$scope.meta.totalItems = filteredList.length;
// filter list based on page size and pagination
if ($scope.meta.totalItems > _.min($scope.meta.pageSizes)) {
var start = ($scope.meta.currentPage - 1) * $scope.meta.pageSize;
var limit = $scope.meta.pageSize;
filteredList = $filter("limitTo")($filter("startFrom")(filteredList, start), limit);
$scope.showPager = true;
// table statistics
$scope.meta.start = start + 1;
$scope.meta.limit = start + filteredList.length;
} else {
// hide pager and pagination
$scope.showPager = false;
if (filteredList.length === 0) {
$scope.meta.start = 0;
} else {
// table statistics
$scope.meta.start = 1;
}
$scope.meta.limit = filteredList.length;
}
var countNonSelected = 0;
// Add rowSelected attribute to each item in the list to track selections.
filteredList.forEach(function(item) {
// Select the rows if they were previously selected on this page.
if ($scope.selected_domains.indexOf(item.id) !== -1) {
item.rowSelected = true;
} else {
item.rowSelected = false;
countNonSelected++;
}
});
$scope.filteredList = filteredList;
// Clear the 'Select All' checkbox if at least one row is not selected.
$scope.allRowsSelected = (filteredList.length > 0) && (countNonSelected === 0);
return filteredList;
};
$scope.fetch_products = function() {
var products = this.get_products();
$scope.filteredProductList = $scope.filter_products(products);
return $scope.filteredProductList;
};
$scope.init = function() {
// if routeParam domains set it
if ($routeParams["domain"]) {
var preselectDomains = $routeParams["domain"];
if (_.isString(preselectDomains)) {
preselectDomains = [preselectDomains];
}
angular.forEach(preselectDomains, function(domain) {
var domainObject = CertificatesService.get_domain_by_domain(domain);
if (domainObject) {
domainObject.selected = true;
}
});
}
var productSearchOptions = CertificatesService.get_product_search_options();
var defaultSearchValues = {
"certTerms": {
"1_year": true,
"2_year": false,
"3_year": false,
},
};
if ($routeParams["certificate_type"]) {
var preselectCertificateTypes = $routeParams["certificate_type"];
if (_.isString(preselectCertificateTypes)) {
preselectCertificateTypes = [preselectCertificateTypes];
}
var validationType = {};
// Assume that if these aren't set in any products (since they are optional, after all) that it is 'all' -- CPANEL-12128.
if (typeof productSearchOptions.validationType === "undefined") {
validationType["all"] = true;
} else {
angular.forEach(productSearchOptions.validationType.options, function(option) {
validationType[option.value] = preselectCertificateTypes.indexOf(option.value) !== -1;
});
}
defaultSearchValues["validationType"] = validationType;
}
$scope.searchFilterOptions = new SearchSettingsModel(CertificatesService.get_domain_search_options());
$scope.productSearchFilterOptions = new SearchSettingsModel(CertificatesService.get_product_search_options(), defaultSearchValues);
$scope.fetch();
$scope.fetch_products();
$scope.current_certificate = new Certificate();
$scope.update_selected_domains();
if ($scope.selected_domains.length) {
$scope.check_selected_domains();
$scope.goto("products");
} else {
$scope.goto("domains");
}
};
// first page load
$scope.init();
},
]);
});
/*
* base/frontend/jupiter/security/tls_wizard/views/CheckoutController.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(
'app/views/CheckoutController',[
"lodash",
"angular",
"jquery",
"cjt/util/locale",
"cjt/util/query",
"app/views/Certificate",
"cjt/modules",
"app/services/CertificatesService",
"app/services/LocationService",
"cjt/directives/spinnerDirective",
"uiBootstrap",
],
function(_, angular, $, LOCALE, QUERY) {
"use strict";
var app = angular.module("App");
function CheckoutController(
$scope,
$controller,
$location,
$filter,
$routeParams,
$window,
$timeout,
CertificatesService,
spinnerAPI,
$q,
$modal,
$log,
Certificate,
LocationService,
alertService) {
var steps = {
"cPStore": ["login", "send_cart_items", "checkout", "payment_callback", "checkout_complete"],
"default": ["login", "send_cart_items", "checkout", "payment_callback", "checkout_complete"],
};
$scope.pending_certificates = CertificatesService.get_pending_certificates();
$scope.LOCALE = LOCALE;
$scope.purchase_steps = [];
$scope.current_step = -1;
$scope.start_step = null;
$scope.providers = [];
$scope.certificates_count = 0;
$scope.steps = [];
$scope.html_escape = _.escape.bind(_);
$scope.get_step_classes = function(provider, step) {
var steps = $scope.get_steps(provider.name).length;
var stepIndex = $scope.get_step_index(provider.name, step);
var cols = Math.floor(12 / steps);
var classes = ["col-xs-12", "col-sm-12", "col-md-" + cols, "col-lg-" + cols, "checkout-step"];
if ($scope.current_step_index === stepIndex) {
classes.push("checkout-step-current");
if ("checkout_complete" === step) {
classes.push("checkout-step-completed");
}
} else if ($scope.current_step_index > stepIndex) {
classes.push("checkout-step-completed");
}
return classes;
};
$scope.cert_count_title = function() {
return LOCALE.maketext("Purchasing [quant,_1,certificate,certificates] …", $scope.certificates_count);
};
$scope.get_purchases_title = function(provider) {
return LOCALE.maketext("Completing [numerate,_2,purchase,purchases] for the “[_1]” provider …", $scope.html_escape(provider.display_name), provider.certificates.length);
};
$scope.sending_items_msg = function() {
return LOCALE.maketext("Sending your [numerate,_1,item,items] to the store cart …", $scope.certificates_count);
};
$scope.starting_polling_msg = function() {
return LOCALE.maketext("Starting background polling for the [numerate,_1,certificate,certificates]. The system will download and install the [numerate,_1,certificate,certificates] when available.", $scope.certificates_count);
};
$scope.get_provider_by_name = function(name) {
for (var i = 0; i < $scope.providers.length; i++) {
if ($scope.providers[i].name === name) {
return $scope.providers[i];
}
}
};
$scope.get_steps = function(providerName) {
if (steps[providerName]) {
return steps[providerName];
}
return steps["default"];
};
$scope.get_current_step = function() {
return $scope.steps[$scope.current_step_index];
};
$scope.get_step_index = function(providerName, step) {
for (var i = 0; i < $scope.steps.length; i++) {
if ($scope.steps[i].provider === providerName && $scope.steps[i].step === step) {
return i;
}
}
return 0;
};
$scope.get_step_url = function(step) {
return "/" + encodeURIComponent(step.provider) + "/" + encodeURIComponent(step.step);
};
$scope.get_next_step = function() {
if ($scope.current_step_index + 1 < $scope.steps.length) {
return $scope.steps[$scope.current_step_index + 1];
}
};
$scope.get_param = function(key) {
return QUERY.parse_query_string(location.search.replace(/^\?/, ""))[key] || $routeParams[key];
};
$scope.require_params = function(keys) {
var badKeys = [];
var tooManyKeys = [];
angular.forEach(keys, function(key) {
var value = $scope.get_param(key);
if (!value) {
badKeys.push(key);
} else if (value instanceof Array) {
tooManyKeys.push(key);
}
});
if (badKeys.length) {
alertService.add({
type: "danger",
message: LOCALE.maketext("The following [numerate,_1,parameter is,parameters are] required but [numerate,_1,does,do] not appear in the [asis,URL]: [list_and_quoted,_2]", badKeys.length, badKeys),
group: "tlsWizard",
});
}
if (tooManyKeys.length) {
alertService.add({
type: "danger",
message: LOCALE.maketext("The following [numerate,_1,parameter appears,parameters appear] more than once in the [asis,URL]: [list_and_quoted,_2]", tooManyKeys.length, tooManyKeys),
group: "tlsWizard",
});
}
return badKeys.length || tooManyKeys.length ? false : true;
};
$scope.in_debug_mode = false;
$scope.get_route_url = function() {
var routeURL = "";
routeURL += $location.absUrl().replace(/tls_wizard\/.+/, "tls_wizard/#/purchase");
return routeURL;
};
function _pemToBase64(pem) {
return pem
.replace(/^\s*-\S+/, "")
.replace(/-\S+\s*$/, "")
.replace(/\s+/g, "");
}
// $q.all() will reject the “aggregate” promise with the
// exact same value as the one that failed. That’s not good
// enough; we also need to know which promise failed in addition
// to why it failed.
//
// This transforms all failure callback payloads into 2-member
// arrays: [ <promise_index>, <payload> ]
//
// So, if you do:
// _qAllWithErrIndex( [ prA, prB, prC ] )
//
// ...and “prB” fails with the string "hahaha", the
// failure callback will receive [ 1, "hahaha" ].
//
function _qAllWithErrIndex(promisesArray) {
if (!(promisesArray instanceof Array)) {
throw "Only arrays here!";
}
return $q.all(promisesArray.map(function(p, i) {
return $q(function(resolve, reject) {
p.then(
resolve,
function(payload) {
reject([i, payload]);
}
);
});
}));
}
$scope.dismiss_modal = function() {
this.modal.dismiss();
};
$scope.go_to_purchase_page = LocationService.go_to_last_create_route;
$scope.go_to_login = function() {
this.go_step(this.get_current_step().provider, "login");
};
$scope.do_current_step = function() {
var step = $scope.get_current_step();
if (!step) {
// something is severely wrong
// maybe they hit the back button a lot for some random reason.
// let's send them back somewhere safe.
LocationService.go_to_last_create_route();
return;
}
var nextStep = $scope.get_next_step();
var orderID = $scope.get_param("order_id");
var loginCode = $scope.get_param("code");
var order = CertificatesService.get_order_by_id(orderID);
var orderStatus = $scope.get_param("order_status");
var provider = $scope.get_provider_by_name(step.provider);
var accessToken = $scope.get_param("access_token");
var returnURL;
if (step.step === "login") {
returnURL = $scope.get_route_url() + $scope.get_step_url(step);
if (order) {
returnURL += "?order_id=" + order.order_id;
}
if (loginCode) {
/* Back from Login, Verify It */
CertificatesService.verify_login_token(step.provider, loginCode, returnURL).then(function(result) {
if (order) {
/* there's an order, so don't create another one */
$scope.go_step(step.provider, "checkout", {
order_id: order.order_id,
access_token: result.data.access_token,
});
} else {
/* no order, so create one */
$scope.go_step(step.provider, "send_cart_items", {
access_token: result.data.access_token,
});
}
}, function(errorHTML) {
$scope.return_to_wizard();
alertService.add({
type: "danger",
message: LOCALE.maketext("The system encountered an error as it attempted to verify the login token: [_1]", errorHTML) + " " + LOCALE.maketext("You will now return to the beginning of the wizard."),
closeable: true,
replace: false,
group: "tlsWizard",
});
});
} else {
/* There's no login code */
CertificatesService.get_store_login_url(step.provider, returnURL).then(function(result) {
$window.location.href = result.data;
}, function(errorHTML) {
$scope.return_to_wizard();
alertService.add({
type: "danger",
message: LOCALE.maketext("The system encountered an error as it attempted to get the store login [output,abbr,URL,Uniform Resource Location]: [_1]", errorHTML) + " " + LOCALE.maketext("You will now return to the beginning of the wizard."),
closeable: true,
replace: false,
group: "tlsWizard",
});
});
}
} else if (step.step === "send_cart_items") {
/* create order / build cart */
if (!$scope.require_params(["access_token"])) {
return;
}
returnURL = $scope.get_route_url() + $scope.get_step_url(nextStep);
return CertificatesService.request_certificates(step.provider, accessToken, provider.certificates).then(function(result) {
var order = result.data;
order.order_id = order.order_id.toString();
CertificatesService.add_order(order);
CertificatesService.save();
$scope.go_step(step.provider, "checkout", {
order_id: order.order_id,
access_token: accessToken,
});
}, function(errorHTML) {
$scope.return_to_wizard();
alertService.add({
type: "danger",
message: LOCALE.maketext("The system encountered an error as it attempted to request the [asis,SSL] [numerate,_2,certificate,certificates]: [_1]", errorHTML, $scope.get_provider_by_name(step.provider).certificates.length) + " " + LOCALE.maketext("You will now return to the beginning of the wizard."),
closeable: true,
replace: false,
group: "tlsWizard",
});
});
} else if (step.step === "checkout") {
if (!$scope.require_params(["order_id"])) {
return;
}
returnURL = $scope.get_route_url() + $scope.get_step_url(step);
if (orderStatus) {
/* are we back from checking out? */
$scope.go_step(step.provider, "payment_callback", {
order_id: order.order_id,
order_status: orderStatus,
});
} else {
if (!$scope.require_params(["access_token"])) {
return;
}
/* no? let's update the checkout url and head to checkout */
CertificatesService.set_url_after_checkout(step.provider, accessToken, order.order_id, returnURL).then(function() {
$window.location.href = order.checkout_url;
}, function(response) { // NB: the argument is *not* the error!
var isOtherUser = response.data && response.data.error_type === "OrderNotFound";
if (isOtherUser) {
$scope.order_id = order.order_id;
$scope.provider = $scope.get_provider_by_name(step.provider);
$scope.modal = $modal.open({
template: document.getElementById("user-mismatch-modal").text,
scope: $scope,
backdrop: "static",
animation: false,
size: "sm",
});
} else {
LocationService.go_to_last_create_route();
alertService.add({
type: "danger",
message: LOCALE.maketext("The system encountered an error as it attempted to set the [asis,URL] after checkout: [_1]", _.escape(response.error)) + " " + LOCALE.maketext("You will now return to the beginning of the wizard."),
closeable: true,
replace: false,
group: "tlsWizard",
});
}
});
}
} else if (step.step === "payment_callback") {
/* post checkout processing */
CPANEL.PAGE.pending_certificates = null;
CPANEL.PAGE.installed_hosts = null;
if (orderStatus === "success") {
alertService.add({
type: "success",
message: LOCALE.maketext("You have successfully completed your certificate order (order ID “[_1]”). If you need help with this order, use the support [numerate,_2,link,links] below.", _.escape(orderID), order.certificates.length),
closeable: true,
replace: false,
autoClose: 10000,
group: "tlsWizard",
});
CertificatesService.set_confirmed_status_for_ssl_certificates(step.provider, order).then(function() {
// successful
$scope.go_step(step.provider, nextStep.step);
}, function(response) {
// This is here to accommodate cases where the certificate
// becomes available, and gets installed, prior to the
// browser’s being able to set the certificate to “confirmed”.
// When that happens, we get back a data structure that
// describes which vhosts’ pending queue entries didn’t exist;
// we then do what can be done to ensure that the cert(s)
// is/are installed where it/they should be.
//
if (response.data && response.data.error_type === "EntryDoesNotExist") {
var notFound = response.data.order_item_ids;
var msg = LOCALE.maketext("There are no pending certificates from “[_1]” with the following order item [numerate,_2,ID,IDs]: [join,~, ,_3]. The system will now verify that the [numerate,_2,certificate has,certificates have] been issued and installed.", _.escape(step.provider), notFound.length, notFound.map(_.escape.bind(_)));
alertService.add({
type: "info",
message: msg,
closeable: true,
replace: false,
autoClose: 10000,
group: "tlsWizard",
});
var certificates = provider.certificates;
notFound.forEach(function(oiid) {
// Fetch the new SSL cert.
var providerPromise = CertificatesService.get_ssl_certificate_if_available(step.provider, oiid);
// There will only be one vhost
// per certificate for now, but with
// wildcard support that could change.
certificates.forEach(function(cert) {
cert.get_virtual_hosts().forEach(function(vhostName) {
var domain = cert.get_domains().filter(function(domain) {
return domain.virtual_host === vhostName;
}).pop().domain;
var bigP = _qAllWithErrIndex([
CertificatesService.get_installed_ssl_for_domain(),
providerPromise,
]);
bigP.then(function yay(responses) {
var installedPEM = responses[0].data.certificate.text;
var installedB64;
if (installedPEM) {
installedB64 = _pemToBase64(installedPEM);
}
var providerPEM = responses[1].data.certificate_pem;
var providerB64;
if (providerPEM) {
providerB64 = _pemToBase64(providerPEM);
} else {
var apiResponse = responses[1].data;
var statusCode = apiResponse.status_code;
var statusMessage = apiResponse.status_message;
var alertMessage;
// There is ambiguity over the spelling of “canceled”.
if (/OrderCancell?ed/.test(statusCode)) {
alertMessage = LOCALE.maketext("“[_1]” indicated that the order with [asis,ID] “[_2]” has been canceled.", _.escape(step.provider), _.escape(orderID));
} else if (/OrderItemCancell?ed/.test(statusCode)) {
alertMessage = LOCALE.maketext("“[_1]” indicated that the certificate with order item [asis,ID] “[_2]” has been canceled.", _.escape(step.provider), _.escape(oiid));
} else {
alertMessage = LOCALE.maketext("“[_1]” has not issued a certificate for order item [asis,ID] “[_2]”. Contact them for further assistance.", _.escape(step.provider), _.escape(oiid));
// This yields one of:
// statusCode
// statusMessage
// statusCode: statusMessage
// … depending on the two items’
// truthiness.
var statusStr = [statusCode, statusMessage].filter( function(s) {
return !!s;
} ).join(": ");
if (statusStr) {
alertMessage += " (" + statusStr + ")";
}
}
alertService.add({
type: "danger",
message: alertMessage,
closeable: true,
replace: false,
group: "tlsWizard",
});
// Since there’s no new certificate,
// there’s nothing more we can do.
LocationService.go_to_last_create_route();
return;
}
if (providerB64 === installedB64) {
// This is the most optimal outcome:
// we confirmed that the new cert is
// installed, as the user wanted.
alertService.add({
type: "success",
message: LOCALE.maketext("The system confirmed that the certificate for the website “[_1]” is installed.", _.escape(vhostName)),
closeable: true,
replace: false,
autoClose: 10000,
group: "tlsWizard",
});
if (installedB64) {
alertService.add({
type: "info",
message: LOCALE.maketext("“[_1]” has an [asis,SSL] certificate installed, but it is not the certificate that you just ordered (order item [asis,ID] “[_2]”). The system will now install this certificate.", _.escape(vhostName), _.escape(oiid)),
closeable: true,
replace: false,
autoClose: 10000,
group: "tlsWizard",
});
} else {
var noCertMessage;
noCertMessage = LOCALE.maketext("You do not have an [asis,SSL] certificate installed for the website “[_1]”.", _.escape(vhostName));
noCertMessage += LOCALE.maketext("The system will now install the new certificate.");
alertService.add({
type: "info",
message: noCertMessage,
closeable: true,
replace: false,
autoClose: 10000,
group: "tlsWizard",
});
CertificatesService.install_certificate(providerPEM, [domain]).then(
function yay() {
alertService.add({
type: "success",
message: LOCALE.maketext("The system installed the certificate onto the website “[_1]”.", _.escape(vhostName)),
closeable: true,
replace: false,
autoClose: 10000,
group: "tlsWizard",
});
},
function nay(errorHTML) {
alertService.add({
type: "danger",
message: LOCALE.maketext("The system failed to install the certificate onto the website “[_1]” because of the following error: [_2]", _.escape(vhostName), errorHTML),
closeable: true,
replace: false,
group: "tlsWizard",
});
}
).then(LocationService.go_to_last_create_route);
}
}
},
function onerror(idxAndResponse) {
// We’re here because we failed either
// to fetch the new cert or to query
// the current SSL state.
var promiseI = idxAndResponse[0];
var errorHTML = idxAndResponse[1];
if (promiseI === 0) {
alertService.add({
type: "danger",
message: LOCALE.maketext("The system failed to locate the installed [asis,SSL] certificate for the website “[_1]” because of the following error: [_2]", _.escape(vhostName), errorHTML),
closeable: true,
replace: false,
group: "tlsWizard",
});
} else if (promiseI === 1) {
alertService.add({
type: "danger",
message: LOCALE.maketext("The system failed to query “[_1]” for order item [asis,ID] “[_2]” ([_3]) because of the following error: [_4]", _.escape(step.provider), _.escape(oiid), _.escape(vhostName), errorHTML),
closeable: true,
replace: false,
group: "tlsWizard",
});
} else {
// should never happen
alertService.add({
type: "danger",
message: "Unknown index: " + promiseI,
closeable: true,
replace: false,
group: "tlsWizard",
});
}
LocationService.go_to_last_create_route();
}
);
});
});
});
} else {
var errorHTML = response.error;
alertService.add({
type: "danger",
message: LOCALE.maketext("The system failed to begin polling for [quant,_2,new certificate,new certificates] because of an error: [_1]", errorHTML, $scope.certificates_count) + " " + LOCALE.maketext("You will now return to the beginning of the wizard."),
closeable: true,
replace: false,
group: "tlsWizard",
});
}
});
// get info from local storage
} else {
if (orderStatus === "error") {
CertificatesService.reset();
CertificatesService.save();
$scope.return_to_wizard();
alertService.add({
type: "danger",
message: LOCALE.maketext("The system encountered an error as it attempted to complete your transaction.") + " " + LOCALE.maketext("You will now return to the beginning of the wizard."),
closeable: true,
replace: false,
group: "tlsWizard",
});
} else if (/^cancel?led$/.test(orderStatus)) { // cPStore gives two l’s
var orderItemIDs = [];
angular.forEach(order.certificates, function(cert) {
orderItemIDs.push(cert.order_item_id);
});
alertService.add({
type: "warn",
message: LOCALE.maketext("You seem to have canceled your transaction.") + " " + LOCALE.maketext("You will now return to the beginning of the wizard."),
closeable: true,
replace: false,
group: "tlsWizard",
});
$location.url($location.path()); // clear out the params so we do not get a cancel on subsequent orders
CertificatesService.cancel_pending_ssl_certificates(step.provider, orderItemIDs).then(function() {
/* need to clear old unused in page data to get a fresh load */
CertificatesService.reset();
CertificatesService.save();
$scope.return_to_wizard();
}, function(errorHTML) {
alertService.add({
type: "danger",
message: LOCALE.maketext("The system encountered an error as it attempted to cancel your transaction: [_1]", errorHTML) + " " + LOCALE.maketext("You will now return to the beginning of the wizard."),
closeable: true,
replace: false,
group: "tlsWizard",
});
});
}
return false;
}
} else if (step.step === "checkout_complete") {
// go next step or to done page
if (!nextStep) {
CertificatesService.reset();
CertificatesService.save();
// done
alertService.add({
type: "success",
message: LOCALE.maketext("The system has completed the [numerate,_1,purchase,purchases] and will begin to poll for your [numerate,_2,certificate,certificates].", $scope.providers.length, $scope.certificates_count),
closeable: true,
replace: false,
autoClose: 10000,
group: "tlsWizard",
});
$timeout($scope.go_to_pending, 1000);
}
}
};
$scope.return_to_wizard = function() {
var curURL = $location.absUrl();
// force reset for specific cases, use path redirect otherwise;
// this allows us to not clear growl notifications if we don't have to.
// could be replaced with replaceState if we ever get to IE11
if ($scope.get_param("code")) {
var newURL = curURL.replace(/([^#?]+\/).*/, "$1#" + LocationService.last_create_route());
$window.location.href = newURL;
} else {
LocationService.go_to_last_create_route();
}
};
$scope.check_step_success = function(stepIndex) {
if (stepIndex < $scope.current_step_index) {
return true;
}
};
$scope.go_step = function(provider, step, params) {
$location.path("/purchase/" + provider + "/" + step + "/");
if (params) {
$location.search(params);
}
};
$scope.get_providers = function() {
$scope.providers = [];
var steps;
$scope.purchasing_certs.forEach(function(cert) {
var product = cert.get_product();
var provider = $scope.get_provider_by_name(product.provider);
if (!provider) {
provider = {
name: product.provider,
display_name: product.provider_display_name || product.provider,
certificates: [],
};
$scope.providers.push(provider);
steps = $scope.get_steps(provider.name);
angular.forEach(steps, function(step) {
$scope.steps.push({
provider: provider.name,
step: step,
});
});
}
provider.certificates.push(cert);
$scope.certificates_count++;
});
return $scope.providers;
};
$scope.go_to_pending = function(orderItemID) {
if (orderItemID) {
$location.path("/pending-certificates/").search("orderItemID", orderItemID);
} else {
$location.path("/pending-certificates");
}
};
$scope.pending_certificate = function(virtualHost) {
var result = false;
angular.forEach($scope.pending_certificates, function(pcert) {
angular.forEach(pcert.vhost_names, function(vhostName) {
if (vhostName === virtualHost.display_name) {
result = pcert.order_item_id;
}
});
});
return result;
};
$scope.view_pending_certificate = function(virtualHost) {
var orderItemID = $scope.pending_certificate(virtualHost);
$scope.go_to_pending(orderItemID);
};
$scope.begin = function() {
// Only the “Simple” screen populates this.
$scope.purchasing_certs = CertificatesService.get_purchasing_certs();
if ($scope.purchasing_certs.length === 0) {
// The “Advanced” screen goes here, as does a resumed checkout.
CertificatesService.get_virtual_hosts().filter( function(vhost) {
if (!vhost.has_selected_domains()) {
return false;
}
var product = vhost.get_product();
if (!product) {
$log.warn("has selected, but no product?");
return false;
}
if (!CertificatesService.get_product_by_id(product.provider, product.id)) {
$log.warn("Unknown product!", product);
return false;
}
return true;
}).forEach(function(virtualHost) {
var product = virtualHost.get_product();
var cert = new Certificate();
cert.set_product(product);
cert.set_price(virtualHost.get_price());
cert.set_domains(virtualHost.get_selected_domains());
cert.set_virtual_hosts([virtualHost.display_name]);
if (product.x_identity_verification) {
var idVer = virtualHost.get_identity_verification();
// It’s ok if we don’t have the idver because
// that means we’re resuming a checkout, which
// means that the idver is already sent in, and
// the only reason we’re assembling cert/vhost/etc.
// is so that the controller can quantify the
// domains propertly in localization.
if (idVer) {
cert.set_identity_verification(idVer);
}
}
CertificatesService.add_new_certificate(cert);
});
$scope.purchasing_certs = CertificatesService.get_purchasing_certs();
}
$scope.get_providers();
$scope.current_provider_name = $routeParams.provider;
$scope.current_step_id = $routeParams.step;
$scope.current_step_index = $scope.get_step_index($scope.current_provider_name, $scope.current_step_id);
$scope.do_current_step();
$timeout(function() {
_resizedWindow();
}, 1);
};
$scope.init = function() {
CertificatesService.restore();
$scope.begin();
};
function _resizedWindow() {
$(".checkout-step-inner").each(function(index, block) {
block = $(block);
var wrapper = block.find(".content-wrapper");
var padding = (block.height() - wrapper.height()) / 2;
wrapper.css("padding-top", padding);
});
}
var window = angular.element($window);
window.bind("resize", _resizedWindow);
$scope.init();
}
app.controller("CheckoutController", [
"$scope",
"$controller",
"$location",
"$filter",
"$routeParams",
"$window",
"$timeout",
"CertificatesService",
"spinnerAPI",
"$q",
"$uibModal",
"$log",
"Certificate",
"LocationService",
"alertService",
CheckoutController]);
}
);
/*
* 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(
'app/views/PendingCertificatesController',[
"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,
]
);
}
);
/*
# base/frontend/jupiter/security/tls_wizard/directives/identityVerificationDirective.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/directives/identityVerificationDirective',[
"angular",
"cjt/util/locale",
],
function(angular, LOCALE) {
"use strict";
// Retrieve the current application
var app;
try {
app = angular.module("App"); // For runtime
} catch (e) {
app = angular.module("App", []); // Fall-back for unit testing
}
var TEMPLATE_PATH = "directives/identityVerification.phtml";
// Only these “type”s are passed through to HTML.
var ALLOWED_HTML_INPUT_TYPES = {
date: true,
number: true,
email: true,
tel: true,
};
// Only these return false from use_html_input().
var NON_HTML_INPUT_TYPES = {
country_code: true,
choose_one: true,
};
var str = {
required: LOCALE.maketext("Required"),
preamble: LOCALE.maketext("To ensure quick service, fill out the form below as completely as possible."),
};
function use_html_input(item) {
return !( item.type in NON_HTML_INPUT_TYPES );
}
function get_html_input_type(item) {
return ( ALLOWED_HTML_INPUT_TYPES[item.type] ? item.type : "text" );
}
function get_date_format_description() {
var now = new Date();
var today_yyyy_mm_dd = now.toISOString().replace(/T.*/, "");
return LOCALE.maketext("Use the format “[_1]”. For example, today is “[_2]”.", "YYYY-MM-DD", today_yyyy_mm_dd);
}
app.directive("identityVerification", [
function() {
return {
templateUrl: TEMPLATE_PATH,
restrict: "E",
scope: {
items: "=",
models: "=",
countries: "=",
vhostName: "=",
},
link: function(scope, element) {
angular.extend(
scope,
{
SELF: scope,
STR: str,
get_html_input_type: get_html_input_type,
use_html_input: use_html_input,
get_date_format_description: get_date_format_description,
}
);
scope.items.forEach( function(item) {
item.is_optional = Boolean(+item.is_optional);
} );
},
};
},
]);
}
);
/*
* base/frontend/jupiter/security/tls_wizard/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
*/
/* global define: false, require: false */
define(
'app/index',[
"angular",
"cjt/core",
"lodash",
"cjt/modules",
"uiBootstrap",
"ngRoute",
"ngSanitize",
"ngAnimate",
],
function(angular, CJT, _) {
"use strict";
return function() {
angular.module("App", ["ui.bootstrap", "angular-growl", "cjt2.cpanel", "ngAnimate"]);
var app = require(
[
"cjt/bootstrap",
"cjt/services/alertService",
"cjt/directives/alert",
"cjt/directives/alertList",
"app/services/CertificatesService",
"app/services/LocationService",
"app/views/VirtualHostsController",
"app/views/PurchaseSimpleController",
"app/views/CheckoutController",
"app/views/PendingCertificatesController",
"app/directives/identityVerificationDirective",
"cjt/decorators/growlDecorator",
"cjt/services/cpanel/componentSettingSaverService",
],
function(BOOTSTRAP) {
var app = angular.module("App");
app.value("PAGE", CPANEL.PAGE);
// If using views
app.controller("BaseController", [
"componentSettingSaverService",
"CertificatesService",
"$rootScope",
"$scope",
"$route",
"$location",
function(
$CSSS,
CertificatesService,
$rootScope,
$scope,
$route,
$location
) {
var INTRODUCTION_BLOCK = "IntroductionBlock";
$scope.loading = false;
// Convenience functions so we can track changing views for loading purposes
$rootScope.$on("$routeChangeStart", function(event, next, current) {
if (!current || next.loadedTemplateURL !== current.loadedTemplateURL) {
$scope.loading = true;
}
});
$rootScope.$on("$routeChangeSuccess", function() {
$scope.loading = false;
});
$rootScope.$on("$routeChangeError", function() {
$scope.loading = false;
});
$scope.introduction_dismissed = function() {
$CSSS.set(INTRODUCTION_BLOCK, {
hidden: true,
});
CertificatesService.dismiss_introduction();
};
$scope.current_route_matches = function(key) {
return $location.path().match(key);
};
$scope.go = function(path) {
$location.path(path);
};
var registering = $CSSS.register(INTRODUCTION_BLOCK);
if (registering) {
registering.then(function(result) {
if (result && result.hidden) {
CertificatesService.dismiss_introduction();
}
});
}
$scope.$on("$destroy", function() {
$CSSS.unregister(INTRODUCTION_BLOCK);
});
},
]);
// viewName
app.config(["$routeProvider", "growlProvider",
function($routeProvider, growlProvider) {
growlProvider.globalPosition("top-right");
// Setup a route - copy this to add additional routes as necessary
$routeProvider.when("/create-advanced/:domain?", {
controller: "VirtualHostsController",
templateUrl: CJT.buildFullPath("security/tls_wizard/views/virtual_hosts.html.tt"),
resolve: {
installed_hosts: ["CertificatesService",
function(CertificatesService) {
return CertificatesService.fetch_installed_hosts();
},
],
products: ["CertificatesService",
function(CertificatesService) {
return CertificatesService.fetch_products();
},
],
domains: ["CertificatesService",
function(CertificatesService) {
return CertificatesService.fetch_domains();
},
],
pending_certificates: ["CertificatesService",
function(CertificatesService) {
return CertificatesService.fetch_pending_certificates();
},
],
},
});
// Setup a route - copy this to add additional routes as necessary
$routeProvider.when("/create/:domain?", {
controller: "PurchaseSimpleController",
templateUrl: CJT.buildFullPath("security/tls_wizard/views/purchase_simple.html.tt"),
resolve: {
installed_hosts: ["CertificatesService",
function(CertificatesService) {
return CertificatesService.fetch_installed_hosts();
},
],
products: ["CertificatesService",
function(CertificatesService) {
return CertificatesService.fetch_products();
},
],
domains: ["CertificatesService",
function(CertificatesService) {
return CertificatesService.fetch_domains();
},
],
pending_certificates: ["CertificatesService",
function(CertificatesService) {
return CertificatesService.fetch_pending_certificates();
},
],
},
});
// Setup a route - copy this to add additional routes as necessary
$routeProvider.when("/pending-certificates/", {
controller: "PendingCertificatesController",
reloadOnSearch: false,
templateUrl: CJT.buildFullPath("security/tls_wizard/views/pending_certificates.html.tt"),
resolve: {
installed_hosts: ["CertificatesService",
function(CertificatesService) {
return CertificatesService.fetch_installed_hosts();
},
],
pending_certificates: ["CertificatesService", "LocationService", "$location",
function(CertificatesService, LocationService, $location) {
var fetching = CertificatesService.fetch_pending_certificates();
if (_.isFunction(fetching["finally"])) {
return fetching.then(function() {
if (CertificatesService.get_pending_certificates().length) {
return true;
} else {
LocationService.go_to_last_create_route();
return false;
}
});
} else {
if (CertificatesService.get_pending_certificates().length) {
return true;
} else {
LocationService.go_to_last_create_route();
return false;
}
}
},
],
products: ["CertificatesService",
function(CertificatesService) {
return CertificatesService.fetch_products();
},
],
},
});
$routeProvider.when("/purchase/:provider?/:step?/:everythingelse?", {
controller: "CheckoutController",
templateUrl: CJT.buildFullPath("security/tls_wizard/views/checkout.html.tt"),
resolve: {
products: ["CertificatesService",
function(CertificatesService) {
return CertificatesService.fetch_products();
},
],
},
});
// default route
$routeProvider.otherwise({
"redirectTo": "/pending-certificates",
});
},
]);
// end of using views
BOOTSTRAP("#content", "App");
});
return app;
};
}
);
Back to Directory
File Manager