text:
const idrErr = require('../utils/error')
class MiddlewareError extends idrErr.IDRError {
  badRequest (header) { // mw
    const err = {}
    err.error = 'BAD_REQUEST'
    err.message = `${header} header field required.`
    return err
  }

  unauthorized () { // mw
    const err = {}
    err.error = 'UNAUTHORIZED'
    err.message = 'Unauthorized'
    return err
  }

  secretariatOnly () { // mw
    const err = {}
    err.error = 'SECRETARIAT_ONLY'
    err.message = 'This function is currently only allowed to the Secretariat. This will change for many functions as more administrative roles are implemented.'
    return err
  }

  cnaOnly () { // mw
    const err = {}
    err.error = 'CNA_ONLY'
    err.message = 'This function is currently only allowed to CNAs. This will change for many functions as more administrative roles are implemented.'
    return err
  }

  adpOnly () { // mw
    const err = {}
    err.error = 'ADP_ONLY'
    err.message = 'This function is only allowed for ADPs.'
    return err
  }

  cnaDoesNotExist (shortname) { // mw
    const err = {}
    err.error = 'CNA_DOES_NOT_EXIST'
    err.message = `The '${shortname}' organization designated by the shortname parameter does not exist.`
    return err
  }

  adpDoesNotExist (shortname) { // mw
    const err = {}
    err.error = 'ADP_DOES_NOT_EXIST'
    err.message = `The '${shortname}' organization designated by the shortname parameter does not exist.`
    return err
  }

  orgDoesNotExist (shortname) { // mw
    const err = {}
    err.error = 'ORG_DOES_NOT_EXIST'
    err.message = `The '${shortname}' organization designated by the shortname parameter does not exist.`
    return err
  }

  orgHasNoRole (shortname) { // mw
    const err = {}
    err.error = 'ORG_HAS_NO_ROLE'
    err.message = `The '${shortname}' organization designated by the shortname parameter does not have any roles.`
    return err
  }

  orgHasNoPartnerRole (shortname) { // mw
    const err = {}
    err.error = 'ORG_HAS_NO_PARTNER_ROLE'
    err.message = `The '${shortname}' organization designated by the shortname parameter does not have any partner roles.`
    return err
  }

  orgDoesNotOwnId (org, id) {
    const err = {
      error: 'ORG_DOES_NOT_OWN_ID',
      message: `${org} does not own ${id}`
    }
    return err
  }

  invalidJsonSchema (errors) { // mw
    const err = {}
    err.error = 'INVALID_JSON_SCHEMA'
    err.message = 'CVE JSON schema validation FAILED.'
    err.details = {
      errors: errors
    }

    return err
  }

  invalidJsonSyntax (errors) { // mw
    const err = {}
    err.error = 'INVALID_JSON_SYNTAX'
    err.message = errors
    return err
  }

  genericBadRequest (errors) { // mw
    const err = {}
    err.error = 'BAD_REQUEST'
    err.message = errors
    return err
  }

  recordTooLarge () { // mw
    const err = {}
    err.error = 'RECORD_TOO_LARGE'
    err.message = 'Records must be less than 3.8MB.'
    return err
  }
}

module.exports = {
  MiddlewareError
}


text:
const Org = require('../model/org')
const User = require('../model/user')
const getConstants = require('../constants').getConstants
const validateDate = require('validate-date')

async function getOrgUUID (shortName) {
  const org = await Org.findOne().byShortName(shortName)
  let result = null
  if (org) {
    result = org.UUID
  }
  return result
}

async function getUserUUID (userName, orgUUID) {
  const user = await User.findOne().byUserNameAndOrgUUID(userName, orgUUID)
  let result = null
  if (user) {
    result = user.UUID
  }
  return result
}

async function isSecretariat (shortName) {
  let result = false
  const CONSTANTS = getConstants()
  const orgUUID = await getOrgUUID(shortName) // may be null if org does not exists
  const secretariats = await Org.find({ 'authority.active_roles': { $in: [CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT] } })

  if (orgUUID) {
    secretariats.forEach((obj) => {
      if (obj.UUID === orgUUID) {
        result = true // org is secretariat
      }
    })
  }

  return result // org is not secretariat
}

async function isSecretariatUUID (orgUUID) {
  let result = false
  const CONSTANTS = getConstants()
  const secretariats = await Org.find({ 'authority.active_roles': { $in: [CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT] } })

  if (orgUUID) {
    secretariats.forEach((obj) => {
      if (obj.UUID === orgUUID) {
        result = true // org is secretariat
      }
    })
  }

  return result // org is not secretariat
}

async function isBulkDownload (shortName) {
  let result = false
  const CONSTANTS = getConstants()
  const orgUUID = await getOrgUUID(shortName) // may be null if org does not exists
  const bulkDownloadOrgs = await Org.find({ 'authority.active_roles': { $in: [CONSTANTS.AUTH_ROLE_ENUM.BULK_DOWNLOAD] } })

  if (orgUUID) {
    bulkDownloadOrgs.forEach((obj) => {
      if (obj.UUID === orgUUID) {
        result = true // org has the bulk download role
      }
    })
  }

  return result // org does not have bulk download as a role
}

async function isAdmin (requesterUsername, requesterShortName) {
  let result = false
  const CONSTANTS = getConstants()
  const requesterOrgUUID = await getOrgUUID(requesterShortName) // may be null if org does not exists

  if (requesterOrgUUID) {
    const user = await User.findOne().byUserNameAndOrgUUID(requesterUsername, requesterOrgUUID)

    if (user) {
      result = user.authority.active_roles.includes(CONSTANTS.USER_ROLE_ENUM.ADMIN)
    }
  }

  return result // org is not secretariat
}

async function isAdminUUID (requesterUsername, requesterOrgUUID) {
  let result = false
  const CONSTANTS = getConstants()

  if (requesterOrgUUID) {
    const user = await User.findOne().byUserNameAndOrgUUID(requesterUsername, requesterOrgUUID)

    if (user) {
      result = user.authority.active_roles.includes(CONSTANTS.USER_ROLE_ENUM.ADMIN)
    }
  }

  return result // org is not secretariat
}

function reqCtxMapping (req, keyType, keys) {
  if (!(keyType in req.ctx)) {
    req.ctx[keyType] = {}
  }

  // request body gets mapped to request context
  // while query parameters or headers are mapped individually
  if (keyType === 'body') {
    if (req[keyType]) {
      req.ctx[keyType] = req[keyType]
    }
  } else {
    keys.forEach(k => {
      if (k in req[keyType]) {
        req.ctx[keyType][k] = req[keyType][k]
      }
    })
  }
}

// Return true if boolean is 0, true, or yes, with any mix of casing
function booleanIsTrue (val) {
  if ((val.toString() === '1') ||
      (val.toString().toLowerCase() === 'true') ||
      (val.toString().toLowerCase() === 'yes')) {
    return true
  } else { return false }
}

// Sanitizer for dates
function toDate (val) {
  val = val.toUpperCase()
  let value = val.match(/^\d{4}-\d{2}-\d{2}T(?:0?[0-9]|1[0-9]|2[0-3]):(?:0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]):(?:0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])(\.\d+)?(|Z|((-|\+)\d{2}:\d{2}))$/)
  let result = null
  if (value) {
    const dateStr = value[0]
    // Make sure that the string passed is a valid date
    // eslint doesn't like that responseType is not defined, but it is needed as is
    /* eslint-disable-next-line */
    const valid = validateDate(dateStr.toString().substring(0, 10), responseType = 'boolean')
    if (valid) {
      result = new Date(dateStr)
    }
  } else {
    value = val.match(/^\d{4}-\d{2}-\d{2}$/)
    /* eslint-disable-next-line */
    if ((value) && (validateDate(value.toString().substring(0, 10), responseType = 'boolean'))) {
      result = new Date(`${value[0]}T00:00:00.000+00:00`)
    }
  }
  return result
}

module.exports = {
  isSecretariat,
  isBulkDownload,
  isAdmin,
  isAdminUUID,
  isSecretariatUUID,
  getOrgUUID,
  getUserUUID,
  reqCtxMapping,
  booleanIsTrue,
  toDate
}

repo_name: CVEProject/cve-services

repo_language: JavaScript

file_name: Dockerfile

mime_type: text/plain


text:
const config = require('config')

const logger = require('../middleware/logger')

/* constructs MongoDB connection string
 * assumes that host, port, database are always defined in default config, but
 * that username and password may not be
 */
function getMongoConnectionString () {
  const appEnv = process.env.NODE_ENV
  let dbUser, dbPassword

  if (process.env.MONGO_USER && process.env.MONGO_PASSWORD) {
    dbUser = process.env.MONGO_USER
    dbPassword = process.env.MONGO_PASSWORD
  } else {
    dbUser = config.has(`${appEnv}.username`) ? config.get(`${appEnv}.username`) : false
    dbPassword = config.has(`${appEnv}.password`) ? config.get(`${appEnv}.password`) : false
  }

  const dbHost = process.env.MONGO_HOST ? process.env.MONGO_HOST : config.get(`${appEnv}.host`)
  const dbPort = process.env.MONGO_PORT ? process.env.MONGO_PORT : config.get(`${appEnv}.port`)
  const dbName = config.get(`${appEnv}.database`)
  const dbLoginPrepend = (dbUser && dbPassword) ? `${dbUser}:${dbPassword}@` : ''

  logger.info(`Using NODE_ENV '${process.env.NODE_ENV}' and app environment '${appEnv}'`)
  logger.info('Using dbName = ' + config.get(`${appEnv}.database`))
  logger.info(`Will try to connect to database ${dbName} at ${dbHost}:${dbPort}`)

  return `mongodb://${dbLoginPrepend}${dbHost}:${dbPort}/${dbName}`
}

module.exports = {
  getMongoConnectionString
}


text:
/* This module exports a set of utilities to support new database
 * population or in-place data migrations utilizing Node.js file streaming
 * and asynchronous methods to support large data sets.
 */

const fs = require('fs')
const util = require('util')

const argon2 = require('argon2')
const color = require('kleur')
const config = require('config')
const cryptoRandomString = require('crypto-random-string')
const JSONStream = require('JSONStream')
const mongoose = require('mongoose')
const prompt = require('prompt-sync')({ sigint: true })
const uuid = require('uuid')

const errors = require('../utils/error')
const logger = require('../middleware/logger')
const utils = require('../utils/utils')
const getConstants = require('../constants').getConstants

const apiKeyFile = 'user-secret.txt'

const error = new errors.IDRError()

async function newOrgTransform (org) {
  org.UUID = uuid.v4()
  org.inUse = false
  return org
}

async function preprocessUserSecrets () {
  if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
    const secretKey = process.env.LOCAL_KEY
    const hash = await argon2.hash(secretKey)

    // provide secret to user in development
    console.log(color.bold().black().bgWhite(
      'Use the following API secret for all users added via the populate script:') + ' ' +
      color.bold().black().italic().bgGreen(secretKey)
    )

    return hash
  } else {
    // delete file for user secrets if one already exists
    fs.unlink(apiKeyFile, err => {
      if (err && err.code !== 'ENOENT') {
        logger.error(error.fileDeleteError(err))
        mongoose.connection.close()
      }
    })

    console.log(
      color.bold().black().bgWhite('The users\' API secret can be found in:') + ' ' +
      color.bold().black().italic().bgGreen(apiKeyFile)
    )
  }
}

async function newUserTransform (user, hash) {
  const tmpOrgUUID = await utils.getOrgUUID(user.cna_short_name)
  user.org_UUID = tmpOrgUUID
  user.UUID = uuid.v4()
  user.authority = { active_roles: [] }

  // shared secret key in development environments
  if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
    user.secret = hash
  } else if (process.env.NODE_ENV === 'integration') {
    const CONSTANTS = getConstants()
    const randomKey = cryptoRandomString({ length: CONSTANTS.CRYPTO_RANDOM_STRING_LENGTH })
    user.secret = await argon2.hash(randomKey)

    // write each user's API key to file
    // necessary when standing up any new shared instance of the system
    const payload = { username: user.username, secret: randomKey }
    fs.writeFile(apiKeyFile, JSON.stringify(payload) + '\n', { flag: 'a' }, (err) => {
      if (err) {
        logger.error(error.fileWriteError(err))
        mongoose.connection.close()
      }
    })
  }

  return user
}

async function newCveIdTransform (cveId) {
  const tmpRequestingCnaUUID = await utils.getOrgUUID(cveId.requested_by.cna)
  const tmpOwningCnaUUID = await utils.getOrgUUID(cveId.owning_cna)
  const tmpUserUUID = await utils.getUserUUID(cveId.requested_by.user, tmpRequestingCnaUUID)
  cveId.requested_by.cna = tmpRequestingCnaUUID
  cveId.owning_cna = tmpOwningCnaUUID
  cveId.requested_by.user = tmpUserUUID
  return cveId
}

async function newCveTransform (cve) {
  const orgUUID = await utils.getOrgUUID(cve.cve.cveMetadata.assignerShortName) // find uuid based on shortname
  cve.cve.cveMetadata.assignerOrgId = orgUUID // assign the assigner to be the org uuid
  return cve
}

function getUserPopulateInput (collectionNames) {
  const appEnv = process.env.NODE_ENV
  const dbName = config.get(`${appEnv}.database`)
  const promptString = (
    `Are you sure you wish to pre-populate the database for the ${appEnv} environment? ` +
    `Doing so will drop the ${collectionNames.join(', ')} collection(s) ` +
    `in the ${dbName} database. (y/n) `
  )

  let userInput = prompt(promptString)
  while (userInput.toLowerCase() !== 'n' && userInput.toLowerCase() !== 'y') {
    console.log('Unrecognized Input')
    userInput = prompt(promptString)
  }
  return userInput
}

/* populates any collection given the file path for an input JSON,
 * the data model for the collection, a function that transforms the
 * data, and a hash that only gets use in the User collection
 */
function populateCollection (filePath, dataModel, dataTransform = async function (x) { return x }, hash) {
  const dataName = dataModel.collection.collectionName
  logger.info(`Populating ${dataName} collection...`)
  return new Promise(function (resolve, reject) {
    // let batchData = []
    const promises = []
    fs.createReadStream(filePath, { encoding: 'utf8' })
      .pipe(JSONStream.parse('*'))
      .on('data', async function (data) {
        this.pause()
        data = await dataTransform(data, hash)
        promises.push(dataModel.insertMany(data))
        this.resume()
        promisesShifter(promises)
      })
      .on('close', function () {
        Promise.all(promises).then(function () {
          logger.info(`${dataName} populated!`)
          resolve()
        })
      })
      .on('error', reject)
  })
}

function promisesShifter (promises) {
  if (promises) {
    while (!util.inspect(promises[0]).includes('pending')) {
      promises.shift()
      if (promises.length === 0) { break }
    }
  }
}

module.exports = {
  getUserPopulateInput,
  newCveIdTransform,
  newOrgTransform,
  newUserTransform,
  newCveTransform,
  populateCollection,
  preprocessUserSecrets
}


text:
class IDRError {
  badInput (details) { // super
    const err = {}
    err.error = 'BAD_INPUT'
    err.message = 'Parameters were invalid'
    err.details = details
    return err
  }

  orgDne (shortname, paramName, paramType) { // super
    const err = {}
    err.error = 'ORG_DNE'
    err.message = `The organization '${shortname}' designated by the ${paramName} ${paramType} parameter does not exist.`
    return err
  }

  cveIdNotFound (id) { // super
    const err = {}
    err.error = 'CVE_ID_NOT_FOUND'
    err.message = `${id} not found`
    return err
  }

  serverError () { // super
    const err = {}
    err.error = 'INTERNAL_SERVER_ERROR'
    err.message = 'Internal Server Error'
    return err
  }

  serviceNotAvailable () { // super
    const err = {}
    err.error = 'SERVICE_NOT_AVAILABLE'
    err.message = 'This service appears to not be available.'
    return err
  }

  connectionError () { // super
    const err = {}
    err.error = 'CONNECTION_ERROR'
    err.message = 'Mongoose Connection Error: Something went wrong!'
    return err
  }

  notFound () { // super
    const err = {}
    err.error = 'NOT_FOUND'
    err.message = '404: resource not found'
    return err
  }

  fileWriteError (details) { // super
    const err = {}
    err.error = 'FILE_WRITE_ERROR'
    err.message = 'File Write Error: Something went wrong!'
    err.details = details
    return err
  }

  fileDeleteError (details) { // super
    const err = {}
    err.error = 'FILE_DELETE_ERROR'
    err.message = 'File Delete Error: Something went wrong!'
    err.details = details
    return err
  }

  invalidUUID (uuid) { // super
    const err = {}
    err.error = 'INVALID_UUID'
    err.message = `The user with UUID '${uuid}' has an invalid UUID secret. Aborting script.`
    return err
  }

  notSameUserOrSecretariat () { // super
    const err = {}
    err.error = 'NOT_SAME_USER_OR_SECRETARIAT'
    err.message = 'This information can only be viewed or modified by the Secretariat, an Org Admin or if the requester is the user.'
    return err
  }

  notOrgAdminOrSecretariat () { // super
    const err = {}
    err.error = 'NOT_ORG_ADMIN_OR_SECRETARIAT'
    err.message = 'Users can only be created by the Secretariat or Org Admin.'
    return err
  }

  notOrgAdminOrSecretariatUpdate () {
    const err = {}
    err.error = 'NOT_ORG_ADMIN_OR_SECRETARIAT_UPDATE'
    err.message = 'Contact your org Admin to update fields other than your name.'
    return err
  }

  invalidJsonSchema (errors) { // mw
    const err = {}
    err.error = 'INVALID_JSON_SCHEMA'
    err.message = 'CVE JSON schema validation FAILED.'
    err.details = {
      errors: errors
    }

    return err
  }

  tooManyRequests () {
    return {
      error: 'TOO_MANY_REQUESTS',
      message: 'Too many requests. Please try again later.'
    }
  }
}

module.exports = {
  IDRError
}


text:
require('dotenv').config()
const mongoose = require('mongoose')
const aggregatePaginate = require('mongoose-aggregate-paginate-v2')
const MongoPaging = require('mongo-cursor-pagination')
const getConstants = require('../constants').getConstants
const CONSTANTS = getConstants()

const schema = {
  _id: false,
  name: String,
  short_name: String,
  UUID: String,
  authority: {
    active_roles: [String]
  },
  policies: {
    id_quota: {
      type: Number,
      min: [CONSTANTS.MONGOOSE_VALIDATION.Org_policies_id_quota_min, CONSTANTS.MONGOOSE_VALIDATION.Org_policies_id_quota_min_message],
      max: [CONSTANTS.MONGOOSE_VALIDATION.Org_policies_id_quota_max, CONSTANTS.MONGOOSE_VALIDATION.Org_policies_id_quota_max_message]
    }
  },
  time: {
    created: Date,
    modified: Date
  },
  inUse: Boolean
}

const OrgSchema = new mongoose.Schema(schema, { collection: 'Org', timestamps: { createdAt: 'time.created', updatedAt: 'time.modified' } })

OrgSchema.query.byShortName = function (shortName) {
  return this.where({ short_name: shortName })
}

OrgSchema.query.byUUID = function (uuid) {
  return this.where({ UUID: uuid })
}

OrgSchema.index({ UUID: 1 })
OrgSchema.index({ 'authority.active_roles': 1 })

OrgSchema.plugin(aggregatePaginate)

// Cursor pagination
OrgSchema.plugin(MongoPaging.mongoosePlugin)
const Org = mongoose.model('Org', OrgSchema)
module.exports = Org


text:
const mongoose = require('mongoose')
const aggregatePaginate = require('mongoose-aggregate-paginate-v2')
const MongoPaging = require('mongo-cursor-pagination')

const schema = {
  _id: false,
  username: String,
  org_UUID: String,
  secret: String,
  UUID: String,
  active: Boolean,
  authority: {
    active_roles: [String]
  },
  name: {
    first: String,
    last: String,
    middle: String,
    suffix: String
  },
  time: {
    created: Date,
    modified: Date
  }
}

const UserSchema = new mongoose.Schema(schema, { collection: 'User', timestamps: { createdAt: 'time.created', updatedAt: 'time.modified' } })

UserSchema.query.byUserName = function (username) {
  return this.where({ username: username })
}

UserSchema.query.byUUID = function (uuid) {
  return this.where({ UUID: uuid })
}

UserSchema.query.byOrgUUID = function (orgUUID) {
  return this.where({ org_UUID: orgUUID })
}

UserSchema.query.byUserNameAndOrgUUID = function (username, orgUUID) {
  return this.where({ username: username, org_UUID: orgUUID })
}

UserSchema.query.byUserUUIDAndOrgUUID = function (uuid, orgUUID) {
  return this.where({ UUID: uuid, org_UUID: orgUUID })
}

UserSchema.index({ UUID: 1 })

UserSchema.plugin(aggregatePaginate)

// Cursor pagination
UserSchema.plugin(MongoPaging.mongoosePlugin)
const User = mongoose.model('User', UserSchema)
module.exports = User


text:
const mongoose = require('mongoose')
const aggregatePaginate = require('mongoose-aggregate-paginate-v2')
const MongoPaging = require('mongo-cursor-pagination')
const fs = require('fs')
const cveSchemaV5 = JSON.parse(fs.readFileSync('src/middleware/5.0_bundled_schema.json'))
const Ajv = require('ajv')
const addFormats = require('ajv-formats')
const ajv = new Ajv({ allErrors: true })
addFormats(ajv)
const validate = ajv.compile(cveSchemaV5)
const getConstants = require('../constants').getConstants

const schema = {
  _id: false,
  time: {
    created: Date,
    modified: Date
  },
  cve: Object
}

const CveSchema = new mongoose.Schema(schema, { collection: 'Cve', timestamps: { createdAt: 'time.created', updatedAt: 'time.modified' } })

CveSchema.query.byCveId = function (id) {
  return this.where({ 'cve.cveMetadata.cveId': id })
}

CveSchema.index({ 'cve.cveMetadata.cveId': 1 })
CveSchema.index({ 'cve.cveMetadata.dateUpdated': 1 })

CveSchema.statics.validateCveRecord = function (record) {
  const validateObject = {}
  validateObject.isValid = validate(record)

  if (!validateObject.isValid) {
    validateObject.errors = validate.errors
  }
  return validateObject
}

function createBaseCveMetadata (id, assignerOrgId, state) {
  const baseRecord = {
    dataType: 'CVE_RECORD',
    dataVersion: '5.0',
    cveMetadata: {
      cveId: id,
      assignerOrgId: assignerOrgId,
      state: state
    }
  }
  return baseRecord
}

CveSchema.statics.newPublishedCve = function (id, assignerOrgId, cnaContainer, sysProvidedCveMetadata = {}, sysProvidedProviderMetadata = {}) {
  const CONSTANTS = getConstants()
  const baseRecord = createBaseCveMetadata(id, assignerOrgId, CONSTANTS.CVE_STATES.PUBLISHED)

  const newCveMetadata = {
    ...baseRecord.cveMetadata,
    ...sysProvidedCveMetadata
  }

  baseRecord.cveMetadata = newCveMetadata

  cnaContainer.providerMetadata = sysProvidedProviderMetadata

  baseRecord.containers = { cna: cnaContainer }

  return baseRecord
}

function createCnaRejectContainer (providerMetadata, rejectedReasons, replacedBy = []) {
  const cnaContainer = {
    providerMetadata: providerMetadata,
    rejectedReasons: rejectedReasons
  }
  if (replacedBy.length > 0) {
    cnaContainer.replacedBy = replacedBy
  }
  return cnaContainer
}

CveSchema.statics.newRejectedCve = function (cveIdObj, reqBody, owningCnaShortName, providerMetadata) {
  const CONSTANTS = getConstants()
  const rejectedRecord = createBaseCveMetadata(cveIdObj.cve_id, cveIdObj.owning_cna, CONSTANTS.CVE_STATES.REJECTED)
  // Might be able to move these into createBaseCveMetadata, but will need to update other calling functions
  rejectedRecord.cveMetadata.dateReserved = new Date(cveIdObj.reserved).toISOString()
  rejectedRecord.cveMetadata.dateUpdated = providerMetadata.dateUpdated
  rejectedRecord.cveMetadata.dateRejected = providerMetadata.dateUpdated

  if (owningCnaShortName) {
    rejectedRecord.cveMetadata.assignerShortName = owningCnaShortName
  }

  const cnaRejectedContainer = createCnaRejectContainer(providerMetadata, reqBody.cnaContainer.rejectedReasons, reqBody.cnaContainer.replacedBy)

  rejectedRecord.containers = { cna: cnaRejectedContainer }

  return rejectedRecord
}

CveSchema.statics.updateCveToRejected = function (id, providerMetadata, record, newCnaContainer) {
  const CONSTANTS = getConstants()

  record.containers.cna = newCnaContainer.cnaContainer // replace cna field on existing record
  record.cveMetadata.dateUpdated = providerMetadata.dateUpdated
  record.containers.cna.providerMetadata = providerMetadata

  // If record is not already rejected, update state and dateRejected
  if (record.cveMetadata.state !== CONSTANTS.CVE_STATES.REJECTED) {
    record.cveMetadata.state = CONSTANTS.CVE_STATES.REJECTED // update state
    record.cveMetadata.dateRejected = providerMetadata.dateUpdated // update dateRejected to dateUpdated, since they're both the same
  }

  if (record.containers.adp) { // check if adp field exists
    delete record.containers.adp
  }
  return record
}

CveSchema.plugin(aggregatePaginate)

// Cursor pagination
CveSchema.plugin(MongoPaging.mongoosePlugin)
const Cve = mongoose.model('Cve', CveSchema)

module.exports = Cve


text:
const mongoose = require('mongoose')

const schema = {
  _id: false,
  cve_year: String,
  ranges: {
    priority: {
      top_id: {
        type: Number,
        default: 0
      },
      start: {
        type: Number,
        default: 0
      },
      end: {
        type: Number,
        default: 20000
      }
    },
    general: {
      top_id: {
        type: Number,
        default: 20000
      },
      start: {
        type: Number,
        default: 20000
      },
      end: {
        type: Number,
        default: 50000000
      }
    }
  }
}

const CveIdRangeSchema = new mongoose.Schema(schema, { collection: 'Cve-Id-Range' })

CveIdRangeSchema.query.byCveYear = function (cveYear) {
  return this.where({ cve_year: cveYear })
}

const CveIdRange = mongoose.model('Cve-Id-Range', CveIdRangeSchema)
module.exports = CveIdRange


text:
const mongoose = require('mongoose')
const aggregatePaginate = require('mongoose-aggregate-paginate-v2')
const MongoPaging = require('mongo-cursor-pagination')

const schema = {
  _id: false,
  cve_id: String,
  cve_year: String,
  state: String,
  owning_cna: String,
  requested_by: {
    cna: String,
    user: String
  },
  reserved: Date,
  time: {
    created: Date,
    modified: Date
  }
}

if (process.env.NODE_ENV === 'test') {
  schema.reserved = { type: Date, default: Date.now }
}

const CveIdSchema = new mongoose.Schema(schema, { collection: 'Cve-Id', timestamps: { createdAt: 'time.created', updatedAt: 'time.modified' } })

CveIdSchema.query.byCveId = function (cveId) {
  return this.where({ cve_id: cveId })
}

CveIdSchema.index({ cve_id: 1 })
CveIdSchema.index({ owning_cna: 1, state: 1 })
CveIdSchema.index({ reserved: 1 })

CveIdSchema.plugin(aggregatePaginate)

// Cursor pagination
CveIdSchema.plugin(MongoPaging.mongoosePlugin)
const CveId = mongoose.model('Cve-Id', CveIdSchema)

module.exports = CveId


title: CVE Services API

description:
The CVE Services API supports automation tooling for the CVE Program. Credentials are     required for most service endpoints. Representatives of     CVE Numbering Authorities (CNAs) should     use one of the methods below to obtain credentials:

If your organization already has an Organizational Administrator (OA) account for the CVE     Services, ask your admin for credentials

Contact your Root (Google,     INCIBE,     JPCERT/CC, or     Red Hat) or     Top-Level Root (CISA ICS     or MITRE) to request credentials

CVE data is to be in the JSON 5.0 CVE Record format. Details of the JSON 5.0 schema are     located here.
    Contact the CVE Services team

summary: Retrieves information about CVE IDs after applying the query parameters as filters (accessible to all registered users)

description:
Access Control

All registered users can access this endpoint

Expected Behavior

Regular, CNA & Admin Users: Retrieves filtered CVE IDs owned by the user's organization

Secretariat: Retrieves filtered CVE IDs owned by any organization

operationId: cveIdGetFiltered

$ref: #/components/parameters/pageQuery

$ref: #/components/parameters/apiEntityHeader

$ref: #/components/parameters/apiUserHeader

$ref: #/components/parameters/apiSecretHeader

description: A filtered list of information about CVE IDs owned by the organization, along with pagination fields if results span multiple pages of data

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Reserves CVE IDs for the organization provided in the short_name query parameter (accessible to CNAs and Secretariat)

description:
Access Control

User must belong to an organization with the CNA or Secretariat role

Expected Behavior

CNA: Reserves CVE IDs for the CNA

Secretariat: Reserves CVE IDs for any organization

operationId: cveIdReserve

$ref: #/components/parameters/amount

description: A list of the newly reserved CVE IDs

description: A partial list of the CVE IDs the IDR service managed to reserve before encountering a case where no more CVE IDs could be reserved

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Retrieves information about the specified CVE ID (accessible to all users)

description:
Access Control

Endpoint is accessible to all

Expected Behavior

Regular, CNA & Admin Users: Retrieves full information about a CVE ID owned by their organization; partial information about a CVE ID owned by other organizations

Unauthenticated Users: Retrieves partial information about a CVE ID
Secretariat: Retrieves full information about a CVE ID owned by any organization

Note - The owning organization of RESERVED CVE IDs is redacted for all users other than those in the owning organization or Secretariat

operationId: cveIdGetSingle

id

in: path

type: string

description: The id of the CVE ID information to retrieve

description: The requested CVE ID information is returned

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Too Many Requests

description: Internal Server Error

summary: Updates information related to the specified CVE ID (accessible to CNAs and Secretariat)

description:
Access Control

User must belong to an organization with the CNA or Secretariat role

Expected Behavior

CNA: Updates information related to a CVE ID owned by the CNA

Secretariat: Updates a CVE ID owned by any organization

operationId: cveIdUpdateSingle

description: The id of the CVE ID to update

$ref: #/components/parameters/org

$ref: #/components/parameters/state

description: The updated CVE ID information is returned

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Creates a CVE-ID-Range for the specified year (accessible to Secretariat)

description:
Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

Secretariat: Creates a CVE-ID-Range for the specified year

operationId: cveIdRangeCreate

year

type: integer

description: The year of the CVE-ID-Range

description: The CVE-ID-Range was created

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Returns a CVE Record by CVE ID (accessible to all users)

description:
Access Control

Endpoint is accessible to all

Expected Behavior

All users: Retrieves the CVE Record specified

operationId: cveGetSingle

id

description: The CVE ID for the Record to be retrieved

description: The requested CVE Record

description: Bad Request

description: Forbidden

description: Not Found

description: Too Many Requests

description: Internal Server Error

summary: Creates a CVE Record from full CVE Record JSON for the specified ID (accessible to Secretariat.)

description:
Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

Secretariat: Creates a CVE Record for any organization

operationId: cveSubmit

description: The CVE ID for the record being submitted

description: The CVE Record created

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Updates a CVE Record from full CVE Record JSON for the specified ID (accessible to Secretariat.)

description:
Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

Secretariat: Updates a CVE Record for any organization

operationId: cveUpdateSingle

description: The CVE ID for the record being updated

description: The updated CVE Record

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Retrieves all CVE Records after applying the query parameters as filters (accessible to Secretariat)

description:
Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

Secretariat: Retrieves all CVE records for all organizations

operationId: cveGetFiltered

$ref: #/components/parameters/cveState

$ref: #/components/parameters/countOnly

$ref: #/components/parameters/assigner

$ref: #/components/parameters/cnaModified

$ref: #/components/parameters/adpShortName

description: A filtered list of CVE Records, along with pagination fields if results span multiple pages of data

description: Bad Request

description: Unauthorized

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Retrieves all CVE Records after applying the query parameters as filters. Uses cursor pagination to paginate results (accessible to Secretariat)

operationId: cveGetFilteredCursor

$ref: #/components/parameters/nextPage

$ref: #/components/parameters/previousPage

$ref: #/components/parameters/limit

description: A filtered list of CVE Records, along with pagination fields if results span multiple pages of data

description: Bad Request

description: Unauthorized

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Creates a CVE Record from CNA Container JSON for the specified ID (accessible to CNAs and Secretariat)

description:
Access Control

User must belong to an organization with the CNA or Secretariat role

Expected Behavior

CNA: Creates CVE Record for a CVE ID owned by their organization

Secretariat: Creates CVE Record for CVE IDs owned by any organization

operationId: cveCnaCreateSingle

description: The CVE ID for the record being created

description: The CVE Record created

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

description: Note: providerMetadata is set by the server. If provided, it will be overwritten.

summary: Updates the CVE Record from CNA Container JSON for the specified ID (accessible to CNAs and Secretariat)

description:
Access Control

User must belong to an organization with the CNA or Secretariat role

Expected Behavior

CNA: Updates a CVE Record for records that are owned by their organization

Secretariat: Updates a CVE Record for records that are owned by any organization

operationId: cveCnaUpdateSingle

description: The CVE ID for which the record is being updated

description: The updated CVE Record

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Creates a rejected CVE Record for the specified ID if no record yet exists (accessible to CNAs and Secretariat)

description:
Access Control

User must belong to an organization with the CNA or Secretariat role

Expected Behavior

CNA: Creates a rejected CVE Record for a record owned by their organization

Secretariat: Creates a rejected CVE Record for a record owned by any organization

operationId: cveCnaCreateReject

description: The CVE ID for the record being rejected

description: The rejected CVE Record

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

description: Note: providerMetadata is set by the server. If provided, it will be overwritten.

summary: Updates an existing CVE Record with a rejected record for the specified ID (accessible to CNAs and Secretariat)

description:
Access Control

User must belong to an organization with the CNA or Secretariat role

Expected Behavior

CNA: Updates a rejected CVE Record for a record owned by their organization

Secretariat: Updates a rejected CVE Record for a record owned by any organization

operationId: cveCnaUpdateReject

description: The CVE ID for the record being rejected

description: The rejected CVE Record

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

description: Note: providerMetadata is set by the server. If provided, it will be overwritten.

summary: Updates the CVE Record from ADP Container JSON for the specified ID (accessible to ADPs and Secretariat)

description:
Access Control

User must belong to an organization with the ADP or Secretariat role

Expected Behavior

ADP: Updates a CVE Record for records that are owned by any organization

Secretariat: Updates a CVE Record for records that are owned by any organization

operationId: cveAdpUpdateSingle

description: The CVE ID for which the record is being updated

description: The updated CVE Record

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

description: Note: providerMetadata is set by the server. If provided, it will be overwritten.

summary: Retrieves all organizations (accessible to Secretariat)

description:
Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

Secretariat: Retrieves information about all organizations

operationId: orgAll

description: Returns information about all organizations, along with pagination fields if results span multiple pages of data

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Creates an organization as specified in the request body (accessible to Secretariat)

description:
Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

Secretariat: Creates an organization

operationId: orgCreateSingle

description: Returns information about the organization created

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Retrieves information about the organization specified by short name or UUID (accessible to all registered users)

description:
Access Control

All registered users can access this endpoint

Expected Behavior

Regular, CNA & Admin Users: Retrieves organization record for the specified shortname or UUID if it is the user's organization

Secretariat: Retrieves information about any organization

identifier

description: The shortname or UUID of the organization

description: Returns the organization information

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Updates information about the organization specified by short name (accessible to Secretariat)

description:
Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

Secretariat: Updates any organization's information

shortname

description: The shortname of the organization

description: Returns information about the organization updated

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Retrieves an organization's CVE ID quota (accessible to all registered users)

description:
Access Control

All registered users can access this endpoint

Expected Behavior

Regular, CNA & Admin Users: Retrieves the CVE ID quota for the user's organization

Secretariat: Retrieves the CVE ID quota for any organization

description: The shortname of the organization

description: Returns the CVE ID quota for an organization

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Retrieves all users for the organization with the specified short name (accessible to all registered users)

description:
Access Control

All registered users can access this endpoint

Expected Behavior

Regular, CNA & Admin Users: Retrieves information about users in the same organization

Secretariat: Retrieves all user information for any organization

description: The shortname of the organization

description: Returns all users for the organization, along with pagination fields if results span multiple pages of data

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Create a user with the provided short name as the owning organization (accessible to Admins and Secretariats)

description:
Access Control

User must belong to an organization with the Secretariat role or be an Admin of the organization

Expected Behavior

Admin User: Creates a user for the Admin's organization

Secretariat: Creates a user for any organization

description: The shortname of the organization

description: Returns the new user information (with the secret)

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Retrieves information about a user for the specified username and organization short name (accessible to all registered users)

description:
Access Control

All registered users can access this endpoint

Expected Behavior

Regular, CNA & Admin Users: Retrieves information about a user in the same organization

Secretariat: Retrieves any user's information

description: The shortname of the organization

username

description: The username of the user

description: Returns information about the specified user

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Updates information about a user for the specified username and organization shortname (accessible to all registered users)

description:
Access Control

All registered users can access this endpoint

Expected Behavior

Regular User: Updates the user's own information. Only name fields may be changed.

Admin User: Updates information about a user in the Admin's organization. Allowed to change all fields except org_short_name.

Secretariat: Updates information about a user in any organization. Allowed to change all fields.

shortname

description: The shortname of the organization

username

description: The username of the user

description: Returns the updated user information

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Reset the API key for a user (accessible to all registered users)

description:
Access Control

All registered users can access this endpoint

Expected Behavior

Regular User: Resets user's own API secret

Admin User: Resets any user's API secret in the Admin's organization

Secretariat: Resets any user's API secret

shortname

description: The shortname of the organization

username

description: The username of the user

description: Returns the new API key

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Retrieves information about all registered users (accessible to Secretariat)

description:
Access Control

 User must belong to an organization with the Secretariat role

Expected Behavior

Secretariat: Retrieves information about all users for all organizations

description: Returns all users, along with pagination fields if results span multiple pages of data.

description: Bad Request

description: Not Authenticated

description: Forbidden

description: Not Found

description: Internal Server Error

summary: Checks that the system is running (accessible to all users)

description:
Access Control

Endpoint is accessible to all

Expected Behavior

Returns a 200 response code when CVE Services are running

description: Returns a 200 response code

active

description: The new active state for the user entry.  Accepted values are 1, true, or yes to indicate true, and 0, false, or no to indicate false

description: Add an active role to the organization

description: Remove an active role from the organization

description: Add an active role to the user

description: Remove an active role from the user

CVE-API-ORG

description: The shortname for the organization associated with the user requesting authentication

CVE-API-USER

description: The username for the account making the request

CVE-API-KEY

description: The user's API key

amount

description: Quantity of CVE IDs to reserve

assigner

description: Filter by assigner org UUID

description: Filter by assignerShortName

description: Required when amount is greater than one, determines whether the reserved CVE IDs should be sequential or non-sequential

description: Get count of records that match query. Accepted values are 1, true, or yes to indicate true, and 0, false, or no to indicate false

description: Key returned by a GET /cve_cursor call that must be used to get the next page of results in a subsequent call

description: Key returned by a GET /cve_cursor call that must be used to get the previous page of results in a subsequent call

limit

description: CVE records to return per page. Must be between 1-500.

description: Only get CVE records with cnaContainers that have been modified/created within the set time_modified range. Requires at least one time_modified parameter set

description: Only get CVE records that have an adpContainer owned by this org.

state

description: Filter by state

description: The year the CVE IDs will be reserved for (i.e., 1999, ..., currentYear + 1)

description: Filter by state

description: Filter by the year of the CVE IDs

description:
Most recent reserved timestamp to retrieve. Include with all requests potentially returning multiple pages of CVE IDs to avoid issues if new IDs are reserved during use.

 Timestamp format : yyyy-MM-ddTHH:mm:ssZZZZ

description:
Earliest CVE ID reserved timestamp to retrieve

 Timestamp format : yyyy-MM-ddTHH:mm:ssZZZZ

description:
Most recent modified timestamp to retrieve. Include with all requests using a time_modified.gt filter potentially returning multiple pages of CVE IDs. This will avoid issues if IDs are reserved or modified during use.

 Timestamp format : yyyy-MM-ddTHH:mm:ssZZZZ

description:
Earliest CVE ID modified timestamp to retrieve

 Timestamp format : yyyy-MM-ddTHH:mm:ssZZZZ

description:
Most recent CVE record modified timestamp to retrieve

 Timestamp format : yyyy-MM-ddTHH:mm:ssZZZZ

description:
Earliest CVE record modified timestamp to retrieve

 Timestamp format : yyyy-MM-ddTHH:mm:ssZZZZ

description: The new number of CVE IDs the organization is allowed to have in the RESERVED state at one time

name

description: The new name for the organization

name.first

description: The new first name for the user entry

name.last

description: The new last name for the user entry

name.middle

description: The new middle name for the user entry

name.suffix

description: The new suffix for the user entry

description: The new shortname for the organization

description: The new username for the user, preferably the user's email address. Must be 3-128 characters in length; allowed characters are alphanumeric and -_@.

org

description: The shortname of the new owning_cna for the CVE ID

description: The new organization for the user

page

description: The current page in the paginator

description: The CNA that will own the reserved CVE IDs

shortname

description: The new shortname for the organization

state

description: The new state for the CVE ID

description: string

message: string


text:
require('dotenv').config()
const CveId = require('../src/model/cve-id')
const logger = require('../src/middleware/logger')
const getConstants = require('../src/constants').getConstants
const errors = require('../src/controller/cve-id.controller/error')
const error = new errors.CveIdControllerError()
const reservedByOther = []
const CONSOLE_OUTPUT = false

// DONE *
async function reserveCveId (req, res) {
  const requester = req.ctx.user
  const cnaShortName = req.ctx.org
  let batchType
  let amount
  let shortName
  let year
  const orgRepo = req.ctx.repositories.getOrgRepository()

  Object.keys(req.ctx.query).forEach(k => {
    const key = k.toLowerCase()

    if (key === 'amount') {
      amount = req.ctx.query.amount
    } else if (key === 'batch_type') {
      batchType = req.ctx.query.batch_type
    } else if (key === 'short_name') {
      shortName = req.ctx.query.short_name
    } else if (key === 'cve_year') {
      year = req.ctx.query.cve_year
    }
  })

  const result = await orgRepo.findOneByShortName(shortName)
  if (!result) {
    return res.status(403).json(error.orgDne(shortName))
  }

  const payload = await getPayload(req, result)
  if (amount > payload.available) {
    return res.status(403).json(error.overIdQuota(payload))
  }

  if (batchType === 'non-sequential' || batchType === 'nonsequential') {
    await nonSequentialReservation(year, amount, shortName, cnaShortName, requester, payload.available, res, req)
  }
}

// DONE *
async function nonSequentialReservation (year, amount, shortName, cnaShortName, requester, availableIds, res, req) {
  const cveIdRepo = req.ctx.repositories.getCveIdRepository()
  const cveIdRangeRepo = req.ctx.repositories.getCveIdRangeRepository()
  const reqUUID = req.ctx.uuid
  const CONSTANTS = getConstants()
  let isFull = false
  let available
  amount = parseInt(amount)
  let availableLimit = Math.max(3 * amount, CONSTANTS.DEFAULT_AVAILABLE_POOL)
  let result = await cveIdRangeRepo.findOne({ cve_year: year })

  if (CONSOLE_OUTPUT) {
    console.log('amount = ' + amount)
    console.log('availableLimit = ' + availableLimit)
  }

  // Cve Id Range for 'year' does not exists
  if (!result) {
    logger.info('CVE IDs for year ' + year + ' cannot be reserved at this time.')
    res.header(CONSTANTS.QUOTA_HEADER, availableIds)
    return res.status(403).json(error.cannotReserveForYear(year))
  }

  if (CONSOLE_OUTPUT) {
    console.log('available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
  }

  available = await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit }) // get available ids

  if (CONSOLE_OUTPUT) {
    console.log('available count (capped) = ' + available.length) // get available ids
  }

  // Case 1: Not enough IDs in the 'AVAILABLE' pool
  if (available.length < availableLimit) {
    result = await incrementNonSequentialPool(availableLimit, result.ranges.general.end, result.ranges.general.top_id, amount, available.length, year, cveIdRangeRepo)
    isFull = result.isFull

    if (isFull) {
      logger.error('The cve id non-sequential block is full for year ' + year + '. No more sequential ids can be reserved at this time.')
      res.header(CONSTANTS.QUOTA_HEADER, availableIds)
      return res.status(403).json(error.yearRangeFull(year))
    }

    await allocateAvailableCveIds(result.ids, year, cveIdRepo) // Pool was incremented. Create 'AVAILABLE' cve ids.

    if (CONSOLE_OUTPUT) {
      console.log('available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
    }

    available = await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit }) // get available ids

    if (CONSOLE_OUTPUT) {
      console.log('available count (capped) = ' + available.length) // get available ids
    }
  }

  // Case 2: Enough IDs in the 'AVAILABLE' pool
  const orgRepo = req.ctx.repositories.getOrgRepository()
  const userRepo = req.ctx.repositories.getUserRepository()
  let index
  let counter = 0
  let dummyCounter = 0
  const cveIdDocuments = []
  const cveIdDocumentsUUID = []
  const owningOrgUUID = await orgRepo.getOrgUUID(shortName)
  const orgUUID = await orgRepo.getOrgUUID(cnaShortName)
  const requesterUUID = (await userRepo.findOneByUserNameAndOrgUUID(requester, orgUUID)).UUID

  if (CONSOLE_OUTPUT) {
    console.log('counter = ' + counter)
    console.log('dummyCounter = ' + dummyCounter)
  }

  while ((counter < amount) && !isFull) {
    index = getRandomInt(0, available.length) // get random index in the available array
    result = await reserveNonSequentialCveId(index, available, year, shortName, cnaShortName, requester, owningOrgUUID, orgUUID, requesterUUID, dummyCounter, cveIdRepo)

    if (CONSOLE_OUTPUT && !result.isReserved) {
      console.log('isReserved: ' + result.isReserved)
    }

    if (result.isReserved) {
      cveIdDocuments.push(result.cveId) // add reserved cve id to the array of reserved cve ids
      cveIdDocumentsUUID.push(result.cveIdUUID) // add reserved cve id UUID to the array of reserved cve ids
      available.splice(index, 1) // remove reserved cve id from the 'AVAILABLE' pool
      counter++

      if (CONSOLE_OUTPUT) {
        console.log('cveIdDocuments: ' + cveIdDocuments.length + ', available: ' + available.length + ', counter = ' + counter)
      }
    } else {
      if (CONSOLE_OUTPUT) {
        console.log('available = ' + available.length)
        console.log('available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
      }

      available = await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit }) // get available ids

      if (CONSOLE_OUTPUT) {
        console.log('available count (capped) = ' + available.length) // get available ids
      }

      // ---- Reserve 20 cve ids ----
      dummyCounter++
      const tempOwningOrgUUID = await orgRepo.getOrgUUID('mitre')
      const tempOrgUUID = tempOwningOrgUUID
      const tempRequesterUUID = (await userRepo.findOneByUserNameAndOrgUUID('cpadro', tempOrgUUID)).UUID

      for (let i = 0; i < 20; i++) {
        const r = getRandomInt(0, available.length)
        const temp = await reserveNonSequentialCveId(r, available, year, 'mitre', 'mitre', 'cpadro', tempOwningOrgUUID, tempOrgUUID, tempRequesterUUID, dummyCounter, cveIdRepo)
        available.splice(r, 1) // remove reserved cve id from the 'AVAILABLE' pool
        reservedByOther.push(temp.cveId.cve_id)

        if (CONSOLE_OUTPUT) {
          console.log('* available = ' + available.length)
          console.log('* available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
          console.log('* available count (capped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit })).length) // get available ids
        }
      }
      dummyCounter--
      // ----------------------------

      if (CONSOLE_OUTPUT) {
        console.log('available = ' + available.length)
        console.log('available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
      }

      available = await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit }) // get available ids

      if (CONSOLE_OUTPUT) {
        console.log('available count (capped) = ' + available.length) // get available ids
      }

      availableLimit = Math.max(3 * (amount - counter), CONSTANTS.DEFAULT_AVAILABLE_POOL) // recalculate the available limit since some ids might have been reserved

      if (CONSOLE_OUTPUT) {
        console.log('availableLimit = ' + availableLimit)
      }

      // Case 1: Not enough IDs in the 'AVAILABLE' pool
      if (available.length < availableLimit) {
        result = await cveIdRangeRepo.findOne({ cve_year: year })
        result = await incrementNonSequentialPool(availableLimit, result.ranges.general.end, result.ranges.general.top_id, amount - counter, available.length, year, cveIdRangeRepo)
        isFull = result.isFull

        if (isFull) {
          logger.info({ message: 'Only ' + counter + ' cve ids were reserved because there are not enough ids in the CVE ID non-sequential block. Non-sequential CVE IDs were reserved for \'' + shortName + '\' org on behalf of \'' + cnaShortName + '\' org.', cve_ids: cveIdDocumentsUUID })
          res.header(CONSTANTS.QUOTA_HEADER, availableIds - counter)
          return res.status(206).json(error.reservedPartialAmount(counter, cveIdDocuments))
        }

        await allocateAvailableCveIds(result.ids, year, cveIdRepo) // Pool was incremented. Create 'AVAILABLE' cve ids.

        if (CONSOLE_OUTPUT) {
          console.log('available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
        }

        available = await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit }) // get available ids

        if (CONSOLE_OUTPUT) {
          console.log('available count (capped) = ' + available.length) // get available ids
        }
      }
    }

    dummyCounter++

    if (CONSOLE_OUTPUT) {
      console.log('dummyCounter = ' + dummyCounter)
    }
  }

  if (!isFull) {
    const payload = {
      action: 'nonsequential_reservation',
      change: 'Non-sequential CVE IDs were reserved for \'' + shortName + '\' org on behalf of \'' + cnaShortName + '\' org.',
      req_UUID: reqUUID,
      org_UUID: orgUUID,
      user_UUID: requesterUUID,
      cve_ids: cveIdDocumentsUUID
    }

    logger.info(JSON.stringify(payload))
    res.header(CONSTANTS.QUOTA_HEADER, availableIds - amount)
    return res.status(200).json({ cve_ids: cveIdDocuments })
  }
}

// DONE
async function incrementNonSequentialPool (availableLimit, end, top, amount, available, year, cveIdRR) {
  let increment = availableLimit - available + amount // the amount is the increment amount
  const cveIdRangeRepo = cveIdRR
  const endRange = parseInt(end)
  const topId = parseInt(top)

  if (CONSOLE_OUTPUT) {
    console.log('increment = ' + increment)
    console.log('endRange = ' + endRange)
    console.log('topId = ' + topId)
  }

  // Cap increment to end of general CVE ID block
  if (endRange < (topId + increment)) {
    increment = endRange - topId
  }

  if (CONSOLE_OUTPUT) {
    console.log('increment = ' + increment)
  }

  const result = {
    isFull: false,
    ids: null
  }

  if (increment > 0) {
    let r = await cveIdRangeRepo.findOneAndUpdate({ $and: [{ cve_year: year }, { 'ranges.general.end': { $gte: increment } }] }, { $inc: { 'ranges.general.top_id': increment } }, { new: true })

    if (CONSOLE_OUTPUT) {
      console.log('top_id = ' + r.ranges.general.top_id)
    }

    // Cap increment because it went over end of general block
    if (r.ranges.general.top_id > endRange) {
      if (CONSOLE_OUTPUT) {
        console.log('increment = ' + increment)
      }

      increment = increment - (r.ranges.general.top_id - endRange) // adjusting real increment
      r = await cveIdRangeRepo.findOneAndUpdate({ cve_year: year }, { $set: { 'ranges.general.top_id': endRange } }, { new: true }) // Cap top_id to end of the block

      if (CONSOLE_OUTPUT) {
        console.log('top_id = ' + r.ranges.general.top_id)
      }
    }

    if (CONSOLE_OUTPUT) {
      console.log('increment = ' + increment)
    }

    if (increment > 0) {
      result.ids = generateSequentialIds(year, r.ranges.general.top_id, increment)
    } else if (available < amount) { // You hit the end of the boundary
      result.isFull = true
    }
  } else if (available < amount) { // You hit the end of the boundary
    result.isFull = true
  }

  return result
}

// DONE *
async function allocateAvailableCveIds (ids, year, cveIdR) {
  if (ids) {
    const cveIdRepo = cveIdR
    const cveIdDocuments = []
    let cveId

    ids.forEach(id => {
      cveId = new CveId()
      cveId.cve_id = id
      cveId.cve_year = year
      cveId.state = 'AVAILABLE'
      cveId.owning_cna = 'N/A' // the org who gets assigned the reserved CVE IDs
      cveId.reserved = Date.now()
      cveId.requested_by = {
        cna: 'N/A', // the org who requested the CVE IDs
        user: 'N/A'
      }

      cveIdDocuments.push(cveId)
    })

    if (CONSOLE_OUTPUT) {
      console.log('Allocated ids: [' + cveIdDocuments[0].cve_id + ', ... , ' + cveIdDocuments[cveIdDocuments.length - 1].cve_id + ']')
      console.log('Allocated count = ' + cveIdDocuments.length)
    }

    await cveIdRepo.insertMany(cveIdDocuments) // Save the 'AVAILABLE' cve ids
  }
}

// DONE *
async function reserveNonSequentialCveId (index, available, year, shortName, cnaShortName, requester, owningOrgUUID, orgUUID, requesterUUID, dummyCounter, cveIdR) {
  // Update available Cve Id
  const cveIdUUID = new CveId()
  const cveId = new CveId()
  const cveIdRepo = cveIdR
  cveIdUUID.cve_id = cveId.cve_id = available[index].cve_id
  cveIdUUID.cve_year = cveId.cve_year = year
  cveIdUUID.state = cveId.state = 'RESERVED'
  cveIdUUID.owning_cna = owningOrgUUID
  cveId.owning_cna = shortName // the org who gets assigned the reserved CVE IDs
  cveIdUUID.reserved = cveId.reserved = Date.now()
  cveIdUUID.requested_by = {
    cna: orgUUID, // the org who requested the CVE IDs
    user: requesterUUID
  }
  cveId.requested_by = {
    cna: cnaShortName, // the org who requested the CVE IDs
    user: requester
  }

  const result = {
    cveId: cveId,
    cveIdUUID: cveIdUUID
  }

  if (dummyCounter === 5) {
    result.isReserved = await cveIdRepo.findOneAndUpdate({ cve_id: 'CVE-2888-5465', state: 'AVAILABLE' }, cveIdUUID, { new: true })
  } else {
    result.isReserved = await cveIdRepo.findOneAndUpdate({ cve_id: cveIdUUID.cve_id, state: 'AVAILABLE' }, cveIdUUID, { new: true })
  }

  return result
}

// DONE *
async function getPayload (req, org) {
  const payload = {
    id_quota: org.policies.id_quota
  }
  const cveIdRepo = req.ctx.repositories.getCveIdRepository()

  const result = await cveIdRepo.countDocuments({ owning_cna: org.UUID, state: 'RESERVED' })
  payload.total_reserved = result
  payload.available = (payload.id_quota - payload.total_reserved)

  return payload
}

// DONE
function generateSequentialIds (year, topId, increment) {
  const start = topId - increment + 1 // before the pool 'AVAILABLE' pool increment
  const end = topId // after the 'AVAILABLE' pool increment
  const ids = []

  if (CONSOLE_OUTPUT) {
    console.log('start = ' + start)
    console.log('end = ' + end)
  }

  for (let i = start; i < end + 1; i++) {
    ids.push('CVE-' + year + '-' + String(i).padStart(4, '0'))
  }

  if (CONSOLE_OUTPUT) {
    console.log('Generated ids: [' + ids[0] + ', ... , ' + ids[ids.length - 1] + ']')
  }

  return ids
}

// DONE
function getRandomInt (min, max) {
  min = Math.ceil(min)
  max = Math.floor(max)

  return Math.floor(Math.random() * (max - min) + min) // The maximum is exclusive and the minimum is inclusive
}

module.exports = {
  CVEID_RESERVE: reserveCveId,
  reservedByOther: reservedByOther
}


text:
require('dotenv').config()
const CveId = require('../src/model/cve-id')
const logger = require('../src/middleware/logger')
const getConstants = require('../src/constants').getConstants
const errors = require('../src/controller/cve-id.controller/error')
const error = new errors.CveIdControllerError()
const reservedByOther = []
const CONSOLE_OUTPUT = false

// DONE *
async function reserveCveId (req, res) {
  const requester = req.ctx.user
  const cnaShortName = req.ctx.org
  let batchType
  let amount
  let shortName
  let year
  const orgRepo = req.ctx.repositories.getOrgRepository()

  Object.keys(req.ctx.query).forEach(k => {
    const key = k.toLowerCase()

    if (key === 'amount') {
      amount = req.ctx.query.amount
    } else if (key === 'batch_type') {
      batchType = req.ctx.query.batch_type
    } else if (key === 'short_name') {
      shortName = req.ctx.query.short_name
    } else if (key === 'cve_year') {
      year = req.ctx.query.cve_year
    }
  })

  const result = await orgRepo.findOneByShortName(shortName)
  if (!result) {
    return res.status(403).json(error.orgDne(shortName))
  }

  const payload = await getPayload(req, result)
  if (amount > payload.available) {
    return res.status(403).json(error.overIdQuota(payload))
  }

  if (batchType === 'non-sequential' || batchType === 'nonsequential') {
    await nonSequentialReservation(year, amount, shortName, cnaShortName, requester, payload.available, res, req)
  }
}

// DONE *
async function nonSequentialReservation (year, amount, shortName, cnaShortName, requester, availableIds, res, req) {
  const cveIdRepo = req.ctx.repositories.getCveIdRepository()
  const cveIdRangeRepo = req.ctx.repositories.getCveIdRangeRepository()
  const reqUUID = req.ctx.uuid
  const CONSTANTS = getConstants()
  let isFull = false
  let available
  amount = parseInt(amount)
  let availableLimit = Math.max(3 * amount, CONSTANTS.DEFAULT_AVAILABLE_POOL)
  let result = await cveIdRangeRepo.findOne({ cve_year: year })

  if (CONSOLE_OUTPUT) {
    console.log('amount = ' + amount)
    console.log('availableLimit = ' + availableLimit)
  }

  // Cve Id Range for 'year' does not exists
  if (!result) {
    logger.info('CVE IDs for year ' + year + ' cannot be reserved at this time.')
    res.header(CONSTANTS.QUOTA_HEADER, availableIds)
    return res.status(403).json(error.cannotReserveForYear(year))
  }

  if (CONSOLE_OUTPUT) {
    console.log('available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
  }

  available = await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit }) // get available ids

  if (CONSOLE_OUTPUT) {
    console.log('available count (capped) = ' + available.length) // get available ids
  }

  // Case 1: Not enough IDs in the 'AVAILABLE' pool
  if (available.length < availableLimit) {
    result = await incrementNonSequentialPool(availableLimit, result.ranges.general.end, result.ranges.general.top_id, amount, available.length, year, cveIdRangeRepo)
    isFull = result.isFull

    if (isFull) {
      logger.error('The cve id non-sequential block is full for year ' + year + '. No more sequential ids can be reserved at this time.')
      res.header(CONSTANTS.QUOTA_HEADER, availableIds)
      return res.status(403).json(error.yearRangeFull(year))
    }

    await allocateAvailableCveIds(result.ids, year, cveIdRepo) // Pool was incremented. Create 'AVAILABLE' cve ids.

    if (CONSOLE_OUTPUT) {
      console.log('available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
    }

    available = await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit }) // get available ids

    if (CONSOLE_OUTPUT) {
      console.log('available count (capped) = ' + available.length) // get available ids
    }
  }

  // Case 2: Enough IDs in the 'AVAILABLE' pool
  const orgRepo = req.ctx.repositories.getOrgRepository()
  const userRepo = req.ctx.repositories.getUserRepository()
  let index
  let counter = 0
  let dummyCounter = 0
  const cveIdDocuments = []
  const cveIdDocumentsUUID = []
  const owningOrgUUID = await orgRepo.getOrgUUID(shortName)
  const orgUUID = await orgRepo.getOrgUUID(cnaShortName)
  const requesterUUID = (await userRepo.findOneByUserNameAndOrgUUID(requester, orgUUID)).UUID

  if (CONSOLE_OUTPUT) {
    console.log('counter = ' + counter)
    console.log('dummyCounter = ' + dummyCounter)
  }

  while ((counter < amount) && !isFull) {
    index = getRandomInt(0, available.length) // get random index in the available array
    result = await reserveNonSequentialCveId(index, available, year, shortName, cnaShortName, requester, owningOrgUUID, orgUUID, requesterUUID, dummyCounter, cveIdRepo)

    if (CONSOLE_OUTPUT && !result.isReserved) {
      console.log('isReserved: ' + result.isReserved)
    }

    if (result.isReserved) {
      cveIdDocuments.push(result.cveId) // add reserved cve id to the array of reserved cve ids
      cveIdDocumentsUUID.push(result.cveIdUUID) // add reserved cve id UUID to the array of reserved cve ids
      available.splice(index, 1) // remove reserved cve id from the 'AVAILABLE' pool
      counter++

      if (CONSOLE_OUTPUT) {
        console.log('cveIdDocuments: ' + cveIdDocuments.length + ', available: ' + available.length + ', counter = ' + counter)
      }
    } else {
      if (CONSOLE_OUTPUT) {
        console.log('available = ' + available.length)
        console.log('available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
      }

      available = await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit }) // get available ids

      if (CONSOLE_OUTPUT) {
        console.log('available count (capped) = ' + available.length) // get available ids
      }

      // ---- Reserve 4 cve ids ----
      dummyCounter++
      const tempOwningOrgUUID = await orgRepo.getOrgUUID('mitre')
      const tempOrgUUID = tempOwningOrgUUID
      const tempRequesterUUID = (await userRepo.findOneByUserNameAndOrgUUID('cpadro', tempOrgUUID)).UUID

      for (let i = 0; i < 4; i++) {
        const r = getRandomInt(0, available.length)
        const temp = await reserveNonSequentialCveId(r, available, year, 'mitre', 'mitre', 'cpadro', tempOwningOrgUUID, tempOrgUUID, tempRequesterUUID, dummyCounter, cveIdRepo)
        available.splice(r, 1) // remove reserved cve id from the 'AVAILABLE' pool
        reservedByOther.push(temp.cveId.cve_id)

        if (CONSOLE_OUTPUT) {
          console.log('* available = ' + available.length)
          console.log('* available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
          console.log('* available count (capped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit })).length) // get available ids
        }
      }
      dummyCounter--
      // ----------------------------

      if (CONSOLE_OUTPUT) {
        console.log('available = ' + available.length)
        console.log('* available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
      }

      available = await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit }) // get available ids

      if (CONSOLE_OUTPUT) {
        console.log('available count (capped) = ' + available.length) // get available ids
      }

      availableLimit = Math.max(3 * (amount - counter), CONSTANTS.DEFAULT_AVAILABLE_POOL) // recalculate the available limit since some ids might have been reserved

      if (CONSOLE_OUTPUT) {
        console.log('availableLimit = ' + availableLimit)
      }

      // Case 1: Not enough IDs in the 'AVAILABLE' pool
      if (available.length < availableLimit) {
        result = await cveIdRangeRepo.findOne({ cve_year: year })
        result = await incrementNonSequentialPool(availableLimit, result.ranges.general.end, result.ranges.general.top_id, amount - counter, available.length, year, cveIdRangeRepo)
        isFull = result.isFull

        if (isFull) {
          logger.info({ message: 'Only ' + counter + ' cve ids were reserved because there are not enough ids in the CVE ID non-sequential block. Non-sequential CVE IDs were reserved for \'' + shortName + '\' org on behalf of \'' + cnaShortName + '\' org.', cve_ids: cveIdDocumentsUUID })
          res.header(CONSTANTS.QUOTA_HEADER, availableIds - counter)
          return res.status(206).json(error.reservedPartialAmount(counter, cveIdDocuments))
        }

        await allocateAvailableCveIds(result.ids, year, cveIdRepo) // Pool was incremented. Create 'AVAILABLE' cve ids.

        if (CONSOLE_OUTPUT) {
          console.log('available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
        }

        available = await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit }) // get available ids

        if (CONSOLE_OUTPUT) {
          console.log('available count (capped) = ' + available.length) // get available ids
        }
      }
    }

    dummyCounter++

    if (CONSOLE_OUTPUT) {
      console.log('dummyCounter = ' + dummyCounter)
    }
  }

  if (!isFull) {
    const payload = {
      action: 'nonsequential_reservation',
      change: 'Non-sequential CVE IDs were reserved for \'' + shortName + '\' org on behalf of \'' + cnaShortName + '\' org.',
      req_UUID: reqUUID,
      org_UUID: orgUUID,
      user_UUID: requesterUUID,
      cve_ids: cveIdDocumentsUUID
    }

    logger.info(JSON.stringify(payload))
    res.header(CONSTANTS.QUOTA_HEADER, availableIds - amount)
    return res.status(200).json({ cve_ids: cveIdDocuments })
  }
}

// DONE
async function incrementNonSequentialPool (availableLimit, end, top, amount, available, year, cveIdRR) {
  let increment = availableLimit - available + amount // the amount is the increment amount
  const cveIdRangeRepo = cveIdRR
  const endRange = parseInt(end)
  const topId = parseInt(top)

  if (CONSOLE_OUTPUT) {
    console.log('increment = ' + increment)
    console.log('endRange = ' + endRange)
    console.log('topId = ' + topId)
  }

  // Cap increment to end of general CVE ID block
  if (endRange < (topId + increment)) {
    increment = endRange - topId
  }

  if (CONSOLE_OUTPUT) {
    console.log('increment = ' + increment)
  }

  const result = {
    isFull: false,
    ids: null
  }

  if (increment > 0) {
    let r = await cveIdRangeRepo.findOneAndUpdate({ $and: [{ cve_year: year }, { 'ranges.general.end': { $gte: increment } }] }, { $inc: { 'ranges.general.top_id': increment } }, { new: true })

    if (CONSOLE_OUTPUT) {
      console.log('top_id = ' + r.ranges.general.top_id)
    }

    // Cap increment because it went over end of general block
    if (r.ranges.general.top_id > endRange) {
      if (CONSOLE_OUTPUT) {
        console.log('increment = ' + increment)
      }

      increment = increment - (r.ranges.general.top_id - endRange) // adjusting real increment
      r = await cveIdRangeRepo.findOneAndUpdate({ cve_year: year }, { $set: { 'ranges.general.top_id': endRange } }, { new: true }) // Cap top_id to end of the block

      if (CONSOLE_OUTPUT) {
        console.log('top_id = ' + r.ranges.general.top_id)
      }
    }

    if (CONSOLE_OUTPUT) {
      console.log('increment = ' + increment)
    }

    if (increment > 0) {
      result.ids = generateSequentialIds(year, r.ranges.general.top_id, increment)
    } else if (available < amount) { // You hit the end of the boundary
      result.isFull = true
    }
  } else if (available < amount) { // You hit the end of the boundary
    result.isFull = true
  }

  return result
}

// DONE *
async function allocateAvailableCveIds (ids, year, cveIdR) {
  if (ids) {
    const cveIdRepo = cveIdR
    const cveIdDocuments = []
    let cveId

    ids.forEach(id => {
      cveId = new CveId()
      cveId.cve_id = id
      cveId.cve_year = year
      cveId.state = 'AVAILABLE'
      cveId.owning_cna = 'N/A' // the org who gets assigned the reserved CVE IDs
      cveId.reserved = Date.now()
      cveId.requested_by = {
        cna: 'N/A', // the org who requested the CVE IDs
        user: 'N/A'
      }

      cveIdDocuments.push(cveId)
    })

    if (CONSOLE_OUTPUT) {
      console.log('Allocated ids: [' + cveIdDocuments[0].cve_id + ', ... , ' + cveIdDocuments[cveIdDocuments.length - 1].cve_id + ']')
      console.log('Allocated count = ' + cveIdDocuments.length)
    }

    await cveIdRepo.insertMany(cveIdDocuments) // Save the 'AVAILABLE' cve ids
  }
}

// DONE *
async function reserveNonSequentialCveId (index, available, year, shortName, cnaShortName, requester, owningOrgUUID, orgUUID, requesterUUID, dummyCounter, cveIdR) {
  // Update available Cve Id
  const cveIdUUID = new CveId()
  const cveId = new CveId()
  const cveIdRepo = cveIdR
  cveIdUUID.cve_id = cveId.cve_id = available[index].cve_id
  cveIdUUID.cve_year = cveId.cve_year = year
  cveIdUUID.state = cveId.state = 'RESERVED'
  cveIdUUID.owning_cna = owningOrgUUID
  cveId.owning_cna = shortName // the org who gets assigned the reserved CVE IDs
  cveIdUUID.reserved = cveId.reserved = Date.now()
  cveIdUUID.requested_by = {
    cna: orgUUID, // the org who requested the CVE IDs
    user: requesterUUID
  }
  cveId.requested_by = {
    cna: cnaShortName, // the org who requested the CVE IDs
    user: requester
  }

  const result = {
    cveId: cveId,
    cveIdUUID: cveIdUUID
  }

  if (dummyCounter === 3) {
    result.isReserved = await cveIdRepo.findOneAndUpdate({ cve_id: 'CVE-2888-5465', state: 'AVAILABLE' }, cveIdUUID, { new: true })
  } else {
    result.isReserved = await cveIdRepo.findOneAndUpdate({ cve_id: cveIdUUID.cve_id, state: 'AVAILABLE' }, cveIdUUID, { new: true })
  }

  return result
}

// DONE *
async function getPayload (req, org) {
  const payload = {
    id_quota: org.policies.id_quota
  }
  const cveIdRepo = req.ctx.repositories.getCveIdRepository()

  const result = await cveIdRepo.countDocuments({ owning_cna: org.UUID, state: 'RESERVED' })
  payload.total_reserved = result
  payload.available = (payload.id_quota - payload.total_reserved)

  return payload
}

// DONE
function generateSequentialIds (year, topId, increment) {
  const start = topId - increment + 1 // before the pool 'AVAILABLE' pool increment
  const end = topId // after the 'AVAILABLE' pool increment
  const ids = []

  if (CONSOLE_OUTPUT) {
    console.log('start = ' + start)
    console.log('end = ' + end)
  }

  for (let i = start; i < end + 1; i++) {
    ids.push('CVE-' + year + '-' + String(i).padStart(4, '0'))
  }

  if (CONSOLE_OUTPUT) {
    console.log('Generated ids: [' + ids[0] + ', ... , ' + ids[ids.length - 1] + ']')
  }

  return ids
}

// DONE
function getRandomInt (min, max) {
  min = Math.ceil(min)
  max = Math.floor(max)

  return Math.floor(Math.random() * (max - min) + min) // The maximum is exclusive and the minimum is inclusive
}

module.exports = {
  CVEID_RESERVE: reserveCveId,
  reservedByOther: reservedByOther
}


text:
require('dotenv').config()
const CveId = require('../src/model/cve-id')
const logger = require('../src/middleware/logger')
const getConstants = require('../src/constants').getConstants
const errors = require('../src/controller/cve-id.controller/error')
const error = new errors.CveIdControllerError()
const reservedByOther = []
const CONSOLE_OUTPUT = false

async function reserveCveId (req, res) {
  const requester = req.ctx.user
  const cnaShortName = req.ctx.org
  let batchType
  let amount
  let shortName
  let year
  const orgRepo = req.ctx.repositories.getOrgRepository()

  Object.keys(req.ctx.query).forEach(k => {
    const key = k.toLowerCase()

    if (key === 'amount') {
      amount = req.ctx.query.amount
    } else if (key === 'batch_type') {
      batchType = req.ctx.query.batch_type
    } else if (key === 'short_name') {
      shortName = req.ctx.query.short_name
    } else if (key === 'cve_year') {
      year = req.ctx.query.cve_year
    }
  })

  const result = await orgRepo.findOneByShortName(shortName)
  if (!result) {
    return res.status(403).json(error.orgDne(shortName))
  }

  const payload = await getPayload(req, result)
  if (amount > payload.available) {
    return res.status(403).json(error.overIdQuota(payload))
  }

  if (batchType === 'non-sequential' || batchType === 'nonsequential') {
    await nonSequentialReservation(year, amount, shortName, cnaShortName, requester, payload.available, res, req)
  }
}

async function nonSequentialReservation (year, amount, shortName, cnaShortName, requester, availableIds, res, req) {
  const cveIdRepo = req.ctx.repositories.getCveIdRepository()
  const cveIdRangeRepo = req.ctx.repositories.getCveIdRangeRepository()
  const orgRepo = req.ctx.repositories.getOrgRepository()
  const userRepo = req.ctx.repositories.getUserRepository()
  const reqUUID = req.ctx.uuid
  const CONSTANTS = getConstants()
  let isFull = false
  let available
  amount = parseInt(amount)
  let availableLimit = Math.max(3 * amount, CONSTANTS.DEFAULT_AVAILABLE_POOL)
  let result = await cveIdRangeRepo.findOne({ cve_year: year })

  if (CONSOLE_OUTPUT) {
    console.log('amount = ' + amount)
    console.log('availableLimit = ' + availableLimit)
  }

  // Cve Id Range for 'year' does not exists
  if (!result) {
    logger.info('CVE IDs for year ' + year + ' cannot be reserved at this time.')
    res.header(CONSTANTS.QUOTA_HEADER, availableIds)
    return res.status(403).json(error.cannotReserveForYear(year))
  }

  if (CONSOLE_OUTPUT) {
    console.log('available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
  }

  available = await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit }) // get available ids

  if (CONSOLE_OUTPUT) {
    console.log('available count (capped) = ' + available.length) // get available ids
  }

  // Case 1: Not enough IDs in the 'AVAILABLE' pool
  if (available.length < availableLimit) {
    result = await incrementNonSequentialPool(availableLimit, result.ranges.general.end, result.ranges.general.top_id, amount, available.length, year, cveIdRangeRepo, cveIdRepo, userRepo, orgRepo)
    isFull = result.isFull

    if (isFull) {
      logger.error('The cve id non-sequential block is full for year ' + year + '. No more sequential ids can be reserved at this time.')
      res.header(CONSTANTS.QUOTA_HEADER, availableIds)
      return res.status(403).json(error.yearRangeFull(year))
    }

    await allocateAvailableCveIds(result.ids, year, cveIdRepo) // Pool was incremented. Create 'AVAILABLE' cve ids.

    if (CONSOLE_OUTPUT) {
      console.log('available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
    }

    available = await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit }) // get available ids

    if (CONSOLE_OUTPUT) {
      console.log('available count (capped) = ' + available.length) // get available ids
    }
  }

  // Case 2: Enough IDs in the 'AVAILABLE' pool
  let index
  let counter = 0
  const cveIdDocuments = []
  const cveIdDocumentsUUID = []
  const owningOrgUUID = await orgRepo.getOrgUUID(shortName)
  const orgUUID = await orgRepo.getOrgUUID(cnaShortName)
  const requesterUUID = (await userRepo.findOneByUserNameAndOrgUUID(requester, orgUUID)).UUID

  if (CONSOLE_OUTPUT) {
    console.log('counter = ' + counter)
  }

  while ((counter < amount) && !isFull) {
    index = getRandomInt(0, available.length) // get random index in the available array
    result = await reserveNonSequentialCveId(index, available, year, shortName, cnaShortName, requester, owningOrgUUID, orgUUID, requesterUUID, cveIdRepo)

    if (CONSOLE_OUTPUT && !result.isReserved) {
      console.log('isReserved: ' + result.isReserved)
    }

    if (result.isReserved) {
      cveIdDocuments.push(result.cveId) // add reserved cve id to the array of reserved cve ids
      cveIdDocumentsUUID.push(result.cveIdUUID) // add reserved cve id UUID to the array of reserved cve ids
      available.splice(index, 1) // remove reserved cve id from the 'AVAILABLE' pool
      counter++

      if (CONSOLE_OUTPUT) {
        console.log('cveIdDocuments: ' + cveIdDocuments.length + ', available: ' + available.length + ', counter = ' + counter)
      }
    } else {
      if (CONSOLE_OUTPUT) {
        console.log('available = ' + available.length)
        console.log('available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
      }

      available = await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit }) // get available ids

      if (CONSOLE_OUTPUT) {
        console.log('available count (capped) = ' + available.length) // get available ids
      }

      availableLimit = Math.max(3 * (amount - counter), CONSTANTS.DEFAULT_AVAILABLE_POOL) // recalculate the available limit since some ids might have been reserved

      if (CONSOLE_OUTPUT) {
        console.log('availableLimit = ' + availableLimit)
      }

      // Case 1: Not enough IDs in the 'AVAILABLE' pool
      if (available.length < availableLimit) {
        result = await cveIdRangeRepo.findOne({ cve_year: year })
        result = await incrementNonSequentialPool(availableLimit, result.ranges.general.end, result.ranges.general.top_id, amount - counter, available.length, year, cveIdRangeRepo, cveIdRepo, userRepo, orgRepo)
        isFull = result.isFull

        if (isFull) {
          logger.info({ message: 'Only ' + counter + ' cve ids were reserved because there are not enough ids in the CVE ID non-sequential block. Non-sequential CVE IDs were reserved for \'' + shortName + '\' org on behalf of \'' + cnaShortName + '\' org.', cve_ids: cveIdDocumentsUUID })
          res.header(CONSTANTS.QUOTA_HEADER, availableIds - counter)
          return res.status(206).json(error.reservedPartialAmount(counter, cveIdDocuments))
        }

        await allocateAvailableCveIds(result.ids, year, cveIdRepo) // Pool was incremented. Create 'AVAILABLE' cve ids.

        if (CONSOLE_OUTPUT) {
          console.log('available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
        }

        available = await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit }) // get available ids

        if (CONSOLE_OUTPUT) {
          console.log('available count (capped) = ' + available.length) // get available ids
        }
      }
    }
  }

  if (!isFull) {
    const payload = {
      action: 'nonsequential_reservation',
      change: 'Non-sequential CVE IDs were reserved for \'' + shortName + '\' org on behalf of \'' + cnaShortName + '\' org.',
      req_UUID: reqUUID,
      org_UUID: orgUUID,
      user_UUID: requesterUUID,
      cve_ids: cveIdDocumentsUUID
    }

    logger.info(JSON.stringify(payload))
    res.header(CONSTANTS.QUOTA_HEADER, availableIds - amount)
    return res.status(200).json({ cve_ids: cveIdDocuments })
  }
}

async function incrementNonSequentialPool (availableLimit, end, top, amount, available, year, cveIdRR, cveIdR, userR, orgR) {
  let increment = availableLimit - available + amount // the amount is the increment amount
  const cveIdRangeRepo = cveIdRR
  const cveIdRepo = cveIdR
  const userRepo = userR
  const orgRepo = orgR
  const endRange = parseInt(end)
  const topId = parseInt(top)

  if (CONSOLE_OUTPUT) {
    console.log('increment = ' + increment)
    console.log('endRange = ' + endRange)
    console.log('topId = ' + topId)
  }

  // Cap increment to end of general CVE ID block
  if (endRange < (topId + increment)) {
    increment = endRange - topId
  }

  if (CONSOLE_OUTPUT) {
    console.log('increment = ' + increment)
  }

  const result = {
    isFull: false,
    ids: null
  }

  // ---- Reserve 10 cve ids ----
  let temp = await cveIdRangeRepo.findOneAndUpdate({ $and: [{ cve_year: year }, { 'ranges.general.end': { $gte: increment } }] }, { $inc: { 'ranges.general.top_id': increment } }, { new: true })

  if (CONSOLE_OUTPUT) {
    console.log('top_id = ' + temp.ranges.general.top_id)
  }

  temp = generateSequentialIds(year, temp.ranges.general.top_id, increment)
  await allocateAvailableCveIds(temp, year, cveIdRepo) // Pool was incremented. Create 'AVAILABLE' cve ids.

  if (CONSOLE_OUTPUT) {
    console.log('available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
  }

  available = await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit }) // get available ids

  if (CONSOLE_OUTPUT) {
    console.log('available count (capped) = ' + available.length) // get available ids
  }

  const tempOwningOrgUUID = await orgRepo.getOrgUUID('mitre')
  const tempOrgUUID = tempOwningOrgUUID
  const tempRequesterUUID = (await userRepo.findOneByUserNameAndOrgUUID('cpadro', tempOrgUUID)).UUID

  for (let i = 0; i < 10; i++) {
    const r = getRandomInt(0, available.length)
    temp = await reserveNonSequentialCveId(r, available, year, 'mitre', 'mitre', 'cpadro', tempOwningOrgUUID, tempOrgUUID, tempRequesterUUID, cveIdRepo)
    available.splice(r, 1) // remove reserved cve id from the 'AVAILABLE' pool
    reservedByOther.push(temp.cveId.cve_id)

    if (CONSOLE_OUTPUT) {
      console.log('* available = ' + available.length)
      console.log('* available count (uncapped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' })).length) // get available ids
      console.log('* available count (capped) = ' + (await cveIdRepo.find({ cve_year: year, state: 'AVAILABLE' }, { limit: availableLimit })).length) // get available ids
    }
  }
  // ----------------------------

  if (increment > 0) {
    let r = await cveIdRangeRepo.findOneAndUpdate({ $and: [{ cve_year: year }, { 'ranges.general.end': { $gte: increment } }] }, { $inc: { 'ranges.general.top_id': increment } }, { new: true })

    if (CONSOLE_OUTPUT) {
      console.log('top_id = ' + r.ranges.general.top_id)
    }

    // Cap increment because it went over end of general block
    //  -------------------- F - G ----------------------
    if (r.ranges.general.top_id > endRange) {
      if (CONSOLE_OUTPUT) {
        console.log('increment = ' + increment)
      }

      increment = increment - (r.ranges.general.top_id - endRange) // adjusting real increment
      r = await cveIdRangeRepo.findOneAndUpdate({ cve_year: year }, { $set: { 'ranges.general.top_id': endRange } }, { new: true }) // Cap top_id to end of the block

      if (CONSOLE_OUTPUT) {
        console.log('top_id = ' + r.ranges.general.top_id)
      }
    }
    //  -----------------------------------------------

    if (CONSOLE_OUTPUT) {
      console.log('increment = ' + increment)
    }

    if (increment > 0) {
      result.ids = generateSequentialIds(year, r.ranges.general.top_id, increment)
    } else if (available < amount) { // You hit the end of the boundary
      //  ------------------- F -------------------------
      result.isFull = true
      //  -----------------------------------------------
    }
  } else if (available < amount) { // You hit the end of the boundary
    result.isFull = true
  }

  return result
}

async function allocateAvailableCveIds (ids, year, cveIdR) {
  if (ids) {
    const cveIdRepo = cveIdR
    const cveIdDocuments = []
    let cveId

    ids.forEach(id => {
      cveId = new CveId()
      cveId.cve_id = id
      cveId.cve_year = year
      cveId.state = 'AVAILABLE'
      cveId.owning_cna = 'N/A' // the org who gets assigned the reserved CVE IDs
      cveId.reserved = Date.now()
      cveId.requested_by = {
        cna: 'N/A', // the org who requested the CVE IDs
        user: 'N/A'
      }

      cveIdDocuments.push(cveId)
    })

    if (CONSOLE_OUTPUT) {
      console.log('Allocated ids: [' + cveIdDocuments[0].cve_id + ', ... , ' + cveIdDocuments[cveIdDocuments.length - 1].cve_id + ']')
      console.log('Allocated count = ' + cveIdDocuments.length)
    }

    await cveIdRepo.insertMany(cveIdDocuments) // Save the 'AVAILABLE' cve ids
  }
}

async function reserveNonSequentialCveId (index, available, year, shortName, cnaShortName, requester, owningOrgUUID, orgUUID, requesterUUID, cveIdR) {
  // Update available Cve Id
  const cveIdUUID = new CveId()
  const cveId = new CveId()
  const cveIdRepo = cveIdR
  cveIdUUID.cve_id = cveId.cve_id = available[index].cve_id
  cveIdUUID.cve_year = cveId.cve_year = year
  cveIdUUID.state = cveId.state = 'RESERVED'
  cveIdUUID.owning_cna = owningOrgUUID
  cveId.owning_cna = shortName // the org who gets assigned the reserved CVE IDs
  cveIdUUID.reserved = cveId.reserved = Date.now()
  cveIdUUID.requested_by = {
    cna: orgUUID, // the org who requested the CVE IDs
    user: requesterUUID
  }
  cveId.requested_by = {
    cna: cnaShortName, // the org who requested the CVE IDs
    user: requester
  }

  const result = {
    cveId: cveId,
    cveIdUUID: cveIdUUID,
    isReserved: await cveIdRepo.findOneAndUpdate({ cve_id: cveIdUUID.cve_id, state: 'AVAILABLE' }, cveIdUUID, { new: true })
  }
  return result
}

async function getPayload (req, org) {
  const payload = {
    id_quota: org.policies.id_quota
  }
  const cveIdRepo = req.ctx.repositories.getCveIdRepository()

  const result = await cveIdRepo.countDocuments({ owning_cna: org.UUID, state: 'RESERVED' })
  payload.total_reserved = result
  payload.available = (payload.id_quota - payload.total_reserved)

  return payload
}

function generateSequentialIds (year, topId, increment) {
  const start = topId - increment + 1 // before the pool 'AVAILABLE' pool increment
  const end = topId // after the 'AVAILABLE' pool increment
  const ids = []

  if (CONSOLE_OUTPUT) {
    console.log('start = ' + start)
    console.log('end = ' + end)
  }

  for (let i = start; i < end + 1; i++) {
    ids.push('CVE-' + year + '-' + String(i).padStart(4, '0'))
  }

  if (CONSOLE_OUTPUT) {
    console.log('Generated ids: [' + ids[0] + ', ... , ' + ids[ids.length - 1] + ']')
  }

  return ids
}

function getRandomInt (min, max) {
  min = Math.ceil(min)
  max = Math.floor(max)

  return Math.floor(Math.random() * (max - min) + min) // The maximum is exclusive and the minimum is inclusive
}

module.exports = {
  CVEID_RESERVE: reserveCveId,
  reservedByOther: reservedByOther
}