collection/certificate.js

var _ = require('../util').lodash,
    Property = require('./property').Property,
    PropertyBase = require('./property-base').PropertyBase,
    Url = require('./url').Url,
    UrlMatchPatternList = require('../url-pattern/url-match-pattern-list').UrlMatchPatternList,

    STRING = 'string',
    HTTPS = 'https',

    Certificate;

/**
 * The following is the object representation accepted as param for the Certificate constructor.
 * Also the type of the object returned when {@link Property#toJSON} or {@link Property#toObjectResolved} is called on a
 * Certificate instance.
 *
 * @typedef Certificate.definition
 * @property {String} [name] A name for the certificate
 * @property {Array} [matches] A list of match patterns
 * @property {{ src: (String) }} [key] Object with path on the file system for private key file, as src
 * @property {{ src: (String) }} [cert] Object with path on the file system for certificate file, as src
 * @property {String} [passphrase] The passphrase for the certificate key
 *
 * @example <caption>JSON definition of an example certificate object</caption>
 * {
 *     "name": "My certificate for example.com",
 *     "matches": ["https://example.com/*"],
 *     "key": { "src": "/path/to/key" },
 *     "cert": { "src": "/User/path/to/certificate" },
 *     "passphrase": "iampassphrase"
 * }
 */
_.inherit((

    /**
     * A Certificate definition that represents the ssl certificate
     * to be used for an url.
     * Properties can then use the `.toObjectResolved` function to procure an object representation of the property with
     * all the variable references replaced by corresponding values.
     *
     * @constructor
     * @extends {Property}
     *
     * @param {Certificate.definition=} [options] Object with matches, key, cert and passphrase
     *
     * @example <caption> Create a new Certificate</caption>
     *
     * var Certificate = require('postman-collection').Certificate,
     *    certificate = new Certificate({
     *     name: 'Certificate for example.com',
     *     matches: ['example.com'],
     *     key: { src: '/User/path/to/certificate/key' },
     *     cert: { src: '/User/path/to/certificate' },
     *     passphrase: 'iampassphrase'
     * });
     */
    Certificate = function Certificate (options) {
        // this constructor is intended to inherit and as such the super constructor is required to be executed
        Certificate.super_.apply(this, arguments);

        this.update(options);
    }), Property);

_.assign(Certificate.prototype, /** @lends Certificate.prototype */ {
    /**
     * Ensure all object have id
     *
     * @private
     */
    _postman_propertyRequiresId: true,

    /**
     * Updates the certificate with the given properties.
     *
     * @param {Certificate.definition=} [options] Object with matches, key, cert and passphrase
     */
    update: function (options) {
        // return early if options is empty or invalid
        if (!_.isObject(options)) {
            return;
        }

        _.mergeDefined(this, /** @lends Certificate.prototype */ {
            /**
             * Unique identifier
             *
             * @type {String}
             */
            id: options.id,

            /**
             * Name for user reference
             *
             * @type {String}
             */
            name: options.name,

            /**
             * List of match pattern
             *
             * @type {UrlMatchPatternList}
             */
            matches: options.matches && new UrlMatchPatternList({}, options.matches),

            /**
             * Private Key
             *
             * @type {{ src: (string) }}
             */
            key: _.isObject(options.key) ? options.key : { src: options.key },

            /**
             * Certificate
             *
             * @type {{ src: (string) }}
             */
            cert: _.isObject(options.cert) ? options.cert : { src: options.cert },

            /**
             * PFX or PKCS12 Certificate
             *
             * @type {{ src: (string) }}
             */
            pfx: _.isObject(options.pfx) ? options.pfx : { src: options.pfx },

            /**
             * passphrase
             *
             * @type {Object}
             */
            passphrase: options.passphrase
        });
    },

    /**
     * Checks if the certificate can be applied to a given url
     *
     * @param {String|Url} url The url string for which the certificate is checked for match.
     */
    canApplyTo: function (url) {
        if (_.isEmpty(url)) {
            return false;
        }

        // convert url strings to Url
        (typeof url === STRING) && (url = new Url(url));

        // this ensures we don't proceed any further for any protocol
        // that is not https
        if (url.protocol !== HTTPS) {
            return false;
        }

        // test the input url against allowed matches
        return this.matches.test(url);
    },

    /**
     * Allows the serialization of a {@link Certificate}
     *
     * This is overridden, in order to ensure that certificate contents are not accidentally serialized,
     * which can be a security risk.
     */
    toJSON: function () {
        var obj = PropertyBase.toJSON(this);

        _.unset(obj, 'key.value');
        _.unset(obj, 'cert.value');
        _.unset(obj, 'pfx.value');

        return obj;
    }
});

_.assign(Certificate, /** @lends Certificate */ {
    /**
     * Defines the name of this property for internal use
     *
     * @private
     * @readOnly
     * @type {String}
     */
    _postman_propertyName: 'Certificate',

    /**
     * Specify the key to be used while indexing this object
     *
     * @private
     * @readOnly
     * @type {String}
     */
    _postman_propertyIndexKey: 'id',

    /**
     * Checks if the given object is a Certificate
     *
     * @param {*} obj -
     * @returns {Boolean}
     */
    isCertificate: function (obj) {
        return Boolean(obj) && ((obj instanceof Certificate) ||
            _.inSuperChain(obj.constructor, '_postman_propertyName', Certificate._postman_propertyName));
    }
});

module.exports = {
    Certificate
};