/**
* Copyright (C) 2017-2018 Regents of the University of California.
* @author: Jeff Thompson <[email protected]>
* @author: From ndn-cxx security https://github.com/named-data/ndn-cxx/blob/master/src/security/pib/certificate-container.cpp
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* A copy of the GNU Lesser General Public License is in the file COPYING.
*/
/** @ignore */
var Name = require('../../name.js').Name; /** @ignore */
var SyncPromise = require('../../util/sync-promise.js').SyncPromise; /** @ignore */
var CertificateV2 = require('../v2/certificate-v2.js').CertificateV2;
/**
* A PibCertificateContainer is used to search/enumerate the certificates of a
* key. (A PibCertificateContainer object can only be created by PibKey.)
*
* You should not call this private constructor. Instead, use
* PibCertificateContainer.makePromise().
*
* @param {Name} keyName The name of the key, which is copied.
* @param {PibImpl} pibImpl The PIB backend implementation.
* @param {Array<Name>} certificateNames The set of certificate
* names as an array of Name, as returned by getCertificatesOfKeyPromise.
* @constructor
*/
var PibCertificateContainer = function PibCertificateContainer
(keyName, pibImpl, certificateNames)
{
// The cache of loaded certificates. certificateName URI string => CertificateV2.
// (Use a string because we can't use the Name object as the key in JavaScript.)
this.certificates_ = {};
this.keyName_ = new Name(keyName);
this.pibImpl_ = pibImpl;
if (pibImpl == null)
throw new Error("The pibImpl is null");
// A set of Name URI string.
// (Use a string because we can't use indexOf with a Name object.)
this.certificateNameUris_ = [];
for (var i in certificateNames)
this.certificateNameUris_.push(certificateNames[i].toUri());
};
exports.PibCertificateContainer = PibCertificateContainer;
/**
* Create a PibCertificateContainer for a key with keyName.
* This method that returns a Promise is needed instead of a normal constructor
* since it uses asynchronous PibImpl methods to initialize the object.
* This method should only be called by PibKeyImpl.
*
* @param {Name} keyName The name of the key, which is copied.
* @param {PibImpl} pibImpl The PIB backend implementation.
* @param {boolean} useSync (optional) If true then return a SyncPromise which
* is already fulfilled. If omitted or false, this may return a SyncPromise or
* an async Promise.
* @param {Promise|SyncPromise} A promise which returns the new
* PibCertificateContainer.
*/
PibCertificateContainer.makePromise = function(keyName, pibImpl, useSync)
{
if (pibImpl == null)
return SyncPromise.reject(new Error("The pibImpl is null"));
return pibImpl.getCertificatesOfKeyPromise(keyName, useSync)
.then(function(certificateNames) {
return SyncPromise.resolve(new PibCertificateContainer
(keyName, pibImpl, certificateNames));
});
};
/**
* Get the number of certificates in the container.
* @return {number} The number of certificates.
*/
PibCertificateContainer.prototype.size = function()
{
return this.certificateNameUris_.length;
};
/**
* Add certificate into the container. If the certificate already exists, this
* replaces it.
* @param {CertificateV2} certificate The certificate to add. This copies the
* object.
* @param {boolean} useSync (optional) If true then return a SyncPromise which
* is already fulfilled. If omitted or false, this may return a SyncPromise or
* an async Promise.
* @return {Promise|SyncPromise} A promise which fulfills when finished, or a
* promise rejected with Error if the name of the certificate does not match the
* key name.
*/
PibCertificateContainer.prototype.addPromise = function(certificate, useSync)
{
if (!this.keyName_.equals(certificate.getKeyName()))
return SyncPromise.reject(new Error("The certificate name `" +
certificate.getKeyName().toUri() + "` does not match the key name"));
var certificateNameUri = certificate.getName().toUri();
if (this.certificateNameUris_.indexOf(certificateNameUri) < 0)
// Not already in the set.
this.certificateNameUris_.push(certificateNameUri);
// Copy the certificate.
this.certificates_[certificateNameUri] =
new CertificateV2(certificate);
return this.pibImpl_.addCertificatePromise(certificate, useSync);
};
/**
* Remove the certificate with name certificateName from the container. If the
* certificate does not exist, do nothing.
* @param {Name} certificateName The name of the certificate.
* @param {boolean} useSync (optional) If true then return a SyncPromise which
* is already fulfilled. If omitted or false, this may return a SyncPromise or
* an async Promise.
* @return {Promise|SyncPromise} A promise which fulfills when finished, or a
* promise rejected with Error if certificateName does not match the key name.
*/
PibCertificateContainer.prototype.removePromise = function
(certificateName, useSync)
{
if (!CertificateV2.isValidName(certificateName) ||
!CertificateV2.extractKeyNameFromCertName(certificateName).equals
(this.keyName_))
return SyncPromise.reject(new Error("Certificate name `" +
certificateName.toUri() + "` is invalid or does not match key name"));
var certificateNameUri = certificateName.toUri();
var index = this.certificateNameUris_.indexOf(certificateNameUri);
// Do nothing if it doesn't exist.
if (index >= 0)
this.certificateNameUris_.splice(index, 1);
delete this.certificates_[certificateNameUri];
return this.pibImpl_.removeCertificatePromise(certificateName, useSync);
};
/**
* Get the certificate with certificateName from the container.
* @param {Name} certificateName The name of the certificate.
* @param {boolean} useSync (optional) If true then return a SyncPromise which
* is already fulfilled. If omitted or false, this may return a SyncPromise or
* an async Promise.
* @return {SyncPromise} A promise which returns a copy of the CertificateV2, or
* a promise rejected with Error if certificateName does not match the key name,
* or a promise rejected with Pib.Error if the certificate does not exist.
*/
PibCertificateContainer.prototype.getPromise = function(certificateName, useSync)
{
var certificateNameUri = certificateName.toUri();
var cachedCertificate = this.certificates_[certificateNameUri];
if (cachedCertificate != undefined)
// Make a copy.
// TODO: Copy is expensive. Can we just tell the caller not to modify it?
return SyncPromise.resolve(new CertificateV2(cachedCertificate));
// Get from the PIB and cache.
if (!CertificateV2.isValidName(certificateName) ||
!CertificateV2.extractKeyNameFromCertName(certificateName).equals
(this.keyName_))
return SyncPromise.reject(new Error("Certificate name `" +
certificateName.toUri() + "` is invalid or does not match key name"));
var thisContainer = this;
return this.pibImpl_.getCertificatePromise(certificateName, useSync)
.then(function(certificate) {
thisContainer.certificates_[certificateNameUri] = certificate;
// Make a copy.
// TODO: Copy is expensive. Can we just tell the caller not to modify it?
return SyncPromise.resolve(new CertificateV2(certificate));
});
};