Viewing File: /usr/local/cpanel/share/libraries/cjt2/src/services/cpanel/SSLStatus.js

/*
 * cjt/services/cpanel/sslStatus.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
 */

/** @namespace cjt.services.cpanel.sslStatus */

define(
    [
        "angular",
        "lodash",
        "cjt/util/locale",
        "cjt/io/uapi-request",
        "cjt/services/APICatcher",
        "cjt/modules"
    ],
    function(angular, _, LOCALE, APIRequest, APICatcher ) {

        "use strict";

        var CERT_TYPES = {
            EV: "ev",
            OV: "ov",
            DV: "dv",
            SELF_SIGNED: "self-signed",
        };

        var SSLCertificate = function() {
            this.certificateErrors = [];
            this.hasErrors = false;
        };
        SSLCertificate.prototype.getIconClasses = function getIconClasses() {
            switch (this.validationType) {
                case CERT_TYPES.DV:
                case CERT_TYPES.OV:
                case CERT_TYPES.EV:
                    return "fas fa-lock";
                case CERT_TYPES.SELF_SIGNED:
                default:
                    return "fas fa-unlock-alt";
            }
        };

        SSLCertificate.prototype.getStatusColorClass = function getStatusColorClass() {
            if (this.hasErrors) {
                return "text-danger";
            }

            switch (this.validationType) {
                case CERT_TYPES.DV:
                case CERT_TYPES.OV:
                case CERT_TYPES.EV:
                    return "text-success";
                case CERT_TYPES.SELF_SIGNED:
                default:
                    return "text-danger";
            }
        };

        SSLCertificate.prototype.getTypeName = function getSSLCertTypeName(options) {
            var isExpanded = options && options.stripMarkup;
            switch (this.validationType) {
                case CERT_TYPES.DV:
                    return isExpanded ? LOCALE.maketext("Domain Validated Certificate") : LOCALE.maketext("[output,abbr,DV,Domain Validated] Certificate");
                case CERT_TYPES.EV:
                    return isExpanded ? LOCALE.maketext("Extended Validation Certificate") : LOCALE.maketext("[output,abbr,EV,Extended Validation] Certificate");
                case CERT_TYPES.OV:
                    return isExpanded ? LOCALE.maketext("Organization Validated Certificate") : LOCALE.maketext("[output,abbr,OV,Organization Validated] Certificate");
                case CERT_TYPES.SELF_SIGNED:
                    return LOCALE.maketext("Self-signed Certificate");
                default:
                    return LOCALE.maketext("No Valid Certificate");
            }
        };

        SSLCertificate.prototype.addError = function addError(errorMessage) {
            this.certificateErrors.push(errorMessage);
            this.hasErrors = true;
            return this.certificateErrors;
        };

        SSLCertificate.prototype.getErrors = function getErrors(errorMessage) {
            return this.certificateErrors;
        };

        var SSLCertFactory = function() {
            this.make = function(certificate) {
                var sslCert = new SSLCertificate();
                sslCert.isSelfSigned = certificate.is_self_signed && certificate.is_self_signed.toString() === "1";
                sslCert.validationType = sslCert.isSelfSigned ? CERT_TYPES.SELF_SIGNED : certificate.validation_type;
                return sslCert;
            };
        };

        var _certFactory = new SSLCertFactory();

        var MODULE_NAMESPACE = "cjt2.services.cpanel.sslStatus";
        var SERVICE_NAME = "sslStatusService";
        var MODULE_REQUIREMENTS = [ ];
        var SERVICE_INJECTABLES = ["$log", "$q", "APICatcher"];

        var SERVICE_FACTORY = function($log, $q, APICatcher) {

            /**
             * service wrapper for domain related functions
             *
             * @module sslStatusService
             *
             * @param  {Object} APICatcher cjt2 APICatcher service
             *
             * @example
             * $sslStatusService.getDomainSSLCertificate("foo.com")
             *
             */

            var Service = function() {};

            Service.prototype = Object.create(APICatcher);

            _.assign(Service.prototype, {

                _domainsChecked: {},

                /**
                 * Wrapper for building an apiCall
                 *
                 * @private
                 *
                 * @param {String} module module name to call
                 * @param {String} func api function name to call
                 * @param {Object} args key value pairs to pass to the api
                 * @returns {UAPIRequest} returns the api call
                 *
                 * @example _apiCall( "Tokens", "rename", { name:"OLDNAME", new_name:"NEWNAME" } )
                 */
                _apiCall: function _createApiCall(module, func, args) {
                    var apiCall = new APIRequest.Class();
                    apiCall.initialize(module, func, args);
                    return apiCall;
                },

                _getDomainCertificate: function _getDomainCertificate(domain) {

                    if (!this._domainSSLMap) {
                        return;
                    }

                    var sslDomains = Object.keys(this._domainSSLMap);

                    for (var i = 0; i < sslDomains.length; i++) {
                        var sslDomain = sslDomains[i];

                        // THe domain is covered by a direct match
                        if (domain === sslDomain) {
                            return this._domainSSLMap[sslDomain];
                        }

                        // The domain is covered by a wildcard match foo.bar.com matches *.bar.com
                        var wildcardDomain = domain.replace(/^[^.]+\./, "*.");
                        if (wildcardDomain === sslDomain) {
                            return this._domainSSLMap[sslDomain];
                        }
                    }

                    // Return an "unsecured" certificate
                    var certificate =  _certFactory.make({});
                    certificate.addError(LOCALE.maketext("You have no valid [asis,SSL] certificate configured for this domain."));
                    return certificate;
                },

                _parseInstalledHost: function _parseInstalledHost(installedHost, retainInvalidCerts) {
                    var self = this;

                    if (!self._domainSSLMap) {
                        self._domainSSLMap = {};
                    }

                    var now = new Date();
                    now = now.getTime() / 1000;

                    if (!installedHost.certificate) {
                        $log.debug("Skipping installed host for lack of a certificate.", installedHost);
                        return;
                    }

                    var certificate = _certFactory.make(installedHost.certificate);

                    if (installedHost.verify_error) {
                        $log.debug("Certificate did not pass SSL Verification.", installedHost);
                        certificate.addError(_.escape(installedHost.verify_error));
                    }

                    var notValidAfter = parseInt(installedHost.certificate.not_after, 10);

                    if (notValidAfter < now) {
                        $log.debug("The certificate has expired.", installedHost);
                        certificate.addError(LOCALE.maketext("The certificate has expired."));
                    }

                    if (!retainInvalidCerts && certificate.hasErrors) {
                        $log.debug("Skipping certificate because of errors.");
                        return;
                    }

                    var domains = installedHost.certificate.domains || [];
                    domains.forEach(function(domain) {
                        self._domainSSLMap[domain] = certificate;
                    });
                },

                /**
                 * API Wrapper call to fetch installed hosts for the current user
                 *
                 * @method getValidSSLCertificates
                 *
                 * @param  {String} user username to use to check the domain status, this is required, but only used by WHM
                 *
                 * @return {Promise<Object>} returns the promise that returns a domain object map of ssl certs
                 *
                 */
                getValidSSLCertificates: function getValidSSLCertificates() {
                    var self = this;

                    if (self._domainSSLMap) {
                        return $q.resolve(self._domainSSLMap);
                    }

                    var apiCall = self._apiCall("SSL", "installed_hosts");

                    $log.debug("getValidSSLCertificates");

                    self._domainSSLMap = {};

                    return self._promise(apiCall).then(function(result) {
                        var data = result.data || [];

                        data.forEach(self._parseInstalledHost.bind(self), self);

                        return self._domainSSLMap;
                    });

                },

                /**
                 * API Wrapper call to get the SSL Status of a single domain
                 *
                 * @method getDomainSSLCertificate
                 *
                 * @param  {String} user username to use to check the domain status
                 * @param  {String} domain domain name to get the ssl status of
                 *
                 * @return {Promise<String>} returns the promise that returns the ssl status string
                 *
                 */
                getDomainSSLCertificate: function getDomainSSLCertificate(domain, includeInvalid) {
                    var self = this;

                    $log.debug("getDomainSSLCertificate", domain);

                    if (self._domainsChecked[domain]) {
                        return self._getDomainCertificate(domain);
                    }

                    var apiCall = self._apiCall("SSL", "installed_host", {
                        domain: domain,
                        verify_certificate: 1
                    });

                    return self._promise(apiCall).then(function(result) {

                        // Mark it checked
                        self._domainsChecked[domain] = true;

                        var data = result.data || {};
                        self._parseInstalledHost.call(self, data, includeInvalid);

                        return self._getDomainCertificate(domain);
                    });
                },

                /**
                 * Wrapper for .promise method from APICatcher
                 *
                 * @param {Object} apiCall api call to pass to .promise
                 * @returns {Promise}
                 *
                 * @example $service._promise( $service._apiCall( "Tokens", "rename", { name:"OLDNAME", new_name:"NEWNAME" } ) );
                 */
                _promise: function _promise() {

                    // Because nested inheritence is annoying
                    return APICatcher.promise.apply(this, arguments);
                }

            });

            return new Service();
        };


        var module = angular.module(MODULE_NAMESPACE, MODULE_REQUIREMENTS);

        SERVICE_INJECTABLES.push(SERVICE_FACTORY);

        module.factory(SERVICE_NAME, SERVICE_INJECTABLES );

        return {
            "class": SERVICE_FACTORY,
            "serviceName": SERVICE_NAME,
            "namespace": MODULE_NAMESPACE,
            CERT_TYPES: CERT_TYPES,
            _certFactory: _certFactory,
            SSLCertificateClass: SSLCertificate
        };
    }
);
Back to Directory File Manager