Source: security/v2/validator-config/config-rule.js

/**
 * Copyright (C) 2018 Regents of the University of California.
 * @author: Jeff Thompson <jefft0@remap.ucla.edu>
 * @author: From ndn-cxx security https://github.com/named-data/ndn-cxx/blob/master/src/security/v2/validator-config/rule.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 ConfigChecker = require('./config-checker.js').ConfigChecker; /** @ignore */
var ConfigFilter = require('./config-filter.js').ConfigFilter; /** @ignore */
var ValidatorConfigError = require('../../validator-config-error.js').ValidatorConfigError; /** @ignore */
var LOG = require('../../../log.js').Log.LOG;

/**
 * A ConfigRule represents a rule configuration section, used by ConfigValidator.
 *
 * Create a ConfigRule with empty filters and checkers.
 * @param {String} id The rule ID from the configuration section.
 * @param {boolean} isForInterest True if the rule is for an Interest packet,
 * false if it is for a Data packet.
 * @constructor
 */
var ConfigRule = function ConfigRule(id, isForInterest)
{
  this.id_ = id;
  this.isForInterest_ = isForInterest;
  this.filters_ = [];  // of ConfigFilter
  this.checkers_ = []; // of ConfigChecker
};

exports.ConfigRule = ConfigRule;

/**
 * Get the rule ID.
 * @return {String} The rule ID.
 */
ConfigRule.prototype.getId = function() { return this.id_; };

/**
 * Get the isForInterest flag.
 * @return {boolean} True if the rule is for an Interest packet, false if it is
 * for a Data packet.
 */
ConfigRule.prototype.getIsForInterest = function() { return this.isForInterest_; };

/**
 * Add the ConfigFilter to the list of filters.
 * @param {ConfigFilter} filter The ConfigFilter.
 */
ConfigRule.prototype.addFilter = function(filter)
{
  this.filters_.push(filter);
};

/**
 * Add the ConfigChecker to the list of checkers.
 * @param {ConfigChecker} checker The ConfigChecker.
 */
ConfigRule.prototype.addChecker = function(checker)
{
  this.checkers_.push(checker);
};

/**
 * Check if the packet name matches the rule's filter.
 * If no filters were added, the rule matches everything.
 * @param {boolean} isForInterest True if packetName is for an Interest, false
 * if for a Data packet.
 * @param {Name} packetName The packet name. For a signed interest, the last two
 * components are skipped but not removed.
 * @return {boolean} True if at least one filter matches the packet name, false
 * if none of the filters match the packet name.
 * @throws ValidatorConfigError if the supplied isForInterest doesn't match the
 * one for which the rule is designed.
 */
ConfigRule.prototype.match = function(isForInterest, packetName)
{
  if (LOG > 3) console.log("Trying to match " + packetName.toUri());

  if (isForInterest != this.isForInterest_)
    throw new ValidatorConfigError(new Error
      ("Invalid packet type supplied ( " +
       (isForInterest ? "interest" : "data") + " != " +
       (this.isForInterest_ ? "interest" : "data") + ")"));

  if (this.filters_.length == 0)
    return true;

  var result = false;
  for (var i = 0; i < this.filters_.length; ++i) {
    result = (result || this.filters_[i].match(isForInterest, packetName));
    if (result)
      break;
  }

  return result;
};

/**
 * Check if the packet satisfies the rule's condition.
 * @param {boolean} isForInterest True if packetName is for an Interest, false
 * if for a Data packet.
 * @param {Name} packetName The packet name. For a signed interest, the last two
 * components are skipped but not removed.
 * @param {Name} keyLocatorName The KeyLocator's name.
 * @param {ValidationState} state This calls state.fail() if the packet is invalid.
 * @return {boolean} True if further signature verification is needed, or false
 * if the packet is immediately determined to be invalid in which case this
 * calls state.fail() with the proper code and message.
 * @throws ValidatorConfigError if the supplied isForInterest doesn't match the
 * one for which the rule is designed.
 */
ConfigRule.prototype.check = function
  (isForInterest, packetName, keyLocatorName, state)
{
  if (LOG > 3) console.log("Trying to check " +  packetName.toUri() +
    " with keyLocator " +keyLocatorName.toUri());

  if (isForInterest != this.isForInterest_)
    throw new ValidatorConfigError(new Error
      ("Invalid packet type supplied ( " +
       (isForInterest ? "interest" : "data") + " != " +
       (this.isForInterest_ ? "interest" : "data") + ")"));

  var hasPendingResult = false;
  for (var i = 0; i < this.checkers_.length; ++i) {
    var result = this.checkers_[i].check
      (isForInterest, packetName, keyLocatorName, state);
    if (!result)
      return result;
    hasPendingResult = true;
  }

  return hasPendingResult;
};

/**
 * Create a rule from configuration section.
 * @param {BoostInfoTree} configSection The section containing the definition of
 * the checker, e.g. one of "validator.rule".
 * @return {ConfigRule} A new ConfigRule created from the configuration
 */
ConfigRule.create = function(configSection)
{
  // Get rule.id .
  var ruleId = configSection.getFirstValue("id");
  if (ruleId == null)
    throw new ValidatorConfigError(new Error("Expecting <rule.id>"));

  // Get rule.for .
  var usage = configSection.getFirstValue("for");
  if (usage == null)
    throw new ValidatorConfigError(new Error
      ("Expecting <rule.for> in rule: " + ruleId));

  var isForInterest;
  if (usage.toLowerCase() == "data")
    isForInterest = false;
  else if (usage.toLowerCase() == "interest")
    isForInterest = true;
  else
    throw new ValidatorConfigError(new Error
      ("Unrecognized <rule.for>: " + usage + " in rule: " + ruleId));

  var rule = new ConfigRule(ruleId, isForInterest);

  // Get rule.filter(s)
  var filterList = configSection.get("filter");
  for (var i = 0; i < filterList.length; ++i)
    rule.addFilter(ConfigFilter.create(filterList[i]));

  // Get rule.checker(s)
  var checkerList = configSection.get("checker");
  for (var i = 0; i < checkerList.length; ++i)
    rule.addChecker(ConfigChecker.create(checkerList[i]));

  // Check other stuff.
  if (checkerList.length == 0)
    throw new ValidatorConfigError(new Error
      ("No <rule.checker> is specified in rule: " + ruleId));

  return rule;
};