Skip to content
代码片段 群组 项目
validation.js 6.8 KB
更新 更旧
/**
 *
 *    Copyright (c) 2020 Silicon Labs
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
Timotej Ecimovic's avatar
Timotej Ecimovic 已提交

/**
 * This module provides the APIs for validating inputs to the database, and returning flags indicating if
 * things were successful or not.
 *
Timotej Ecimovic's avatar
Timotej Ecimovic 已提交
 */

const queryZcl = require('../db/query-zcl.js')
const queryConfig = require('../db/query-config.js')
Timotej Ecimovic's avatar
Timotej Ecimovic 已提交
const queryEndpoint = require('../db/query-endpoint.js')
const types = require('../util/types.js')

async function validateAttribute(db, endpointTypeId, attributeRef, clusterRef) {
  let endpointAttribute = await queryZcl.selectEndpointTypeAttribute(
    db,
    endpointTypeId,
    attributeRef,
    clusterRef
  )
  let attribute = await queryZcl.selectAttributeById(db, attributeRef)
  return validateSpecificAttribute(endpointAttribute, attribute)
Timotej Ecimovic's avatar
Timotej Ecimovic 已提交
}

Timotej Ecimovic's avatar
Timotej Ecimovic 已提交
async function validateEndpoint(db, endpointId) {
Timotej Ecimovic's avatar
Timotej Ecimovic 已提交
  let endpoint = await queryEndpoint.selectEndpoint(db, endpointId)
Timotej Ecimovic's avatar
Timotej Ecimovic 已提交
  let currentIssues = validateSpecificEndpoint(endpoint)
  let noDuplicates = await validateNoDuplicateEndpoints(
    db,
    endpoint.endpointId,
    endpoint.sessionRef
  )
  if (!noDuplicates) {
    currentIssues.endpointId.push('Duplicate EndpointIds Exist')
  }
  return currentIssues
Timotej Ecimovic's avatar
Timotej Ecimovic 已提交
async function validateNoDuplicateEndpoints(
  db,
  endpointIdentifier,
  sessionRef
) {
  let count =
    await queryConfig.selectCountOfEndpointsWithGivenEndpointIdentifier(
      db,
      endpointIdentifier,
      sessionRef
    )
Timotej Ecimovic's avatar
Timotej Ecimovic 已提交
  return count.length <= 1
function validateSpecificAttribute(endpointAttribute, attribute) {
  if (attribute.isNullable && endpointAttribute.defaultValue == null) {
    return { defaultValue: defaultAttributeIssues }
  } else if (!types.isString(attribute.type)) {
    if (types.isFloat(attribute.type)) {
      if (!isValidFloat(endpointAttribute.defaultValue))
        defaultAttributeIssues.push('Invalid Float')
      //Interpreting float values
      if (!checkAttributeBoundsFloat(attribute, endpointAttribute))
        defaultAttributeIssues.push('Out of range')
    } else if (types.isSignedInteger(attribute.type)) {
      if (!isValidSignedNumberString(endpointAttribute.defaultValue))
        defaultAttributeIssues.push('Invalid Integer')
      if (!checkAttributeBoundsInteger(attribute, endpointAttribute))
        defaultAttributeIssues.push('Out of range')
    } else {
      if (!isValidNumberString(endpointAttribute.defaultValue))
        defaultAttributeIssues.push('Invalid Integer')
      if (!checkAttributeBoundsInteger(attribute, endpointAttribute))
        defaultAttributeIssues.push('Out of range')
    }
  } else if (types.isString(attribute.type)) {
    let maxLengthForString =
      attribute.type == 'char_string' || attribute.type == 'octet_string'
        ? 255
Bharat Dandu's avatar
Bharat Dandu 已提交
        : 65535
    let maxAllowedLength = attribute.maxLength
      ? attribute.maxLength
      : maxLengthForString
    if (endpointAttribute.defaultValue.length > maxAllowedLength) {
      defaultAttributeIssues.push('String length out of range')
    }
  }
  return { defaultValue: defaultAttributeIssues }
}

function validateSpecificEndpoint(endpoint) {
  let zclEndpointIdIssues = []
  let zclNetworkIdIssues = []
  if (!isValidNumberString(endpoint.endpointId))
    zclEndpointIdIssues.push('EndpointId is invalid number string')
  if (
    extractIntegerValue(endpoint.endpointId) > 0xffff ||
    extractIntegerValue(endpoint.endpointId) < 0
  )
    zclEndpointIdIssues.push('EndpointId is out of valid range')
  if (!isValidNumberString(endpoint.networkId))
    zclNetworkIdIssues.push('NetworkId is invalid number string')
  if (extractIntegerValue(endpoint.endpointId) == 0)
    zclEndpointIdIssues.push('0 is not a valid endpointId')
  return {
    endpointId: zclEndpointIdIssues,
    networkId: zclNetworkIdIssues,
  }
}

//This applies to both actual numbers as well as octet strings.
function isValidNumberString(value) {
  //We test to see if the number is valid in hex. Decimals numbers also pass this test
  return /^(0x)?[\dA-F]+$/i.test(value) || Number.isInteger(Number(value))
Timotej Ecimovic's avatar
Timotej Ecimovic 已提交
}

function isValidSignedNumberString(value) {
  return /^(0x)?[\dA-F]+$/i.test(value) || Number.isInteger(Number(value))
function isValidFloat(value) {
  return !/^0x/i.test(value) && !isNaN(Number(value))
Timotej Ecimovic's avatar
Timotej Ecimovic 已提交
}
function extractFloatValue(value) {
  return parseFloat(value)
Timotej Ecimovic's avatar
Timotej Ecimovic 已提交
}

function extractIntegerValue(value) {
  if (/^-?\d+$/.test(value)) {
    return parseInt(value)
  } else if (/^[0-9A-F]+$/i.test(value)) {
    return parseInt(value, 16)
  } else {
    return parseInt(value, 16)
  }
Timotej Ecimovic's avatar
Timotej Ecimovic 已提交
}

function getBoundsInteger(attribute) {
  return {
    min: extractIntegerValue(attribute.min),
    max: extractIntegerValue(attribute.max),
  }
Timotej Ecimovic's avatar
Timotej Ecimovic 已提交
}

function checkAttributeBoundsInteger(attribute, endpointAttribute) {
  let { min, max } = getBoundsInteger(attribute)
  let defaultValue = extractIntegerValue(endpointAttribute.defaultValue)
  return checkBoundsInteger(defaultValue, min, max)
}
Timotej Ecimovic's avatar
Timotej Ecimovic 已提交

function checkBoundsInteger(defaultValue, min, max) {
  if (Number.isNaN(min)) min = Number.MIN_SAFE_INTEGER
  if (Number.isNaN(max)) max = Number.MAX_SAFE_INTEGER
  return defaultValue >= min && defaultValue <= max
Timotej Ecimovic's avatar
Timotej Ecimovic 已提交
}

function checkAttributeBoundsFloat(attribute, endpointAttribute) {
  let { min, max } = getBoundsFloat(attribute)
  let defaultValue = extractFloatValue(endpointAttribute.defaultValue)
  return checkBoundsFloat(defaultValue, min, max)
}

function getBoundsFloat(attribute) {
  return {
    min: extractFloatValue(attribute.min),
    max: extractFloatValue(attribute.max),
  }
}

function checkBoundsFloat(defaultValue, min, max) {
  if (Number.isNaN(min)) min = Number.MIN_VALUE
  if (Number.isNaN(max)) max = Number.MAX_VALUE
  return defaultValue >= min && defaultValue <= max
}

// exports
exports.validateAttribute = validateAttribute
exports.validateEndpoint = validateEndpoint
exports.validateNoDuplicateEndpoints = validateNoDuplicateEndpoints
exports.validateSpecificAttribute = validateSpecificAttribute
exports.validateSpecificEndpoint = validateSpecificEndpoint
exports.isValidNumberString = isValidNumberString
exports.isValidFloat = isValidFloat
exports.extractFloatValue = extractFloatValue
exports.extractIntegerValue = extractIntegerValue
exports.getBoundsInteger = getBoundsInteger
exports.checkBoundsInteger = checkBoundsInteger
exports.getBoundsFloat = getBoundsFloat
exports.checkBoundsFloat = checkBoundsFloat