text:
const express = require('express')
const router = express.Router()
const mw = require('../../middleware/middleware')
const errorMsgs = require('../../middleware/errorMessages')
const controller = require('./org.controller')
const { body, param, query } = require('express-validator')
const { parseGetParams, parsePostParams, parseError, isOrgRole, isUserRole, isValidUsername } = require('./org.middleware')
const { toUpperCaseArray, isFlatStringArray } = require('../../middleware/middleware')
const getConstants = require('../../../src/constants').getConstants
const CONSTANTS = getConstants()

router.get('/org',
  /*
  #swagger.tags = ['Organization']
  #swagger.operationId = 'orgAll'
  #swagger.summary = "Retrieves all organizations (accessible to Secretariat)"
  #swagger.description = "

Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

Secretariat: Retrieves information about all organizations
"
  #swagger.parameters['$ref'] = [
    '#/components/parameters/pageQuery',
    '#/components/parameters/apiEntityHeader',
    '#/components/parameters/apiUserHeader',
    '#/components/parameters/apiSecretHeader'
  ]
  #swagger.responses[200] = {
    description: 'Returns information about all organizations, along with pagination fields if results span multiple pages of data',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/org/list-orgs-response.json' }
      }
    }
  }
  #swagger.responses[400] = {
    description: 'Bad Request',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/bad-request.json' }
      }
    }
  }
  #swagger.responses[401] = {
    description: 'Not Authenticated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[403] = {
    description: 'Forbidden',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[404] = {
    description: 'Not Found',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[500] = {
    description: 'Internal Server Error',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  */
  mw.validateUser,
  mw.onlySecretariat,
  query().custom((query) => { return mw.validateQueryParameterNames(query, ['page']) }),
  query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }),
  parseError,
  parseGetParams,
  controller.ORG_ALL)
router.post('/org',
  /*
  #swagger.tags = ['Organization']
  #swagger.operationId = 'orgCreateSingle'
  #swagger.summary = "Creates an organization as specified in the request body (accessible to Secretariat)"
  #swagger.description = "

Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

Secretariat: Creates an organization

  "
  #swagger.parameters['$ref'] = [
    '#/components/parameters/apiEntityHeader',
    '#/components/parameters/apiUserHeader',
    '#/components/parameters/apiSecretHeader'
  ]
  #swagger.requestBody = {
    required: true,
    content: {
      'application/json': {
        schema: { $ref: '../schemas/org/create-org-request.json' }
      }
    }
  }
  #swagger.responses[200] = {
    description: 'Returns information about the organization created',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/org/create-org-response.json' }
      }
    }
  }
  #swagger.responses[400] = {
    description: 'Bad Request',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/bad-request.json' }
      }
    }
  }
  #swagger.responses[401] = {
    description: 'Not Authenticated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[403] = {
    description: 'Forbidden',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[404] = {
    description: 'Not Found',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[500] = {
    description: 'Internal Server Error',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  */
  mw.validateUser,
  mw.onlySecretariat,
  body(['short_name']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
  body(['name']).isString().trim().escape().notEmpty(),
  body(['authority.active_roles']).optional()
    .custom(isFlatStringArray)
    .customSanitizer(toUpperCaseArray)
    .custom(isOrgRole),
  body(['policies.id_quota']).optional().not().isArray().isInt({ min: CONSTANTS.MONGOOSE_VALIDATION.Org_policies_id_quota_min, max: CONSTANTS.MONGOOSE_VALIDATION.Org_policies_id_quota_max }).withMessage(errorMsgs.ID_QUOTA),
  parseError,
  parsePostParams,
  controller.ORG_CREATE_SINGLE)
router.get('/org/:identifier',
  /*
  #swagger.tags = ['Organization']
  #swagger.operationId = 'orgSingle'
  #swagger.summary = "Retrieves information about the organization specified by short name or UUID (accessible to all registered users)"
  #swagger.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
"
  #swagger.parameters['identifier'] = { description: 'The shortname or UUID of the organization' }
  #swagger.parameters['$ref'] = [
    '#/components/parameters/apiEntityHeader',
    '#/components/parameters/apiUserHeader',
    '#/components/parameters/apiSecretHeader'
  ]
  #swagger.responses[200] = {
    description: 'Returns the organization information',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/org/get-org-response.json' }
      }
    }
  }
  #swagger.responses[400] = {
    description: 'Bad Request',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/bad-request.json' }
      }
    }
  }
  #swagger.responses[401] = {
    description: 'Not Authenticated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[403] = {
    description: 'Forbidden',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[404] = {
    description: 'Not Found',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[500] = {
    description: 'Internal Server Error',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  */
  mw.validateUser,
  param(['identifier']).isString().trim().escape(),
  parseError,
  parseGetParams,
  controller.ORG_SINGLE)
router.put('/org/:shortname',
  /*
  #swagger.tags = ['Organization']
  #swagger.operationId = 'orgUpdateSingle'
  #swagger.summary = "Updates information about the organization specified by short name (accessible to Secretariat)"
  #swagger.description = "

Access Control

User must belong to an organization with the Secretariat role

Expected Behavior

Secretariat: Updates any organization's information
"
  #swagger.parameters['shortname'] = { description: 'The shortname of the organization' }
  #swagger.parameters['$ref'] = [
    '#/components/parameters/id_quota',
    '#/components/parameters/name',
    '#/components/parameters/newShortname',
    '#/components/parameters/active_roles_add',
    '#/components/parameters/active_roles_remove',
    '#/components/parameters/apiEntityHeader',
    '#/components/parameters/apiUserHeader',
    '#/components/parameters/apiSecretHeader'
  ]
  #swagger.responses[200] = {
    description: 'Returns information about the organization updated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/org/update-org-response.json' }
      }
    }
  }
  #swagger.responses[400] = {
    description: 'Bad Request',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/bad-request.json' }
      }
    }
  }
  #swagger.responses[401] = {
    description: 'Not Authenticated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[403] = {
    description: 'Forbidden',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[404] = {
    description: 'Not Found',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[500] = {
    description: 'Internal Server Error',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  */
  mw.validateUser,
  mw.onlySecretariat,
  query().custom((query) => { return mw.validateQueryParameterNames(query, ['new_short_name', 'id_quota', 'name', 'active_roles.add', 'active_roles.remove']) }),
  param(['shortname']).isString().trim().escape().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
  query(['new_short_name']).optional().isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
  query(['id_quota']).optional().not().isArray().isInt({ min: CONSTANTS.MONGOOSE_VALIDATION.Org_policies_id_quota_min, max: CONSTANTS.MONGOOSE_VALIDATION.Org_policies_id_quota_max }).withMessage(errorMsgs.ID_QUOTA),
  query(['name']).optional().isString().trim().escape().notEmpty(),
  query(['active_roles.add']).optional().toArray()
    .custom(isFlatStringArray)
    .customSanitizer(toUpperCaseArray)
    .custom(isOrgRole).withMessage(errorMsgs.ORG_ROLES),
  query(['active_roles.remove']).optional().toArray()
    .custom(isFlatStringArray)
    .customSanitizer(toUpperCaseArray)
    .custom(isOrgRole).withMessage(errorMsgs.ORG_ROLES),
  parseError,
  parsePostParams,
  controller.ORG_UPDATE_SINGLE)
router.get('/org/:shortname/id_quota',
  /*
  #swagger.tags = ['Organization']
  #swagger.operationId = 'orgIdQuota'
  #swagger.summary = "Retrieves an organization's CVE ID quota (accessible to all registered users)"
  #swagger.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
"
  #swagger.parameters['shortname'] = { description: 'The shortname of the organization' }
  #swagger.parameters['$ref'] = [
    '#/components/parameters/apiEntityHeader',
    '#/components/parameters/apiUserHeader',
    '#/components/parameters/apiSecretHeader'
  ]
  #swagger.responses[200] = {
    description: 'Returns the CVE ID quota for an organization',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/org/get-org-quota-response.json' }
      }
    }
  }
  #swagger.responses[400] = {
    description: 'Bad Request',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/bad-request.json' }
      }
    }
  }
  #swagger.responses[401] = {
    description: 'Not Authenticated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[403] = {
    description: 'Forbidden',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[404] = {
    description: 'Not Found',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[500] = {
    description: 'Internal Server Error',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  */
  mw.validateUser,
  param(['shortname']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
  parseError,
  parseGetParams,
  controller.ORG_ID_QUOTA)
router.get('/org/:shortname/users',
  /*
  #swagger.tags = ['Users']
  #swagger.operationId = 'userOrgAll'
  #swagger.summary = "Retrieves all users for the organization with the specified short name (accessible to all registered users)"
  #swagger.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
"
  #swagger.parameters['shortname'] = { description: 'The shortname of the organization' }
  #swagger.parameters['$ref'] = [
    '#/components/parameters/pageQuery',
    '#/components/parameters/apiEntityHeader',
    '#/components/parameters/apiUserHeader',
    '#/components/parameters/apiSecretHeader'
  ]
  #swagger.responses[200] = {
    description: 'Returns all users for the organization, along with pagination fields if results span multiple pages of data',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/user/list-users-response.json' }
      }
    }
  }
  #swagger.responses[400] = {
    description: 'Bad Request',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/bad-request.json' }
      }
    }
  }
  #swagger.responses[401] = {
    description: 'Not Authenticated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[403] = {
    description: 'Forbidden',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[404] = {
    description: 'Not Found',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[500] = {
    description: 'Internal Server Error',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  */
  mw.validateUser,
  param(['shortname']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
  query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }),
  parseError,
  parseGetParams,
  controller.USER_ALL)
router.post('/org/:shortname/user',
  /*
  #swagger.tags = ['Users']
  #swagger.operationId = 'userCreateSingle'
  #swagger.summary = "Create a user with the provided short name as the owning organization (accessible to Admins and Secretariats)"
  #swagger.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
"
  #swagger.parameters['shortname'] = { description: 'The shortname of the organization' }
  #swagger.parameters['$ref'] = [
    '#/components/parameters/apiEntityHeader',
    '#/components/parameters/apiUserHeader',
    '#/components/parameters/apiSecretHeader'
  ]
  #swagger.requestBody = {
    required: true,
    content: {
      'application/json': {
        schema: { $ref: '../schemas/user/create-user-request.json' },
      }
    }
  }
  #swagger.responses[200] = {
    description: 'Returns the new user information (with the secret)',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/user/create-user-response.json' },
      }
    }
  }
  #swagger.responses[400] = {
    description: 'Bad Request',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/bad-request.json' }
      }
    }
  }
  #swagger.responses[401] = {
    description: 'Not Authenticated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[403] = {
    description: 'Forbidden',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[404] = {
    description: 'Not Found',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[500] = {
    description: 'Internal Server Error',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  */
  mw.validateUser,
  mw.onlySecretariatOrAdmin,
  mw.onlyOrgWithPartnerRole,
  param(['shortname']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
  body(['username']).isString().trim().escape().notEmpty().custom(isValidUsername),
  body(['org_uuid']).optional().isString().trim().escape(),
  body(['uuid']).optional().isString().trim().escape(),
  body(['name.first']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_FIRSTNAME_LENGTH }).withMessage(errorMsgs.FIRSTNAME_LENGTH),
  body(['name.last']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_LASTNAME_LENGTH }).withMessage(errorMsgs.LASTNAME_LENGTH),
  body(['name.middle']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_MIDDLENAME_LENGTH }).withMessage(errorMsgs.MIDDLENAME_LENGTH),
  body(['name.suffix']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_SUFFIX_LENGTH }).withMessage(errorMsgs.SUFFIX_LENGTH),
  body(['authority.active_roles']).optional()
    .custom(mw.isFlatStringArray)
    .customSanitizer(toUpperCaseArray)
    .custom(isUserRole),
  parseError,
  parsePostParams,
  controller.USER_CREATE_SINGLE)
router.get('/org/:shortname/user/:username',
  /*
  #swagger.tags = ['Users']
  #swagger.operationId = 'userSingle'
  #swagger.summary = "Retrieves information about a user for the specified username and organization short name (accessible to all registered users)"
  #swagger.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
"
  #swagger.parameters['shortname'] = { description: 'The shortname of the organization' }
  #swagger.parameters['username'] = { description: 'The username of the user' }
  #swagger.parameters['$ref'] = [
    '#/components/parameters/apiEntityHeader',
    '#/components/parameters/apiUserHeader',
    '#/components/parameters/apiSecretHeader'
  ]
  #swagger.responses[200] = {
    description: 'Returns information about the specified user',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/user/get-user-response.json' }
      }
    }
  }
  #swagger.responses[400] = {
    description: 'Bad Request',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/bad-request.json' }
      }
    }
  }
  #swagger.responses[401] = {
    description: 'Not Authenticated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[403] = {
    description: 'Forbidden',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[404] = {
    description: 'Not Found',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[500] = {
    description: 'Internal Server Error',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  */
  mw.validateUser,
  param(['shortname']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
  param(['username']).isString().trim().escape().notEmpty().custom(isValidUsername),
  parseError,
  parseGetParams,
  controller.USER_SINGLE)
router.put('/org/:shortname/user/:username',
  /*
  #swagger.tags = ['Users']
   #swagger.operationId = 'userUpdateSingle'
  #swagger.summary = "Updates information about a user for the specified username and organization shortname (accessible to all registered users)"
  #swagger.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.
"
  #swagger.parameters['shortname'] = { description: 'The shortname of the organization' }
  #swagger.parameters['username'] = { description: 'The username of the user' }
  #swagger.parameters['$ref'] = [
    '#/components/parameters/active',
    '#/components/parameters/activeUserRolesAdd',
    '#/components/parameters/activeUserRolesRemove',
    '#/components/parameters/nameFirst',
    '#/components/parameters/nameLast',
    '#/components/parameters/nameMiddle',
    '#/components/parameters/nameSuffix',
    '#/components/parameters/newUsername',
    '#/components/parameters/orgShortname',
    '#/components/parameters/apiEntityHeader',
    '#/components/parameters/apiUserHeader',
    '#/components/parameters/apiSecretHeader'
  ]
  #swagger.responses[200] = {
    description: 'Returns the updated user information',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/user/update-user-response.json' }
      }
    }
  }
  #swagger.responses[400] = {
    description: 'Bad Request',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/bad-request.json' }
      }
    }
  }
  #swagger.responses[401] = {
    description: 'Not Authenticated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[403] = {
    description: 'Forbidden',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[404] = {
    description: 'Not Found',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[500] = {
    description: 'Internal Server Error',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  */
  mw.validateUser,
  mw.onlyOrgWithPartnerRole,
  query().custom((query) => {
    return mw.validateQueryParameterNames(query, ['active', 'new_username', 'org_short_name', 'name.first', 'name.last', 'name.middle',
      'name.suffix', 'active_roles.add', 'active_roles.remove'])
  }),
  param(['shortname']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
  param(['username']).isString().trim().escape().notEmpty().custom(isValidUsername),
  query(['active']).optional().isBoolean({ loose: true }),
  query(['new_username']).optional().isString().trim().escape().notEmpty().custom(isValidUsername),
  query(['org_short_name']).optional().isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
  body(['name.first']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_FIRSTNAME_LENGTH }).withMessage(errorMsgs.FIRSTNAME_LENGTH),
  body(['name.last']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_LASTNAME_LENGTH }).withMessage(errorMsgs.LASTNAME_LENGTH),
  body(['name.middle']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_MIDDLENAME_LENGTH }).withMessage(errorMsgs.MIDDLENAME_LENGTH),
  body(['name.suffix']).optional().isString().trim().escape().isLength({ max: CONSTANTS.MAX_SUFFIX_LENGTH }).withMessage(errorMsgs.SUFFIX_LENGTH),
  query(['active_roles.add']).optional().toArray()
    .custom(isFlatStringArray)
    .customSanitizer(toUpperCaseArray)
    .custom(isUserRole).withMessage(errorMsgs.USER_ROLES),
  query(['active_roles.remove']).optional().toArray()
    .custom(isFlatStringArray)
    .customSanitizer(toUpperCaseArray)
    .custom(isUserRole).withMessage(errorMsgs.USER_ROLES),
  parseError,
  parsePostParams,
  controller.USER_UPDATE_SINGLE)
router.put('/org/:shortname/user/:username/reset_secret',
  /*
  #swagger.tags = ['Users']
  #swagger.operationId = 'userResetSecret'
  #swagger.summary = "Reset the API key for a user (accessible to all registered users)"
  #swagger.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
"
  #swagger.parameters['shortname'] = { description: 'The shortname of the organization' }
  #swagger.parameters['username'] = { description: 'The username of the user' }
  #swagger.parameters['$ref'] = [
    '#/components/parameters/apiEntityHeader',
    '#/components/parameters/apiUserHeader',
    '#/components/parameters/apiSecretHeader'
  ]
  #swagger.responses[200] = {
    description: 'Returns the new API key',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/user/reset-secret-response.json' }
      }
    }
  }
  #swagger.responses[400] = {
    description: 'Bad Request',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/bad-request.json' }
      }
    }
  }
  #swagger.responses[401] = {
    description: 'Not Authenticated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[403] = {
    description: 'Forbidden',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[404] = {
    description: 'Not Found',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[500] = {
    description: 'Internal Server Error',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  */
  mw.validateUser,
  mw.onlyOrgWithPartnerRole,
  param(['shortname']).isString().trim().escape().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
  param(['username']).isString().trim().escape().notEmpty().custom(isValidUsername),
  parseError,
  parsePostParams,
  controller.USER_RESET_SECRET)

module.exports = router


text:
const getConstants = require('../../constants').getConstants
const { validationResult } = require('express-validator')
const errors = require('./error')
const error = new errors.OrgControllerError()
const utils = require('../../utils/utils')

function isOrgRole (val) {
  const CONSTANTS = getConstants()

  val.forEach(role => {
    if (!CONSTANTS.ORG_ROLES.includes(role)) {
      throw new Error('Organization role does not exist.')
    }
  })

  return true
}

function isUserRole (val) {
  const CONSTANTS = getConstants()

  val.forEach(role => {
    if (!CONSTANTS.USER_ROLES.includes(role)) {
      throw new Error('User role does not exist.')
    }
  })

  return true
}

function parsePostParams (req, res, next) {
  utils.reqCtxMapping(req, 'body', [])
  utils.reqCtxMapping(req, 'query', [
    'new_short_name', 'name', 'id_quota', 'active',
    'active_roles.add', 'active_roles.remove',
    'new_username', 'org_short_name',
    'name.first', 'name.last', 'name.middle', 'name.suffix'
  ])
  utils.reqCtxMapping(req, 'params', ['shortname', 'username'])
  next()
}

function parseGetParams (req, res, next) {
  utils.reqCtxMapping(req, 'params', ['shortname', 'username', 'identifier'])
  utils.reqCtxMapping(req, 'query', ['page'])
  next()
}

function parseError (req, res, next) {
  const err = validationResult(req).formatWith(({ location, msg, param, value, nestedErrors }) => {
    return { msg: msg, param: param, location: location }
  })
  if (!err.isEmpty()) {
    return res.status(400).json(error.badInput(err.array()))
  }
  next()
}

function isValidUsername (val) {
  const value = val.match(/^[A-Za-z0-9\-_@.]{3,128}$/)
  if (value == null) {
    throw new Error('Username should be 3-128 characters. Allowed characters are alphanumeric and -_@.')
  }
  return true
}

module.exports = {
  parsePostParams,
  parseGetParams,
  parseError,
  isOrgRole,
  isUserRole,
  isValidUsername
}

repo_name: CVEProject/cve-services

repo_language: JavaScript

file_name: Dockerfile

mime_type: text/plain


text:
require('dotenv').config()
const User = require('../../model/user')
const Org = require('../../model/org')
const logger = require('../../middleware/logger')
const argon2 = require('argon2')
const getConstants = require('../../constants').getConstants
const cryptoRandomString = require('crypto-random-string')
const uuid = require('uuid')
const errors = require('./error')
const error = new errors.OrgControllerError()
const validateUUID = require('uuid').validate
const decodeEntities = require('html-entities').decode
const booleanIsTrue = require('../../utils/utils').booleanIsTrue

/**
 *  Get the details of all orgs
 *  Called by GET /api/org
 **/
async function getOrgs (req, res, next) {
  try {
    const CONSTANTS = getConstants()

    // temporary measure to allow tests to work after fixing #920
    // tests required changing the global limit to force pagination
    if (req.TEST_PAGINATOR_LIMIT) {
      CONSTANTS.PAGINATOR_OPTIONS.limit = req.TEST_PAGINATOR_LIMIT
    }

    const options = CONSTANTS.PAGINATOR_OPTIONS
    options.sort = { short_name: 'asc' }
    options.page = req.ctx.query.page ? parseInt(req.ctx.query.page) : CONSTANTS.PAGINATOR_PAGE // if 'page' query parameter is not defined, set 'page' to the default page value
    const repo = req.ctx.repositories.getOrgRepository()

    const agt = setAggregateOrgObj({})
    const pg = await repo.aggregatePaginate(agt, options)
    const payload = { organizations: pg.itemsList }

    if (pg.itemCount >= CONSTANTS.PAGINATOR_OPTIONS.limit) {
      payload.totalCount = pg.itemCount
      payload.itemsPerPage = pg.itemsPerPage
      payload.pageCount = pg.pageCount
      payload.currentPage = pg.currentPage
      payload.prevPage = pg.prevPage
      payload.nextPage = pg.nextPage
    }

    logger.info({ uuid: req.ctx.uuid, message: 'The orgs were sent to the user.' })
    return res.status(200).json(payload)
  } catch (err) {
    next(err)
  }
}

/**
 * Get the details of a single org for the specified shortname
 * Called by GET /api/org/{identifier}
 **/
async function getOrg (req, res, next) {
  try {
    const orgShortName = req.ctx.org
    const identifier = req.ctx.params.identifier
    const repo = req.ctx.repositories.getOrgRepository()
    const isSecretariat = await repo.isSecretariat(orgShortName)
    const org = await repo.findOneByShortName(orgShortName)
    let orgIdentifer = orgShortName
    let agt = setAggregateOrgObj({ short_name: identifier })

    // check if identifier is uuid and if so, reassign agt and orgIdentifier
    if (validateUUID(identifier)) {
      orgIdentifer = org.UUID
      agt = setAggregateOrgObj({ UUID: identifier })
    }

    if (orgIdentifer !== identifier && !isSecretariat) {
      logger.info({ uuid: req.ctx.uuid, message: identifier + ' organization can only be viewed by the users of the same organization or the Secretariat.' })
      return res.status(403).json(error.notSameOrgOrSecretariat())
    }

    let result = await repo.aggregate(agt)
    result = result.length > 0 ? result[0] : null

    if (!result) { // an empty result can only happen if the requestor is the Secretariat
      logger.info({ uuid: req.ctx.uuid, message: identifier + ' organization does not exist.' })
      return res.status(404).json(error.orgDne(identifier, 'identifier', 'path'))
    }

    logger.info({ uuid: req.ctx.uuid, message: identifier + ' organization was sent to the user.', org: result })
    return res.status(200).json(result)
  } catch (err) {
    next(err)
  }
}

/**
 *  Get the details of all users from an org given the specified shortname
 *  Called by GET /api/org/{shortname}/users
 **/
async function getUsers (req, res, next) {
  try {
    const CONSTANTS = getConstants()

    // temporary measure to allow tests to work after fixing #920
    // tests required changing the global limit to force pagination
    if (req.TEST_PAGINATOR_LIMIT) {
      CONSTANTS.PAGINATOR_OPTIONS.limit = req.TEST_PAGINATOR_LIMIT
    }

    const options = CONSTANTS.PAGINATOR_OPTIONS
    options.sort = { username: 'asc' }
    options.page = req.ctx.query.page ? parseInt(req.ctx.query.page) : CONSTANTS.PAGINATOR_PAGE // if 'page' query parameter is not defined, set 'page' to the default page value
    const shortName = req.ctx.org
    const orgShortName = req.ctx.params.shortname
    const orgRepo = req.ctx.repositories.getOrgRepository()
    const userRepo = req.ctx.repositories.getUserRepository()
    const orgUUID = await orgRepo.getOrgUUID(orgShortName)
    const isSecretariat = await orgRepo.isSecretariat(shortName)

    if (!orgUUID) {
      logger.info({ uuid: req.ctx.uuid, message: orgShortName + ' organization does not exist.' })
      return res.status(404).json(error.orgDnePathParam(orgShortName))
    }

    if (orgShortName !== shortName && !isSecretariat) {
      logger.info({ uuid: req.ctx.uuid, message: orgShortName + ' organization can only be viewed by the users of the same organization or the Secretariat.' })
      return res.status(403).json(error.notSameOrgOrSecretariat())
    }

    const agt = setAggregateUserObj({ org_UUID: orgUUID })
    const pg = await userRepo.aggregatePaginate(agt, options)
    const payload = { users: pg.itemsList }

    if (pg.itemCount >= CONSTANTS.PAGINATOR_OPTIONS.limit) {
      payload.totalCount = pg.itemCount
      payload.itemsPerPage = pg.itemsPerPage
      payload.pageCount = pg.pageCount
      payload.currentPage = pg.currentPage
      payload.prevPage = pg.prevPage
      payload.nextPage = pg.nextPage
    }

    logger.info({ uuid: req.ctx.uuid, message: `The users of ${orgShortName} organization were sent to the user.` })
    return res.status(200).json(payload)
  } catch (err) {
    next(err)
  }
}

/**
 *  Get the details of a single user for the specified username
 *  Called by GET /api/org/{shortname}/user/{username}
 **/
async function getUser (req, res, next) {
  try {
    const shortName = req.ctx.org
    const username = req.ctx.params.username
    const orgShortName = req.ctx.params.shortname
    const orgRepo = req.ctx.repositories.getOrgRepository()
    const isSecretariat = await orgRepo.isSecretariat(shortName)

    if (orgShortName !== shortName && !isSecretariat) {
      logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization can only be viewed by that organization\'s users or the Secretariat.' })
      return res.status(403).json(error.notSameOrgOrSecretariat())
    }

    const orgUUID = await orgRepo.getOrgUUID(orgShortName)
    if (!orgUUID) { // the org can only be non-existent if the requestor is the Secretariat
      logger.info({ uuid: req.ctx.uuid, message: orgShortName + ' organization does not exist.' })
      return res.status(404).json(error.orgDnePathParam(orgShortName))
    }

    const userRepo = req.ctx.repositories.getUserRepository()
    const agt = setAggregateUserObj({ username: username, org_UUID: orgUUID })
    let result = await userRepo.aggregate(agt)
    result = result.length > 0 ? result[0] : null

    if (!result) {
      logger.info({ uuid: req.ctx.uuid, message: username + ' does not exist.' })
      return res.status(404).json(error.userDne(username))
    }

    logger.info({ uuid: req.ctx.uuid, message: username + ' was sent to the user.', user: result })
    return res.status(200).json(result)
  } catch (err) {
    next(err)
  }
}

/**
 * Get details on ID quota for an org with the specified org shortname
 * Called by GET /api/org/{shortname}/id_quota
 **/
async function getOrgIdQuota (req, res, next) {
  try {
    const orgShortName = req.ctx.org
    const shortName = req.ctx.params.shortname
    const repo = req.ctx.repositories.getOrgRepository()
    const isSecretariat = await repo.isSecretariat(orgShortName)

    if (orgShortName !== shortName && !isSecretariat) {
      logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization id quota can only be viewed by the users of the same organization or the Secretariat.' })
      return res.status(403).json(error.notSameOrgOrSecretariat())
    }

    let result = await repo.findOneByShortName(shortName)
    if (!result) { // a null result can only happen if the requestor is the Secretariat
      logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization does not exist.' })
      return res.status(404).json(error.orgDnePathParam(shortName))
    }

    const returnPayload = {
      id_quota: result.policies.id_quota,
      total_reserved: null,
      available: null
    }

    const query = {
      owning_cna: await repo.getOrgUUID(shortName),
      state: getConstants().CVE_STATES.RESERVED
    }
    const cveIdRepo = req.ctx.repositories.getCveIdRepository()
    result = await cveIdRepo.countDocuments(query)
    returnPayload.total_reserved = result
    returnPayload.available = returnPayload.id_quota - returnPayload.total_reserved

    logger.info({ uuid: req.ctx.uuid, message: 'The organization\'s id quota was returned to the user.', details: returnPayload })
    return res.status(200).json(returnPayload)
  } catch (err) {
    next(err)
  }
}

/**
 *  Creates a new org only if the org doesn't exist for the specified shortname.
 *  If the org exists, we do not update the org.
 *  Called by POST /api/org/
 **/
async function createOrg (req, res, next) {
  const CONSTANTS = getConstants()

  try {
    const newOrg = new Org()
    const orgRepo = req.ctx.repositories.getOrgRepository()

    for (const k in req.ctx.body) {
      const key = k.toLowerCase()

      switch (key) {
        case 'short_name':
          newOrg.short_name = decodeEntities(req.ctx.body.short_name)
          break

        case 'name':
          newOrg.name = decodeEntities(req.ctx.body.name)
          break

        case 'authority':
          if ('active_roles' in req.ctx.body.authority) {
            newOrg.authority.active_roles = req.ctx.body.authority.active_roles
          }
          break

        case 'policies':
          if ('id_quota' in req.ctx.body.policies) {
            newOrg.policies.id_quota = req.ctx.body.policies.id_quota
          }
          break

        case 'uuid':
          return res.status(400).json(error.uuidProvided('org'))
      }
    }

    let result = await orgRepo.findOneByShortName(newOrg.short_name) // Find org in MongoDB
    if (result) {
      logger.info({ uuid: req.ctx.uuid, message: newOrg.short_name + ' organization was not created because it already exists.' })
      return res.status(400).json(error.orgExists(newOrg.short_name))
    }

    newOrg.inUse = false
    newOrg.UUID = uuid.v4()

    if (newOrg.authority.active_roles.length === 0) { // default is to make the Org a CNA if no role is specified
      newOrg.authority.active_roles = [CONSTANTS.AUTH_ROLE_ENUM.CNA]
    }

    if (newOrg.policies.id_quota === undefined) { // set to default quota if none is specified
      newOrg.policies.id_quota = CONSTANTS.DEFAULT_ID_QUOTA
    }

    if (newOrg.authority.active_roles.length === 1 && newOrg.authority.active_roles[0] === 'ADP') { // ADPs have quota of 0
      newOrg.policies.id_quota = 0
    }

    await orgRepo.updateByOrgUUID(newOrg.UUID, newOrg, { upsert: true }) // Create org in MongoDB if it doesn't exist
    const agt = setAggregateOrgObj({ short_name: newOrg.short_name })
    result = await orgRepo.aggregate(agt)
    result = result.length > 0 ? result[0] : null

    const responseMessage = {
      message: newOrg.short_name + ' organization was successfully created.',
      created: result
    }

    const payload = {
      action: 'create_org',
      change: newOrg.short_name + ' organization was successfully created.',
      req_UUID: req.ctx.uuid,
      org_UUID: await orgRepo.getOrgUUID(req.ctx.org),
      org: result
    }
    const userRepo = req.ctx.repositories.getUserRepository()
    payload.user_UUID = await userRepo.getUserUUID(req.ctx.user, payload.org_UUID)
    logger.info(JSON.stringify(payload))
    return res.status(200).json(responseMessage)
  } catch (err) {
    next(err)
  }
}

/**
 *  Updates an org only if the org exist for the specified shortname.
 *  If no org exists, we do not create the org.
 *  Called by PUT /api/org/{shortname}
 **/
async function updateOrg (req, res, next) {
  try {
    const shortName = req.ctx.params.shortname
    const newOrg = new Org()
    const removeRoles = []
    const addRoles = []
    const orgRepo = req.ctx.repositories.getOrgRepository()
    const org = await orgRepo.findOneByShortName(shortName)
    let agt = setAggregateOrgObj({ short_name: shortName })

    // org doesn't exist
    if (!org) {
      logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization could not be updated in MongoDB because it does not exist.' })
      return res.status(404).json(error.orgDnePathParam(shortName))
    }

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

      if (key === 'new_short_name') {
        newOrg.short_name = decodeEntities(req.ctx.query.new_short_name)
        agt = setAggregateOrgObj({ short_name: newOrg.short_name })
      } else if (key === 'name') {
        newOrg.name = decodeEntities(req.ctx.query.name)
      } else if (key === 'id_quota') {
        newOrg.policies.id_quota = req.ctx.query.id_quota
      } else if (key === 'active_roles.add') {
        if (Array.isArray(req.ctx.query['active_roles.add'])) {
          req.ctx.query['active_roles.add'].forEach(r => {
            addRoles.push(r)
          })
        }
      } else if (key === 'active_roles.remove') {
        if (Array.isArray(req.ctx.query['active_roles.remove'])) {
          req.ctx.query['active_roles.remove'].forEach(r => {
            removeRoles.push(r)
          })
        }
      }
    })

    // updating the org's roles
    if (org) {
      const roles = org.authority.active_roles

      // adding roles
      addRoles.forEach(role => {
        if (!roles.includes(role)) {
          roles.push(role)
        }
      })

      // removing roles
      removeRoles.forEach(role => {
        const index = roles.indexOf(role)

        if (index > -1) {
          roles.splice(index, 1)
        }
      })

      newOrg.authority.active_roles = roles
    }

    if (newOrg.short_name) {
      const result = await orgRepo.findOneByShortName(newOrg.short_name)

      if (result) {
        return res.status(403).json(error.duplicateShortname(newOrg.short_name))
      }
    }

    // update org
    let result = await orgRepo.updateByOrgUUID(org.UUID, newOrg)
    if (result.n === 0) {
      logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization could not be updated in MongoDB because it does not exist.' })
      return res.status(404).json(error.orgDnePathParam(shortName))
    }

    result = await orgRepo.aggregate(agt)
    result = result.length > 0 ? result[0] : null

    const responseMessage = {
      message: shortName + ' organization was successfully updated.',
      updated: result
    }

    const payload = {
      action: 'update_org',
      change: shortName + ' organization was successfully updated.',
      req_UUID: req.ctx.uuid,
      org_UUID: await orgRepo.getOrgUUID(req.ctx.org),
      org: result
    }
    const userRepo = req.ctx.repositories.getUserRepository()
    payload.user_UUID = await userRepo.getUserUUID(req.ctx.user, payload.org_UUID)
    logger.info(JSON.stringify(payload))
    return res.status(200).json(responseMessage)
  } catch (err) {
    next(err)
  }
}

/**
 * Creates a user only if the org exists and
 * the user does not exist for the specified shortname and username
 * Called by POST /api/org/{shortname}/user
 **/
async function createUser (req, res, next) {
  try {
    const orgShortName = req.ctx.params.shortname
    const requesterUsername = req.ctx.user
    const requesterShortName = req.ctx.org
    const orgRepo = req.ctx.repositories.getOrgRepository()
    const userRepo = req.ctx.repositories.getUserRepository()
    const newUser = new User()

    const orgUUID = await orgRepo.getOrgUUID(orgShortName)
    if (!orgUUID) { // the org can only be non-existent if the requestor is the Secretariat
      logger.info({ uuid: req.ctx.uuid, message: 'The user could not be created because ' + orgShortName + ' organization does not exist.' })
      return res.status(404).json(error.orgDnePathParam(orgShortName))
    }

    const users = await userRepo.findUsersByOrgUUID(orgUUID)

    if (users >= 100) {
      return res.status(400).json(error.userLimitReached())
    }

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

      if (key === 'username') {
        newUser.username = req.ctx.body.username
      } else if (key === 'authority') {
        if (req.ctx.body.authority.active_roles) {
          newUser.authority.active_roles = [...new Set(req.ctx.body.authority.active_roles)] // Removes any duplicate strings from array
        }
      } else if (key === 'name') {
        if (req.ctx.body.name.first) {
          newUser.name.first = decodeEntities(req.ctx.body.name.first)
        }
        if (req.ctx.body.name.last) {
          newUser.name.last = decodeEntities(req.ctx.body.name.last)
        }
        if (req.ctx.body.name.middle) {
          newUser.name.middle = decodeEntities(req.ctx.body.name.middle)
        }
        if (req.ctx.body.name.suffix) {
          newUser.name.suffix = decodeEntities(req.ctx.body.name.suffix)
        }
      } else if (key === 'org_uuid') {
        return res.status(400).json(error.uuidProvided('org'))
      } else if (key === 'uuid') {
        return res.status(400).json(error.uuidProvided('user'))
      }
    })

    const requesterOrgUUID = await orgRepo.getOrgUUID(requesterShortName)
    const isSecretariat = await orgRepo.isSecretariatUUID(requesterOrgUUID)
    const isAdmin = await userRepo.isAdminUUID(requesterUsername, requesterOrgUUID)
    // check if user is only an Admin (not Secretatiat) and the user does not belong to the same organization as the new user
    if (!isSecretariat && isAdmin) {
      if (requesterOrgUUID !== orgUUID) {
        return res.status(403).json(error.notOrgAdminOrSecretariat()) // The Admin user must belong to the new user's organization
      }
    }

    newUser.org_UUID = orgUUID
    newUser.UUID = uuid.v4()
    newUser.active = true
    const randomKey = cryptoRandomString({ length: getConstants().CRYPTO_RANDOM_STRING_LENGTH })
    newUser.secret = await argon2.hash(randomKey)

    let result = await userRepo.findOneByUserNameAndOrgUUID(newUser.username, newUser.org_UUID) // Find user in MongoDB
    if (result) {
      logger.info({ uuid: req.ctx.uuid, message: newUser.username + ' was not created because it already exists.' })
      return res.status(400).json(error.userExists(newUser.username))
    }

    // Parsing all user name fields
    newUser.name = parseUserName(newUser)

    await userRepo.updateByUserNameAndOrgUUID(newUser.username, newUser.org_UUID, newUser, { upsert: true }) // Create user in MongoDB if it doesn't exist
    const agt = setAggregateUserObj({ username: newUser.username, org_UUID: newUser.org_UUID })
    result = await userRepo.aggregate(agt)
    result = result.length > 0 ? result[0] : null

    const payload = {
      action: 'create_user',
      change: result.username + ' was successfully created.',
      req_UUID: req.ctx.uuid,
      org_UUID: await orgRepo.getOrgUUID(req.ctx.org),
      user: result
    }
    payload.user_UUID = await userRepo.getUserUUID(req.ctx.user, payload.org_UUID)
    logger.info(JSON.stringify(payload))

    result.secret = randomKey
    const responseMessage = {
      message: result.username + ' was successfully created.',
      created: result
    }

    return res.status(200).json(responseMessage)
  } catch (err) {
    next(err)
  }
}

/**
 *  Updates a user only if the user exist for the specified username.
 *  If no user exists, it does not create the user.
 *  Called by PUT /org/{shortname}/user/{username}
 **/
async function updateUser (req, res, next) {
  try {
    const requesterShortName = req.ctx.org
    const requesterUsername = req.ctx.user
    const username = req.ctx.params.username
    const shortName = req.ctx.params.shortname
    const newUser = new User()
    let newOrgShortName = null
    let changesRequirePrivilegedRole = false // Set variable to true if protected fields are being modified
    const removeRoles = []
    const addRoles = []
    const userRepo = req.ctx.repositories.getUserRepository()
    const orgRepo = req.ctx.repositories.getOrgRepository()
    const orgUUID = await orgRepo.getOrgUUID(shortName)
    const isSecretariat = await orgRepo.isSecretariat(requesterShortName)
    const isAdmin = await userRepo.isAdmin(requesterUsername, requesterShortName) // Check if requester is Admin of the designated user's org

    if (!orgUUID) {
      logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because ' + shortName + ' organization does not exist.' })
      return res.status(404).json(error.orgDnePathParam(shortName))
    }

    if (shortName !== requesterShortName && !isSecretariat) {
      logger.info({ uuid: req.ctx.uuid, message: shortName + ' organization can only be viewed by the users of the same organization or the Secretariat.' })
      return res.status(403).json(error.notSameOrgOrSecretariat())
    }

    const user = await userRepo.findOneByUserNameAndOrgUUID(username, orgUUID)
    if (!user) {
      logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because ' + username + ' does not exist for ' + shortName + ' organization.' })
      return res.status(404).json(error.userDne(username))
    }

    // check if the user is not the requester or if the requester is not a secretariat
    if ((shortName !== requesterShortName || username !== requesterUsername) && !isSecretariat) {
      // check if the requester is not and admin; if admin, the requester must be from the same org as the user
      if (!isAdmin || (isAdmin && shortName !== requesterShortName)) {
        logger.info({ uuid: req.ctx.uuid, message: 'The user can only be updated by the Secretariat, an Org admin or if the requester is the user.' })
        return res.status(403).json(error.notSameUserOrSecretariat())
      }
    }

    // Sets the name values to what currently exists in the database, this ensures data is retained during partial name updates
    newUser.name.first = user.name.first
    newUser.name.last = user.name.last
    newUser.name.middle = user.name.middle
    newUser.name.suffix = user.name.suffix

    for (const k in req.ctx.query) {
      const key = k.toLowerCase()

      if (key === 'new_username') {
        newUser.username = req.ctx.query.new_username
        changesRequirePrivilegedRole = true
      } else if (key === 'org_short_name') {
        newOrgShortName = req.ctx.query.org_short_name
        changesRequirePrivilegedRole = true
        if (!isSecretariat) {
          logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because ' + requesterUsername + ' is an Org Admin and tried to reassign the organization.' })
          return res.status(403).json(error.notAllowedToChangeOrganization())
        }
      } else if (key === 'name.first') {
        newUser.name.first = decodeEntities(req.ctx.query['name.first'])
      } else if (key === 'name.last') {
        newUser.name.last = decodeEntities(req.ctx.query['name.last'])
      } else if (key === 'name.middle') {
        newUser.name.middle = decodeEntities(req.ctx.query['name.middle'])
      } else if (key === 'name.suffix') {
        newUser.name.suffix = decodeEntities(req.ctx.query['name.suffix'])
      } else if (key === 'active') {
        newUser.active = booleanIsTrue(req.ctx.query.active)
        changesRequirePrivilegedRole = true
      } else if (key === 'active_roles.add') {
        if (Array.isArray(req.ctx.query['active_roles.add'])) {
          req.ctx.query['active_roles.add'].forEach(r => {
            addRoles.push(r)
          })
          changesRequirePrivilegedRole = true
        }
      } else if (key === 'active_roles.remove') {
        if (Array.isArray(req.ctx.query['active_roles.remove'])) {
          for (const r of req.ctx.query['active_roles.remove']) {
            if (r.toLowerCase() === 'admin' && requesterUsername === username && (isAdmin || isSecretariat)) {
              logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because ' + requesterUsername + ' is an Org Admin changing their own role.' })
              return res.status(403).json(error.notAllowedToSelfDemote())
            }
            removeRoles.push(r)
          }
          changesRequirePrivilegedRole = true
        }
      }
    }

    // Check for correct privileges if the requested changes require them
    if (changesRequirePrivilegedRole && !(isAdmin || isSecretariat)) {
      logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because ' + requesterUsername + ' user is not Org Admin or Secretariat to modify these fields.' })
      return res.status(403).json(error.notOrgAdminOrSecretariatUpdate())
    }

    // check if the new org exist
    if (newOrgShortName) {
      newUser.org_UUID = await orgRepo.getOrgUUID(newOrgShortName)

      if (!newUser.org_UUID) {
        logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because ' + newOrgShortName + ' organization does not exist.' })
        return res.status(404).json(error.orgDne(newOrgShortName, 'org_short_name', 'query'))
      }
    }

    // error if trying to set org to same org
    if (newOrgShortName === shortName) {
      logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because user is already in ' + newOrgShortName })
      return res.status(403).json(error.alreadyInOrg(newOrgShortName, user.username))
    }

    let agt = setAggregateUserObj({ username: username, org_UUID: orgUUID })

    // check if org has user of same username already
    if (newUser.username && newUser.org_UUID) {
      agt = setAggregateUserObj({ username: newUser.username, org_UUID: newUser.org_UUID })
      const duplicateUsers = await userRepo.find({ org_UUID: newUser.org_UUID, username: newUser.username })
      if (duplicateUsers.length) {
        logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because ' + newOrgShortName + ' organization contains a user with the same username.' })
        return res.status(403).json(error.duplicateUsername(newOrgShortName, newUser.username))
      }
    } else if (newUser.username) {
      agt = setAggregateUserObj({ username: newUser.username, org_UUID: orgUUID })
      const duplicateUsers = await userRepo.find({ org_UUID: orgUUID, username: newUser.username })
      if (duplicateUsers.length) {
        logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because ' + shortName + ' organization contains a user with the same username.' })
        return res.status(403).json(error.duplicateUsername(shortName, newUser.username))
      }
    } else if (newUser.org_UUID) {
      agt = setAggregateUserObj({ username: username, org_UUID: newUser.org_UUID })
      const duplicateUsers = await userRepo.find({ org_UUID: newUser.org_UUID, username: username })
      if (duplicateUsers.length) {
        logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because ' + newOrgShortName + ' organization contains a user with the same username.' })
        return res.status(403).json(error.duplicateUsername(newOrgShortName, username))
      }
    }

    // updating the user's roles
    const roles = user.authority.active_roles

    // adding roles
    addRoles.forEach(role => {
      if (!roles.includes(role)) {
        roles.push(role)
      }
    })

    const duplicateCheckedRoles = [...new Set(roles)] // Removes any possible duplicates that may occur from concurrent users

    // removing roles
    removeRoles.forEach(role => {
      const index = duplicateCheckedRoles.indexOf(role)

      if (index > -1) {
        duplicateCheckedRoles.splice(index, 1)
      }
    })

    newUser.authority.active_roles = duplicateCheckedRoles

    let result = await userRepo.updateByUserNameAndOrgUUID(username, orgUUID, newUser)
    if (result.n === 0) {
      logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because ' + username + ' does not exist for ' + shortName + ' organization.' })
      return res.status(404).json(error.userDne(username))
    }

    result = await userRepo.aggregate(agt)
    result = result.length > 0 ? result[0] : null

    let msgStr = ''
    if (Object.keys(req.ctx.query).length > 0) {
      msgStr = username + ' was successfully updated.'
    } else {
      msgStr = 'No updates were specified for ' + username + '.'
    }

    const responseMessage = {
      message: msgStr,
      updated: result
    }

    const payload = {
      action: 'update_user',
      change: username + ' was successfully updated.',
      req_UUID: req.ctx.uuid,
      org_UUID: await orgRepo.getOrgUUID(req.ctx.org),
      user: result
    }
    payload.user_UUID = await userRepo.getUserUUID(req.ctx.user, payload.org_UUID)
    logger.info(JSON.stringify(payload))
    return res.status(200).json(responseMessage)
  } catch (err) {
    next(err)
  }
}

// Called by PUT /org/{shortname}/user/{username}/reset_secret
async function resetSecret (req, res, next) {
  try {
    const requesterShortName = req.ctx.org
    const requesterUsername = req.ctx.user
    const username = req.ctx.params.username
    const orgShortName = req.ctx.params.shortname
    const userRepo = req.ctx.repositories.getUserRepository()
    const orgRepo = req.ctx.repositories.getOrgRepository()
    const isSecretariat = await orgRepo.isSecretariat(requesterShortName)
    const orgUUID = await orgRepo.getOrgUUID(orgShortName) // userUUID may be null if user does not exist
    if (!orgUUID) {
      logger.info({ uuid: req.ctx.uuid, messsage: orgShortName + ' organization does not exist.' })
      return res.status(404).json(error.orgDnePathParam(orgShortName))
    }

    if (orgShortName !== requesterShortName && !isSecretariat) {
      logger.info({ uuid: req.ctx.uuid, message: orgShortName + ' organization can only be viewed by the users of the same organization or the Secretariat.' })
      return res.status(403).json(error.notSameOrgOrSecretariat())
    }

    const oldUser = await userRepo.findOneByUserNameAndOrgUUID(username, orgUUID)
    if (!oldUser) {
      logger.info({ uuid: req.ctx.uuid, messsage: username + ' user does not exist.' })
      return res.status(404).json(error.userDne(username))
    }

    const isAdmin = await userRepo.isAdmin(requesterUsername, requesterShortName)
    // check if the user is not the requester or if the requester is not a secretariat
    if ((orgShortName !== requesterShortName || username !== requesterUsername) && !isSecretariat) {
      // check if the requester is not and admin; if admin, the requester must be from the same org as the user
      if (!isAdmin || (isAdmin && orgShortName !== requesterShortName)) {
        logger.info({ uuid: req.ctx.uuid, message: 'The api secret can only be reset by the Secretariat, an Org admin or if the requester is the user.' })
        return res.status(403).json(error.notSameUserOrSecretariat())
      }
    }

    const randomKey = cryptoRandomString({ length: getConstants().CRYPTO_RANDOM_STRING_LENGTH })
    oldUser.secret = await argon2.hash(randomKey) // store in db
    const user = await userRepo.updateByUserNameAndOrgUUID(oldUser.username, orgUUID, oldUser)
    if (user.n === 0) {
      logger.info({ uuid: req.ctx.uuid, message: 'The user could not be updated because ' + username + ' does not exist for ' + orgShortName + ' organization.' })
      return res.status(404).json(error.userDne(username))
    }

    logger.info({ uuid: req.ctx.uuid, message: `The API secret was successfully reset and sent to ${username}` })
    const payload = {
      action: 'reset_userAPIkey',
      change: 'API secret was successfully reset.',
      req_UUID: req.ctx.uuid,
      org_UUID: await orgRepo.getOrgUUID(req.ctx.org)
    }
    payload.user_UUID = await userRepo.getUserUUID(req.ctx.user, payload.org_UUID)
    logger.info(JSON.stringify(payload))
    return res.status(200).json({ 'API-secret': randomKey })
  } catch (err) {
    next(err)
  }
}

function setAggregateOrgObj (query) {
  return [
    {
      $match: query
    },
    {
      $project: {
        _id: false,
        UUID: true,
        short_name: true,
        name: true,
        'authority.active_roles': true,
        'policies.id_quota': true,
        time: true
      }
    }
  ]
}

function setAggregateUserObj (query) {
  return [
    {
      $match: query
    },
    {
      $project: {
        _id: false,
        username: true,
        name: true,
        UUID: true,
        org_UUID: true,
        active: true,
        'authority.active_roles': true,
        time: true
      }
    }
  ]
}

function parseUserName (newUser) {
  if (newUser.name) {
    if (!newUser.name.first) {
      newUser.name.first = ''
    }
    if (!newUser.name.last) {
      newUser.name.last = ''
    }
    if (!newUser.name.middle) {
      newUser.name.middle = ''
    }
    if (!newUser.name.suffix) {
      newUser.name.suffix = ''
    }
  }

  return newUser.name
}

module.exports = {
  ORG_ALL: getOrgs,
  ORG_SINGLE: getOrg,
  ORG_CREATE_SINGLE: createOrg,
  ORG_UPDATE_SINGLE: updateOrg,
  USER_ALL: getUsers,
  ORG_ID_QUOTA: getOrgIdQuota,
  USER_SINGLE: getUser,
  USER_CREATE_SINGLE: createUser,
  USER_UPDATE_SINGLE: updateUser,
  USER_RESET_SECRET: resetSecret
}


text:
const idrErr = require('../../utils/error')

class OrgControllerError extends idrErr.IDRError {
  orgDnePathParam (shortname) { // org
    const err = {}
    err.error = 'ORG_DNE_PARAM'
    err.message = `The '${shortname}' organization designated by the shortname path parameter does not exist.`
    return err
  }

  userDne (username) { // org
    const err = {}
    err.error = 'USER_DNE'
    err.message = `The user '${username}' designated by the username parameter does not exist.`
    return err
  }

  notSameOrgOrSecretariat () { // org
    const err = {}
    err.error = 'NOT_SAME_ORG_OR_SECRETARIAT'
    err.message = 'This information can only be viewed by the users of the same organization or the Secretariat.'
    return err
  }

  notAllowedToChangeOrganization () {
    const err = {}
    err.error = 'NOT_ALLOWED_TO_CHANGE_ORGANIZATION'
    err.message = 'Only the Secretariat can change the organization for a user.'
    return err
  }

  orgExists (shortname) { // org
    const err = {}
    err.error = 'ORG_EXISTS'
    err.message = `The '${shortname}' organization already exists.`
    return err
  }

  userExists (username) { // org
    const err = {}
    err.error = 'USER_EXISTS'
    err.message = `The user '${username}' already exists.`
    return err
  }

  uuidProvided (creationType) {
    const err = {}
    err.error = 'UUID_PROVIDED'
    err.message = `Providing UUIDs for ${creationType} creation or update is not allowed.`
    return err
  }

  duplicateUsername (shortname, username) { // org
    const err = {}
    err.error = 'DUPLICATE_USERNAME'
    err.message = `The user could not be updated because the '${shortname}' organization contains another user with the username '${username}'.`
    return err
  }

  alreadyInOrg (shortname, username) { // org
    const err = {}
    err.error = 'USER_ALREADY_IN_ORG'
    err.message = `The user could not be updated because the user '${username}' already belongs to the '${shortname}' organization.`
    return err
  }

  duplicateShortname (shortname) { // org
    const err = {}
    err.error = 'DUPLICATE_SHORTNAME'
    err.message = `The organization cannot be renamed as '${shortname}' because this shortname is used by another organization.`
    return err
  }

  paramDne (param) { // org
    const err = {}
    err.error = 'BAD_PARAMETER_NAME'
    err.message = `'${param}' is not a valid parameter.`
    return err
  }

  notAllowedToSelfDemote () {
    const err = {}
    err.error = 'NOT_ALLOWED_TO_SELF_DEMOTE'
    err.message = 'Please have another admin user from your organization change your role.'
    return err
  }

  userLimitReached () {
    const err = {}
    err.error = 'NUMBER_OF_USERS_IN_ORG_LIMIT_REACHED'
    err.message = 'The requested user can not be created and added to the organization because the organization has hit its limit of 100 users. Contact the Secretariat.'
    return err
  }
}

module.exports = {
  OrgControllerError
}


text:
const express = require('express')
const router = express.Router()
const mw = require('../../middleware/middleware')
const errorMsgs = require('../../middleware/errorMessages')
const controller = require('./cve-id.controller')
const { param, query } = require('express-validator')
const { parseGetParams, parsePostParams, parseError } = require('./cve-id.middleware')
const getConstants = require('../../constants').getConstants
const CONSTANTS = getConstants()
const toDate = require('../../utils/utils').toDate

const CHOICES = [CONSTANTS.CVE_STATES.REJECTED, CONSTANTS.CVE_STATES.PUBLISHED, CONSTANTS.CVE_STATES.RESERVED]
const MODIFYTARGETS = [CONSTANTS.CVE_STATES.REJECTED, CONSTANTS.CVE_STATES.RESERVED]

router.get('/cve-id',
  /*
  #swagger.tags = ['CVE ID']
  #swagger.operationId = 'cveIdGetFiltered'
  #swagger.summary = "Retrieves information about CVE IDs after applying the query parameters as filters (accessible to all registered users)"
  #swagger.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

  #swagger.parameters['$ref'] = [
    '#/components/parameters/cveIdGetFilteredState',
    '#/components/parameters/cveIdGetFilteredCveIdYear',
    '#/components/parameters/cveIdGetFilteredTimeReservedLt',
    '#/components/parameters/cveIdGetFilteredTimeReservedGt',
    '#/components/parameters/cveIdGetFilteredTimeModifiedLt',
    '#/components/parameters/cveIdGetFilteredTimeModifiedGt',
    '#/components/parameters/pageQuery',
    '#/components/parameters/apiEntityHeader',
    '#/components/parameters/apiUserHeader',
    '#/components/parameters/apiSecretHeader'
  ]
  #swagger.responses[200] = {
    description: 'A filtered list of information about CVE IDs owned by the organization, along with pagination fields if results span multiple pages of data',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/cve-id/list-cve-ids-response.json' }
      }
    }
  }
  #swagger.responses[400] = {
    description: 'Bad Request',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/bad-request.json' }
      }
    }
  }
  #swagger.responses[401] = {
    description: 'Not Authenticated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[403] = {
    description: 'Forbidden',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[404] = {
    description: 'Not Found',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[500] = {
    description: 'Internal Server Error',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  */
  mw.validateUser,
  query().custom((query) => { return mw.validateQueryParameterNames(query, ['page', 'state', 'cve_id_year', 'time_reserved.lt', 'time_reserved.gt', 'time_modified.lt', 'time_modified.gt']) }),
  query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }),
  query(['state']).optional().isString().trim().escape().customSanitizer(val => { return val.toUpperCase() }).isIn(CHOICES).withMessage(errorMsgs.ID_STATES),
  query(['cve_id_year']).optional().isNumeric().matches(/^[0-9]{4}$/),
  query(['time_reserved.lt']).optional().isString().trim().escape().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT),
  query(['time_reserved.gt']).optional().isString().trim().escape().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT),
  query(['time_modified.lt']).optional().isString().trim().escape().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT),
  query(['time_modified.gt']).optional().isString().trim().escape().customSanitizer(val => { return toDate(val) }).not().isEmpty().withMessage(errorMsgs.TIMESTAMP_FORMAT),
  parseError,
  parseGetParams,
  controller.CVEID_GET_FILTER)

router.post('/cve-id',
  /*
  #swagger.tags = ['CVE ID']
  #swagger.operationId = 'cveIdReserve'
  #swagger.summary = "Reserves CVE IDs for the organization provided in the short_name query parameter (accessible to CNAs and Secretariat)"
  #swagger.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
"
  #swagger.parameters['$ref'] = [
    '#/components/parameters/amount',
    '#/components/parameters/batch_type',
    '#/components/parameters/cve_year',
    '#/components/parameters/short_name',
    '#/components/parameters/apiEntityHeader',
    '#/components/parameters/apiUserHeader',
    '#/components/parameters/apiSecretHeader'
  ]
  #swagger.responses[200] = {
    description: 'A list of the newly reserved CVE IDs',
    content: {
      'application/json': {
        schema: { $ref: '../schemas/cve-id/create-cve-ids-response.json' }
      }
    }
  }
  #swagger.responses[206] = {
    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',
    content: {
      'application/json': {
        schema: { $ref: '../schemas/cve-id/create-cve-ids-partial-response.json' }
      }
    }
  }
  #swagger.responses[400] = {
    description: 'Bad Request',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/bad-request.json' }
      }
    }
  }
  #swagger.responses[401] = {
    description: 'Not Authenticated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[403] = {
    description: 'Forbidden',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[404] = {
    description: 'Not Found',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[500] = {
    description: 'Internal Server Error',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  */
  mw.validateUser,
  mw.onlyCnas,
  query().custom((query) => { return mw.validateQueryParameterNames(query, ['amount', 'batch_type', 'short_name', 'cve_year']) }),
  query(['amount']).isInt(),
  query(['batch_type']).optional().isString().trim().escape().customSanitizer(val => { return val.toLowerCase() }),
  query(['short_name']).isString().trim().escape().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }),
  query(['cve_year']).isNumeric().matches(/^[0-9]{4}$/),
  parseError,
  parsePostParams,
  controller.CVEID_RESERVE)

router.get('/cve-id/:id',
  /*
  #swagger.tags = ['CVE ID']
  #swagger.operationId = 'cveIdGetSingle'
  #swagger.summary = "Retrieves information about the specified CVE ID (accessible to all users)"
  #swagger.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

  #swagger.parameters['id'] = { description: 'The id of the CVE ID information to retrieve' }
  #swagger.parameters['$ref'] = [
    '#/components/parameters/apiEntityHeader',
    '#/components/parameters/apiUserHeader',
    '#/components/parameters/apiSecretHeader'
  ]
  #swagger.responses[200] = {
    description: 'The requested CVE ID information is returned',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/cve-id/get-cve-id-response.json' }
      }
    }
  }
  #swagger.responses[400] = {
    description: 'Bad Request',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/bad-request.json' }
      }
    }
  }
  #swagger.responses[401] = {
    description: 'Not Authenticated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[403] = {
    description: 'Forbidden',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[404] = {
    description: 'Not Found',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[429] = {
    description: 'Too Many Requests',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[500] = {
    description: 'Internal Server Error',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  */
  mw.rateLimiter,
  mw.optionallyValidateUser,
  param(['id']).isString().matches(CONSTANTS.CVE_ID_REGEX),
  parseError,
  parseGetParams,
  controller.CVEID_GET_SINGLE)

router.put('/cve-id/:id',
  /*
  #swagger.tags = ['CVE ID']
  #swagger.operationId = 'cveIdUpdateSingle'
  #swagger.summary = "Updates information related to the specified CVE ID (accessible to CNAs and Secretariat)"
  #swagger.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
"
  #swagger.parameters['id'] = { description: 'The id of the CVE ID to update' }
  #swagger.parameters['$ref'] = [
    '#/components/parameters/org`,
    '#/components/parameters/state',
    '#/components/parameters/apiEntityHeader',
    '#/components/parameters/apiUserHeader',
    '#/components/parameters/apiSecretHeader'
  ]
  #swagger.responses[200] = {
    description: 'The updated CVE ID information is returned',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/cve-id/update-cve-id-response.json' }
      }
    }
  }
  #swagger.responses[400] = {
    description: 'Bad Request',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/bad-request.json' }
      }
    }
  }
  #swagger.responses[401] = {
    description: 'Not Authenticated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[403] = {
    description: 'Forbidden',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[404] = {
    description: 'Not Found',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[500] = {
    description: 'Internal Server Error',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  */
  mw.validateUser,
  mw.onlyCnas,
  param(['id']).isString().matches(CONSTANTS.CVE_ID_REGEX),
  query().custom((query) => { return mw.validateQueryParameterNames(query, ['state', 'org']) }),
  query(['state']).optional().isString().trim().escape().customSanitizer(val => { return val.toUpperCase() }).isIn(MODIFYTARGETS).withMessage(errorMsgs.ID_MODIFY_STATES),
  query(['org']).optional().isString().trim().escape(),
  parseError,
  parsePostParams,
  mw.cnaMustOwnID,
  controller.CVEID_UPDATE_SINGLE)

router.post('/cve-id-range/:year',
  /*
  #swagger.tags = ['CVE ID']
  #swagger.operationId = 'cveIdRangeCreate'
  #swagger.summary = "Creates a CVE-ID-Range for the specified year (accessible to Secretariat)"
  #swagger.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
"
  #swagger.parameters['year'] = {
    description: 'The year of the CVE-ID-Range',
    schema: {
      type: 'integer'
    }
  }
  #swagger.parameters['$ref'] = [
    '#/components/parameters/apiEntityHeader',
    '#/components/parameters/apiUserHeader',
    '#/components/parameters/apiSecretHeader'
  ]
  #swagger.responses[200] = {
    description: 'The CVE-ID-Range was created',
  }
  #swagger.responses[400] = {
    description: 'Bad Request',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/bad-request.json' }
      }
    }
  }
  #swagger.responses[401] = {
    description: 'Not Authenticated',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[403] = {
    description: 'Forbidden',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[404] = {
    description: 'Not Found',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  #swagger.responses[500] = {
    description: 'Internal Server Error',
    content: {
      "application/json": {
        schema: { $ref: '../schemas/errors/generic.json' }
      }
    }
  }
  */
  mw.validateUser,
  mw.onlySecretariat,
  param(['year']).isNumeric().matches(/^[0-9]{4}$/),
  parseError,
  parsePostParams,
  controller.CVEID_RANGE_CREATE)

module.exports = router