diff --git a/api-docs/openapi.json b/api-docs/openapi.json index f9fbbb5bd..84fe59bcf 100644 --- a/api-docs/openapi.json +++ b/api-docs/openapi.json @@ -1,7 +1,7 @@ { "openapi": "3.0.2", "info": { - "version": "2.8.3", + "version": "2.8.4", "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:
CVE data is to be in the JSON 5.2 CVE Record format. Details of the JSON 5.2 schema are located here.
Contact the CVE Services team", "contact": { @@ -1893,14 +1893,14 @@ } } }, - "/registry/org": { + "/org": { "get": { "tags": [ - "Registry Organization" + "Organization" ], - "summary": "Retrieves all registry organizations (accessible to Secretariat)", - "description": "User must belong to an organization with the Secretariat role
Secretariat: Retrieves information about all registry organizations
", - "operationId": "registryOrgAll", + "summary": "Retrieves all organizations (accessible to Secretariat)", + "description": "User must belong to an organization with the Secretariat role
Secretariat: Retrieves information about all organizations
", + "operationId": "orgAll", "parameters": [ { "$ref": "#/components/parameters/pageQuery" @@ -1917,11 +1917,18 @@ ], "responses": { "200": { - "description": "Returns information about all registry organizations, along with pagination fields if results span multiple pages of data", + "description": "Returns information about all organizations, along with pagination fields if results span multiple pages of data", "content": { "application/json": { "schema": { - "$ref": "../schemas/registry-org/list-registry-orgs-response.json" + "oneOf": [ + { + "$ref": "../schemas/org/list-orgs-response.json" + }, + { + "$ref": "../schemas/registry-org/list-registry-orgs-response.json" + } + ] } } } @@ -1980,10 +1987,10 @@ }, "post": { "tags": [ - "Registry Organization" + "Organization" ], - "summary": "Creates an organization (accessible to Secretariat)", - "description": "User must belong to an organization with the Secretariat role
Secretariat: Creates a new organization
", + "summary": "Creates an organization as specified in the request body (accessible to Secretariat)", + "description": "User must belong to an organization with the Secretariat role
Secretariat: Creates an organization
", "operationId": "orgCreateSingle", "parameters": [ { @@ -1998,11 +2005,18 @@ ], "responses": { "200": { - "description": "Returns information about all organizations, along with pagination fields if results span multiple pages of data", + "description": "Returns information about the organization created", "content": { "application/json": { "schema": { - "$ref": "../schemas/registry-org/list-registry-orgs-response.json" + "oneOf": [ + { + "$ref": "../schemas/org/create-org-response.json" + }, + { + "$ref": "../schemas/registry-org/create-registry-org-response.json" + } + ] } } } @@ -2063,54 +2077,30 @@ "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "../schemas/registry-org/SecretariatOrg.json" - }, - { - "$ref": "../schemas/registry-org/CNAOrg.json" - }, - { - "$ref": "../schemas/registry-org/ADPOrg.json" - }, - { - "$ref": "../schemas/registry-org/BulkDownloadOrg.json" - } - ] - }, - "example": { - "short_name": "fake_company", - "long_name": "Fake Company", - "id_quota": 1000, - "authority": [ - "CNA" - ] + "$ref": "../schemas/org/create-org-request.json" } } } } } }, - "/registry/org/{shortname}/users": { + "/org/{identifier}": { "get": { "tags": [ - "Registry User" + "Organization" ], - "summary": "Retrieves all users for the organization with the specified short name (accessible to same-organization users or Secretariat)", - "description": "Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
Regular, CNA & Admin Users: Retrieves information about users in the same organization
Secretariat: Retrieves all user information for any organization
", - "operationId": "userOrgAll", + "summary": "Retrieves information about the organization specified by short name or UUID (accessible to same-organization users or Secretariat)", + "description": "Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
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
", + "operationId": "orgSingle", "parameters": [ { - "name": "shortname", + "name": "identifier", "in": "path", "required": true, "schema": { "type": "string" }, - "description": "The shortname of the organization" - }, - { - "$ref": "#/components/parameters/pageQuery" + "description": "The shortname or UUID of the organization" }, { "$ref": "#/components/parameters/apiEntityHeader" @@ -2124,33 +2114,11 @@ ], "responses": { "200": { - "description": "Returns all users for the organization, along with pagination fields if results span multiple pages of data", + "description": "Returns the organization information", "content": { "application/json": { "schema": { - "$ref": "../schemas/registry-user/list-registry-users-response.json" - }, - "example": { - "totalCount": 1, - "itemsPerPage": 100, - "pageCount": 1, - "currentPage": 1, - "prevPage": null, - "nextPage": null, - "users": [ - { - "UUID": "fe566221-6a2c-4279-8800-4d3795325997", - "username": "jdoe", - "name": { - "first": "John", - "last": "Doe" - }, - "role": "ADMIN", - "status": "active", - "created": "2021-02-12T17:15:37.382Z", - "last_updated": "2021-02-12T17:15:37.382Z" - } - ] + "$ref": "../schemas/org/get-org-response.json" } } } @@ -2208,14 +2176,14 @@ } } }, - "/registry/org/{shortname}/id_quota": { - "get": { + "/org/{shortname}": { + "put": { "tags": [ - "Registry Organization" + "Organization" ], - "summary": "Retrieves an organization's CVE ID quota (accessible to same-organization users or Secretariat)", - "description": "Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
Regular, CNA & Admin Users: Retrieves the CVE ID quota for the user's organization
Secretariat: Retrieves the CVE ID quota for any organization
", - "operationId": "orgIdQuota", + "summary": "Updates information about the organization specified by short name (accessible to Secretariat)", + "description": "User must belong to an organization with the Secretariat role
Secretariat: Updates any organization's information
", + "operationId": "orgUpdateSingle", "parameters": [ { "name": "shortname", @@ -2226,6 +2194,21 @@ }, "description": "The shortname of the organization" }, + { + "$ref": "#/components/parameters/id_quota" + }, + { + "$ref": "#/components/parameters/name" + }, + { + "$ref": "#/components/parameters/newShortname" + }, + { + "$ref": "#/components/parameters/active_roles_add" + }, + { + "$ref": "#/components/parameters/active_roles_remove" + }, { "$ref": "#/components/parameters/apiEntityHeader" }, @@ -2238,11 +2221,11 @@ ], "responses": { "200": { - "description": "Returns the CVE ID quota for an organization", + "description": "Returns information about the organization updated", "content": { "application/json": { "schema": { - "$ref": "../schemas/registry-org/get-registry-org-quota-response.json" + "$ref": "../schemas/org/update-org-response.json" } } } @@ -2300,47 +2283,23 @@ } } }, - "/registry/org/{identifier}": { + "/org/{shortname}/id_quota": { "get": { "tags": [ - "Registry Organization" + "Organization" ], - "summary": "Retrieves information about the registry organization specified by short name or UUID (accessible to same-organization users or Secretariat)", - "description": "Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
Regular, CNA & Admin Users: Retrieves registry organization record for the specified shortname or UUID if it is the user's organization
Secretariat: Retrieves information about any registry organization
", - "operationId": "registryOrgSingle", + "summary": "Retrieves an organization's CVE ID quota (accessible to same-organization users or Secretariat)", + "description": "Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
Regular, CNA & Admin Users: Retrieves the CVE ID quota for the user's organization
Secretariat: Retrieves the CVE ID quota for any organization
", + "operationId": "orgIdQuota", "parameters": [ { - "name": "identifier", + "name": "shortname", "in": "path", "required": true, "schema": { "type": "string" }, - "description": "The shortname or UUID of the registry organization" - }, - { - "name": "expand", - "in": "query", - "description": "Optional expanded related data. Accepted value: users.", - "required": false, - "schema": { - "type": "object", - "properties": { - "type": { - "type": "string", - "example": "string" - }, - "enum": { - "type": "array", - "example": [ - "users" - ], - "items": { - "type": "string" - } - } - } - } + "description": "The shortname of the organization" }, { "$ref": "#/components/parameters/apiEntityHeader" @@ -2354,11 +2313,11 @@ ], "responses": { "200": { - "description": "Returns the registry organization information", + "description": "Returns the CVE ID quota for an organization", "content": { "application/json": { "schema": { - "$ref": "../schemas/registry-org/get-registry-org-response.json" + "$ref": "../schemas/org/get-org-quota-response.json" } } } @@ -2416,14 +2375,14 @@ } } }, - "/registry/org/{shortname}/user/{username}": { + "/org/{shortname}/users": { "get": { "tags": [ - "Registry User" + "Users" ], - "summary": "Retrieves information about a user for the specified username and organization short name (accessible to same-organization users or Secretariat)", - "description": "Authenticated users can access this endpoint only for users in their own organization. Secretariat users can access any user.
Regular, CNA & Admin Users: Retrieves information about a registry user in the same organization
Secretariat: Retrieves any registry user's information
", - "operationId": "registryUserSingle", + "summary": "Retrieves all users for the organization with the specified short name (accessible to same-organization users or Secretariat)", + "description": "Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
Regular, CNA & Admin Users: Retrieves information about users in the same organization
Secretariat: Retrieves all user information for any organization
", + "operationId": "userOrgAll", "parameters": [ { "name": "shortname", @@ -2431,15 +2390,11 @@ "required": true, "schema": { "type": "string" - } + }, + "description": "The shortname of the organization" }, { - "name": "username", - "in": "path", - "required": true, - "schema": { - "type": "string" - } + "$ref": "#/components/parameters/pageQuery" }, { "$ref": "#/components/parameters/apiEntityHeader" @@ -2453,11 +2408,11 @@ ], "responses": { "200": { - "description": "Returns information about the specified registry user", + "description": "Returns all users for the organization, along with pagination fields if results span multiple pages of data", "content": { "application/json": { "schema": { - "$ref": "../schemas/registry-user/get-registry-user-response.json" + "$ref": "../schemas/user/list-users-response.json" } } } @@ -2513,14 +2468,16 @@ } } } - }, - "put": { + } + }, + "/org/{shortname}/user": { + "post": { "tags": [ - "Registry User" + "Users" ], - "summary": "Updates information about a user for the specified username and organization shortname (accessible to self, same-organization Admins, or Secretariat)", - "description": "Authenticated users can update their own name fields. Organization admins can update users in their organization. Secretariat users can update users in any organization.
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.
", - "operationId": "registryUserUpdateSingle", + "summary": "Create a user with the provided short name as the owning organization (accessible to Secretariat or target organization Admin)", + "description": "User must belong to an organization with the Secretariat role or be an Admin of the target organization
Admin User: Creates a user for the Admin's organization
Secretariat: Creates a user for any organization
", + "operationId": "userCreateSingle", "parameters": [ { "name": "shortname", @@ -2531,21 +2488,6 @@ }, "description": "The shortname of the organization" }, - { - "name": "username", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The username of the user" - }, - { - "$ref": "#/components/parameters/active" - }, - { - "$ref": "#/components/parameters/orgShortname" - }, { "$ref": "#/components/parameters/apiEntityHeader" }, @@ -2558,25 +2500,11 @@ ], "responses": { "200": { - "description": "Returns the updated user information", + "description": "Returns the new user information (with the secret)", "content": { "application/json": { "schema": { - "$ref": "../schemas/registry-user/update-registry-user-response.json" - }, - "example": { - "message": "jdoe was successfully updated.", - "updated": { - "UUID": "fe566221-6a2c-4279-8800-4d3795325997", - "username": "jdoe", - "name": { - "first": "John", - "last": "Doe" - }, - "status": "active", - "created": "2021-02-12T17:15:37.382Z", - "last_updated": "2021-02-12T17:15:37.382Z" - } + "$ref": "../schemas/user/create-user-response.json" } } } @@ -2631,17 +2559,27 @@ } } } - } - } - }, - "/registry/org/{shortname}": { - "put": { - "tags": [ - "Registry Organization" - ], - "summary": "Updates information about the organization specified by short name (accessible to Secretariat or same-organization Admin)", - "description": "User must belong to an organization with the Secretariat role or be an Admin of the requested organization.
With Joint Approval required for the following fields:
Secretariat: Updates any organization's information
Organization Admin: Requests changes to its organization's information
Authenticated users can access this endpoint only for users in their own organization. Secretariat users can access any user.
Regular, CNA & Admin Users: Retrieves information about a user in the same organization
Secretariat: Retrieves any user's information
", + "operationId": "userSingle", "parameters": [ { "name": "shortname", @@ -2652,6 +2590,15 @@ }, "description": "The shortname of the organization" }, + { + "name": "username", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The username of the user" + }, { "$ref": "#/components/parameters/apiEntityHeader" }, @@ -2664,11 +2611,11 @@ ], "responses": { "200": { - "description": "Returns information about the organization updated", + "description": "Returns information about the specified user", "content": { "application/json": { "schema": { - "$ref": "../schemas/registry-org/update-registry-org-response.json" + "$ref": "../schemas/user/get-user-response.json" } } } @@ -2723,35 +2670,15 @@ } } } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "../schemas/registry-org/update-registry-org-request.json" - }, - "example": { - "short_name": "fake_company", - "long_name": "Fake Company", - "id_quota": 1000, - "authority": [ - "CNA" - ] - } - } - } } - } - }, - "/registry/org/{shortname}/user": { - "post": { + }, + "put": { "tags": [ - "Registry User" + "Users" ], - "summary": "Create a user with the provided short name as the owning organization (accessible to Secretariat or target organization Admin)", - "description": "User must belong to an organization with the Secretariat role or be an Admin of the target organization
Admin User: Creates a user for the Admin's organization
Secretariat: Creates a user for any organization
", - "operationId": "registryUserCreateSingle", + "summary": "Updates information about a user for the specified username and organization shortname (accessible to self, same-organization Admins, or Secretariat)", + "description": "Authenticated users can update their own name fields. Organization admins can update users in their organization. Secretariat users can update users in any organization.
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.
", + "operationId": "userUpdateSingle", "parameters": [ { "name": "shortname", @@ -2762,6 +2689,42 @@ }, "description": "The shortname of the organization" }, + { + "name": "username", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The username of the user" + }, + { + "$ref": "#/components/parameters/active" + }, + { + "$ref": "#/components/parameters/activeUserRolesAdd" + }, + { + "$ref": "#/components/parameters/activeUserRolesRemove" + }, + { + "$ref": "#/components/parameters/nameFirst" + }, + { + "$ref": "#/components/parameters/nameLast" + }, + { + "$ref": "#/components/parameters/nameMiddle" + }, + { + "$ref": "#/components/parameters/nameSuffix" + }, + { + "$ref": "#/components/parameters/newUsername" + }, + { + "$ref": "#/components/parameters/orgShortname" + }, { "$ref": "#/components/parameters/apiEntityHeader" }, @@ -2774,26 +2737,11 @@ ], "responses": { "200": { - "description": "Returns the new user information (with the secret)", + "description": "Returns the updated user information", "content": { "application/json": { "schema": { - "$ref": "../schemas/registry-user/create-registry-user-response.json" - } - }, - "example": { - "message": "jdoe was successfully created.", - "created": { - "UUID": "fe566221-6a2c-4279-8800-4d3795325997", - "username": "jdoe", - "name": { - "first": "John", - "last": "Doe" - }, - "status": "active", - "secret": "12345-abcde-67890", - "created": "2021-02-12T17:15:37.382Z", - "last_updated": "2021-02-12T17:15:37.382Z" + "$ref": "../schemas/user/update-user-response.json" } } } @@ -2848,31 +2796,13 @@ } } } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "../schemas/registry-user/create-registry-user-request.json" - } - }, - "example": { - "username": "jdoe", - "status": "active", - "name": { - "first": "John", - "last": "Doe" - } - } - } } } }, - "/registry/org/{shortname}/user/{username}/reset_secret": { + "/org/{shortname}/user/{username}/reset_secret": { "put": { "tags": [ - "Registry User" + "Users" ], "summary": "Reset the API key for a user (accessible to self, same-organization Admins, or Secretariat)", "description": "Authenticated users can reset their own API secret. Organization admins can reset users in their organization. Secretariat users can reset any user's API secret.
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
", @@ -2970,32 +2900,17 @@ } } }, - "/registry/org/{shortname}/user/{username}/grant-role": { - "post": { + "/users": { + "get": { "tags": [ - "Registry User" + "Users" ], - "summary": "Grants a role to a user (accessible to Secretariat or Org Admin)", - "description": "User must belong to an organization with the Secretariat role or be an Admin of the target organization
Admin User: Grants a role to a user in the Admin's organization
Secretariat: Grants a role to a user in any organization
", - "operationId": "registryUserGrantRole", + "summary": "Retrieves information about all registered users (accessible to Secretariat)", + "description": "User must belong to an organization with the Secretariat role
Secretariat: Retrieves information about all users for all organizations
", + "operationId": "userAll", "parameters": [ { - "name": "shortname", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The shortname of the organization" - }, - { - "name": "username", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The username of the user" + "$ref": "#/components/parameters/pageQuery" }, { "$ref": "#/components/parameters/apiEntityHeader" @@ -3009,16 +2924,11 @@ ], "responses": { "200": { - "description": "Role granted successfully", + "description": "Returns all users, along with pagination fields if results span multiple pages of data.", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "message": { - "type": "string" - } - } + "$ref": "../schemas/user/list-users-response.json" } } } @@ -3073,56 +2983,35 @@ } } } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "role": { - "type": "string", - "enum": [ - "ADMIN" - ] - } - }, - "required": [ - "role" - ] - } - } + } + } + }, + "/health-check": { + "get": { + "tags": [ + "Utilities" + ], + "summary": "Checks that the system is running (accessible to all users)", + "description": "Endpoint is accessible to all
Returns a 200 response code when CVE Services are running
", + "operationId": "healthCheck", + "responses": { + "200": { + "description": "Returns a 200 response code" } } } }, - "/registry/org/{shortname}/user/{username}/revoke-role": { - "post": { + "/registry/org": { + "get": { "tags": [ - "Registry User" + "Registry Organization" ], - "summary": "Revokes a role from a user (accessible to Secretariat or Org Admin)", - "description": "User must belong to an organization with the Secretariat role or be an Admin of the target organization
Admin User: Revokes a role from a user in the Admin's organization
Secretariat: Revokes a role from a user in any organization
", - "operationId": "registryUserRevokeRole", + "summary": "Retrieves all registry organizations (accessible to Secretariat)", + "description": "User must belong to an organization with the Secretariat role
Secretariat: Retrieves information about all registry organizations
", + "operationId": "registryOrgAll", "parameters": [ { - "name": "shortname", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The shortname of the organization" - }, - { - "name": "username", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The username of the user" + "$ref": "#/components/parameters/pageQuery" }, { "$ref": "#/components/parameters/apiEntityHeader" @@ -3136,16 +3025,92 @@ ], "responses": { "200": { - "description": "Role revoked successfully", + "description": "Returns information about all registry organizations, along with pagination fields if results span multiple pages of data", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "message": { - "type": "string" - } - } + "$ref": "../schemas/registry-org/list-registry-orgs-response.json" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/bad-request.json" + } + } + } + }, + "401": { + "description": "Not Authenticated", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } + } + } + } + } + }, + "post": { + "tags": [ + "Registry Organization" + ], + "summary": "Creates an organization (accessible to Secretariat)", + "description": "User must belong to an organization with the Secretariat role
Secretariat: Creates a new organization
", + "operationId": "registryOrgCreateSingle", + "parameters": [ + { + "$ref": "#/components/parameters/apiEntityHeader" + }, + { + "$ref": "#/components/parameters/apiUserHeader" + }, + { + "$ref": "#/components/parameters/apiSecretHeader" + } + ], + "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/registry-org/list-registry-orgs-response.json" } } } @@ -3206,32 +3171,367 @@ "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "role": { - "type": "string", - "enum": [ - "ADMIN" - ] + "anyOf": [ + { + "$ref": "../schemas/registry-org/SecretariatOrg.json" + }, + { + "$ref": "../schemas/registry-org/CNAOrg.json" + }, + { + "$ref": "../schemas/registry-org/ADPOrg.json" + }, + { + "$ref": "../schemas/registry-org/BulkDownloadOrg.json" } - }, - "required": [ - "role" ] + }, + "example": { + "short_name": "fake_company", + "long_name": "Fake Company", + "id_quota": 1000, + "authority": [ + "CNA" + ] + } + } + } + } + } + }, + "/registry/org/{shortname}/users": { + "get": { + "tags": [ + "Registry User" + ], + "summary": "Retrieves all users for the organization with the specified short name (accessible to same-organization users or Secretariat)", + "description": "Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
Regular, CNA & Admin Users: Retrieves information about users in the same organization
Secretariat: Retrieves all user information for any organization
", + "operationId": "registryOrgUsersAll", + "parameters": [ + { + "name": "shortname", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The shortname of the organization" + }, + { + "$ref": "#/components/parameters/pageQuery" + }, + { + "$ref": "#/components/parameters/apiEntityHeader" + }, + { + "$ref": "#/components/parameters/apiUserHeader" + }, + { + "$ref": "#/components/parameters/apiSecretHeader" + } + ], + "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/registry-user/list-registry-users-response.json" + }, + "example": { + "totalCount": 1, + "itemsPerPage": 100, + "pageCount": 1, + "currentPage": 1, + "prevPage": null, + "nextPage": null, + "users": [ + { + "UUID": "fe566221-6a2c-4279-8800-4d3795325997", + "username": "jdoe", + "name": { + "first": "John", + "last": "Doe" + }, + "role": "ADMIN", + "status": "active", + "created": "2021-02-12T17:15:37.382Z", + "last_updated": "2021-02-12T17:15:37.382Z" + } + ] + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/bad-request.json" + } + } + } + }, + "401": { + "description": "Not Authenticated", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } + } + } + } + } + } + }, + "/registry/org/{shortname}/id_quota": { + "get": { + "tags": [ + "Registry Organization" + ], + "summary": "Retrieves an organization's CVE ID quota (accessible to same-organization users or Secretariat)", + "description": "Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
Regular, CNA & Admin Users: Retrieves the CVE ID quota for the user's organization
Secretariat: Retrieves the CVE ID quota for any organization
", + "operationId": "registryOrgIdQuota", + "parameters": [ + { + "name": "shortname", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The shortname of the organization" + }, + { + "$ref": "#/components/parameters/apiEntityHeader" + }, + { + "$ref": "#/components/parameters/apiUserHeader" + }, + { + "$ref": "#/components/parameters/apiSecretHeader" + } + ], + "responses": { + "200": { + "description": "Returns the CVE ID quota for an organization", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/registry-org/get-registry-org-quota-response.json" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/bad-request.json" + } + } + } + }, + "401": { + "description": "Not Authenticated", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } + } + } + } + } + } + }, + "/registry/org/{identifier}": { + "get": { + "tags": [ + "Registry Organization" + ], + "summary": "Retrieves information about the registry organization specified by short name or UUID (accessible to same-organization users or Secretariat)", + "description": "Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
Regular, CNA & Admin Users: Retrieves registry organization record for the specified shortname or UUID if it is the user's organization
Secretariat: Retrieves information about any registry organization
", + "operationId": "registryOrgSingle", + "parameters": [ + { + "name": "identifier", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The shortname or UUID of the registry organization" + }, + { + "name": "expand", + "in": "query", + "description": "Optional expanded related data. Accepted value: users.", + "required": false, + "schema": { + "type": "object", + "properties": { + "type": { + "type": "string", + "example": "string" + }, + "enum": { + "type": "array", + "example": [ + "users" + ], + "items": { + "type": "string" + } + } + } + } + }, + { + "$ref": "#/components/parameters/apiEntityHeader" + }, + { + "$ref": "#/components/parameters/apiUserHeader" + }, + { + "$ref": "#/components/parameters/apiSecretHeader" + } + ], + "responses": { + "200": { + "description": "Returns the registry organization information", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/registry-org/get-registry-org-response.json" + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/bad-request.json" + } + } + } + }, + "401": { + "description": "Not Authenticated", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/errors/generic.json" + } } } } } } }, - "/registry/org/{shortname}/conversation/{index}": { - "put": { + "/registry/org/{shortname}/user/{username}": { + "get": { "tags": [ - "Registry Organization" + "Registry User" ], - "summary": "Update the conversation at the given index for the given organization (accessible to Secretariat or original same-organization author)", - "description": "User must belong to an organization with the Secretariat role or be the original author of the conversation in the same organization
Original Author: Allowed to update only the message body of a conversation posted by them
Secretariat: Allowed to update the message body and/or visibility of any conversation
", - "operationId": "registryUserUpdateConversation", + "summary": "Retrieves information about a user for the specified username and organization short name (accessible to same-organization users or Secretariat)", + "description": "Authenticated users can access this endpoint only for users in their own organization. Secretariat users can access any user.
Regular, CNA & Admin Users: Retrieves information about a registry user in the same organization
Secretariat: Retrieves any registry user's information
", + "operationId": "registryUserSingle", "parameters": [ { "name": "shortname", @@ -3239,17 +3539,15 @@ "required": true, "schema": { "type": "string" - }, - "description": "The shortname of the organization" + } }, { - "name": "index", + "name": "username", "in": "path", "required": true, "schema": { "type": "string" - }, - "description": "The index of the conversation to update" + } }, { "$ref": "#/components/parameters/apiEntityHeader" @@ -3263,11 +3561,11 @@ ], "responses": { "200": { - "description": "Returns the updated conversation", + "description": "Returns information about the specified registry user", "content": { "application/json": { "schema": { - "$ref": "../schemas/conversation/update-conversation-response.json" + "$ref": "../schemas/registry-user/get-registry-user-response.json" } } } @@ -3323,26 +3621,38 @@ } } } - } - }, - "/org": { - "get": { + }, + "put": { "tags": [ - "Organization" + "Registry User" ], - "summary": "Retrieves all organizations (accessible to Secretariat)", - "description": "User must belong to an organization with the Secretariat role
Secretariat: Retrieves information about all organizations
", - "operationId": "orgAll", + "summary": "Updates information about a user for the specified username and organization shortname (accessible to self, same-organization Admins, or Secretariat)", + "description": "Authenticated users can update their own name fields. Organization admins can update users in their organization. Secretariat users can update users in any organization.
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.
", + "operationId": "registryUserUpdateSingle", "parameters": [ { - "name": "registry", - "in": "query", + "name": "shortname", + "in": "path", + "required": true, "schema": { "type": "string" - } + }, + "description": "The shortname of the organization" }, { - "$ref": "#/components/parameters/pageQuery" + "name": "username", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The username of the user" + }, + { + "$ref": "#/components/parameters/active" + }, + { + "$ref": "#/components/parameters/orgShortname" }, { "$ref": "#/components/parameters/apiEntityHeader" @@ -3356,18 +3666,25 @@ ], "responses": { "200": { - "description": "Returns information about all organizations, along with pagination fields if results span multiple pages of data", + "description": "Returns the updated user information", "content": { "application/json": { "schema": { - "oneOf": [ - { - "$ref": "../schemas/org/list-orgs-response.json" + "$ref": "../schemas/registry-user/update-registry-user-response.json" + }, + "example": { + "message": "jdoe was successfully updated.", + "updated": { + "UUID": "fe566221-6a2c-4279-8800-4d3795325997", + "username": "jdoe", + "name": { + "first": "John", + "last": "Doe" }, - { - "$ref": "../schemas/registry-org/list-registry-orgs-response.json" - } - ] + "status": "active", + "created": "2021-02-12T17:15:37.382Z", + "last_updated": "2021-02-12T17:15:37.382Z" + } } } } @@ -3424,14 +3741,32 @@ } } }, - "post": { + "delete": { "tags": [ - "Organization" + "Registry User" ], - "summary": "Creates an organization as specified in the request body (accessible to Secretariat)", - "description": "User must belong to an organization with the Secretariat role
Secretariat: Creates an organization
", - "operationId": "orgCreateSingle", + "summary": "Deletes the registry user specified by organization and username (accessible to Secretariat only)", + "description": "User must belong to an organization with the Secretariat role
Secretariat: Deletes the specified user from the specified organization
", + "operationId": "registryUserDeleteSingle", "parameters": [ + { + "name": "shortname", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The shortname of the organization" + }, + { + "name": "username", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The username of the user" + }, { "$ref": "#/components/parameters/apiEntityHeader" }, @@ -3444,18 +3779,11 @@ ], "responses": { "200": { - "description": "Returns information about the organization created", + "description": "Confirms deletion of the registry user", "content": { "application/json": { "schema": { - "oneOf": [ - { - "$ref": "../schemas/org/create-org-response.json" - }, - { - "$ref": "../schemas/registry-org/create-registry-org-response.json" - } - ] + "$ref": "../schemas/registry-user/delete-registry-user-response.json" } } } @@ -3510,36 +3838,26 @@ } } } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "../schemas/org/create-org-request.json" - } - } - } } } }, - "/org/{identifier}": { - "get": { + "/registry/org/{shortname}": { + "put": { "tags": [ - "Organization" + "Registry Organization" ], - "summary": "Retrieves information about the organization specified by short name or UUID (accessible to same-organization users or Secretariat)", - "description": "Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
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
", - "operationId": "orgSingle", + "summary": "Updates information about the organization specified by short name (accessible to Secretariat or same-organization Admin)", + "description": "User must belong to an organization with the Secretariat role or be an Admin of the requested organization.
With Joint Approval required for the following fields:
Secretariat: Updates any organization's information
Organization Admin: Requests changes to its organization's information
User must belong to an organization with the Secretariat role
Secretariat: Updates any organization's information
", - "operationId": "orgUpdateSingle", + "summary": "Deletes the registry organization specified by short name (accessible to Secretariat only)", + "description": "User must belong to an organization with the Secretariat role
Secretariat: Deletes the specified registry organization
", + "operationId": "registryOrgDeleteSingle", "parameters": [ { "name": "shortname", @@ -3631,29 +3965,7 @@ "schema": { "type": "string" }, - "description": "The shortname of the organization" - }, - { - "name": "registry", - "in": "query", - "schema": { - "type": "string" - } - }, - { - "$ref": "#/components/parameters/id_quota" - }, - { - "$ref": "#/components/parameters/name" - }, - { - "$ref": "#/components/parameters/newShortname" - }, - { - "$ref": "#/components/parameters/active_roles_add" - }, - { - "$ref": "#/components/parameters/active_roles_remove" + "description": "The shortname of the registry organization" }, { "$ref": "#/components/parameters/apiEntityHeader" @@ -3667,11 +3979,11 @@ ], "responses": { "200": { - "description": "Returns information about the organization updated", + "description": "Confirms deletion of the registry organization", "content": { "application/json": { "schema": { - "$ref": "../schemas/org/update-org-response.json" + "$ref": "../schemas/registry-org/delete-registry-org-response.json" } } } @@ -3729,14 +4041,14 @@ } } }, - "/org/{shortname}/id_quota": { - "get": { + "/registry/org/{shortname}/user": { + "post": { "tags": [ - "Organization" + "Registry User" ], - "summary": "Retrieves an organization's CVE ID quota (accessible to same-organization users or Secretariat)", - "description": "Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
Regular, CNA & Admin Users: Retrieves the CVE ID quota for the user's organization
Secretariat: Retrieves the CVE ID quota for any organization
", - "operationId": "orgIdQuota", + "summary": "Create a user with the provided short name as the owning organization (accessible to Secretariat or target organization Admin)", + "description": "User must belong to an organization with the Secretariat role or be an Admin of the target organization
Admin User: Creates a user for the Admin's organization
Secretariat: Creates a user for any organization
", + "operationId": "registryUserCreateSingle", "parameters": [ { "name": "shortname", @@ -3759,11 +4071,26 @@ ], "responses": { "200": { - "description": "Returns the CVE ID quota for an organization", + "description": "Returns the new user information (with the secret)", "content": { "application/json": { "schema": { - "$ref": "../schemas/org/get-org-quota-response.json" + "$ref": "../schemas/registry-user/create-registry-user-response.json" + } + }, + "example": { + "message": "jdoe was successfully created.", + "created": { + "UUID": "fe566221-6a2c-4279-8800-4d3795325997", + "username": "jdoe", + "name": { + "first": "John", + "last": "Doe" + }, + "status": "active", + "secret": "12345-abcde-67890", + "created": "2021-02-12T17:15:37.382Z", + "last_updated": "2021-02-12T17:15:37.382Z" } } } @@ -3818,17 +4145,35 @@ } } } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "../schemas/registry-user/create-registry-user-request.json" + } + }, + "example": { + "username": "jdoe", + "status": "active", + "name": { + "first": "John", + "last": "Doe" + } + } + } } } }, - "/org/{shortname}/users": { - "get": { + "/registry/org/{shortname}/user/{username}/reset_secret": { + "put": { "tags": [ - "Users" + "Registry User" ], - "summary": "Retrieves all users for the organization with the specified short name (accessible to same-organization users or Secretariat)", - "description": "Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
Regular, CNA & Admin Users: Retrieves information about users in the same organization
Secretariat: Retrieves all user information for any organization
", - "operationId": "userOrgAll", + "summary": "Reset the API key for a user (accessible to self, same-organization Admins, or Secretariat)", + "description": "Authenticated users can reset their own API secret. Organization admins can reset users in their organization. Secretariat users can reset any user's API secret.
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
", + "operationId": "registryUserResetSecret", "parameters": [ { "name": "shortname", @@ -3840,14 +4185,13 @@ "description": "The shortname of the organization" }, { - "name": "registry", - "in": "query", + "name": "username", + "in": "path", + "required": true, "schema": { "type": "string" - } - }, - { - "$ref": "#/components/parameters/pageQuery" + }, + "description": "The username of the user" }, { "$ref": "#/components/parameters/apiEntityHeader" @@ -3861,11 +4205,11 @@ ], "responses": { "200": { - "description": "Returns all users for the organization, along with pagination fields if results span multiple pages of data", + "description": "Returns the new API key", "content": { "application/json": { "schema": { - "$ref": "../schemas/user/list-users-response.json" + "$ref": "../schemas/user/reset-secret-response.json" } } } @@ -3923,14 +4267,14 @@ } } }, - "/org/{shortname}/user": { + "/registry/org/{shortname}/oversees/{shortname2}/add": { "post": { "tags": [ - "Users" + "Registry Organization" ], - "summary": "Create a user with the provided short name as the owning organization (accessible to Secretariat or target organization Admin)", - "description": "User must belong to an organization with the Secretariat role or be an Admin of the target organization
Admin User: Creates a user for the Admin's organization
Secretariat: Creates a user for any organization
", - "operationId": "userCreateSingle", + "summary": "Assigns an organization to report to a ROOT organization (accessible to Secretariat only)", + "description": "User must belong to an organization with the Secretariat role.
The organization identified by shortname must have ROOT authority. The organization identified by shortname2 will report to it. If shortname2 already reported to a different ROOT. It will be removed from that and both will receive an audit entry.
", + "operationId": "registryOrgAddOverseeRelationship", "parameters": [ { "name": "shortname", @@ -3939,7 +4283,16 @@ "schema": { "type": "string" }, - "description": "The shortname of the organization" + "description": "The shortname of the overseeing ROOT organization" + }, + { + "name": "shortname2", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The shortname of the reporting organization" }, { "$ref": "#/components/parameters/apiEntityHeader" @@ -3953,11 +4306,16 @@ ], "responses": { "200": { - "description": "Returns the new user information (with the secret)", + "description": "Reports-to relationship added successfully", "content": { "application/json": { "schema": { - "$ref": "../schemas/user/create-user-response.json" + "type": "object", + "properties": { + "message": { + "type": "string" + } + } } } } @@ -4012,27 +4370,17 @@ } } } - }, - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "../schemas/user/create-user-request.json" - } - } - } } } }, - "/org/{shortname}/user/{username}": { - "get": { + "/registry/org/{shortname}/oversees/{shortname2}/remove": { + "post": { "tags": [ - "Users" + "Registry Organization" ], - "summary": "Retrieves information about a user for the specified username and organization short name (accessible to same-organization users or Secretariat)", - "description": "Authenticated users can access this endpoint only for users in their own organization. Secretariat users can access any user.
Regular, CNA & Admin Users: Retrieves information about a user in the same organization
Secretariat: Retrieves any user's information
", - "operationId": "userSingle", + "summary": "Removes an organization reports-to relationship (accessible to Secretariat only)", + "description": "User must belong to an organization with the Secretariat role.
The organization identified by shortname must have ROOT authority. The organization identified by shortname2 will no longer report to it.
", + "operationId": "registryOrgRemoveOverseeRelationship", "parameters": [ { "name": "shortname", @@ -4041,16 +4389,16 @@ "schema": { "type": "string" }, - "description": "The shortname of the organization" + "description": "The shortname of the overseeing ROOT organization" }, { - "name": "username", + "name": "shortname2", "in": "path", "required": true, "schema": { "type": "string" }, - "description": "The username of the user" + "description": "The shortname of the reporting organization" }, { "$ref": "#/components/parameters/apiEntityHeader" @@ -4064,11 +4412,16 @@ ], "responses": { "200": { - "description": "Returns information about the specified user", + "description": "Reports-to relationship removed successfully", "content": { "application/json": { "schema": { - "$ref": "../schemas/user/get-user-response.json" + "type": "object", + "properties": { + "message": { + "type": "string" + } + } } } } @@ -4124,14 +4477,16 @@ } } } - }, - "put": { + } + }, + "/registry/org/{shortname}/user/{username}/grant-role": { + "post": { "tags": [ - "Users" + "Registry User" ], - "summary": "Updates information about a user for the specified username and organization shortname (accessible to self, same-organization Admins, or Secretariat)", - "description": "Authenticated users can update their own name fields. Organization admins can update users in their organization. Secretariat users can update users in any organization.
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.
", - "operationId": "userUpdateSingle", + "summary": "Grants a role to a user (accessible to Secretariat or Org Admin)", + "description": "User must belong to an organization with the Secretariat role or be an Admin of the target organization
Admin User: Grants a role to a user in the Admin's organization
Secretariat: Grants a role to a user in any organization
", + "operationId": "registryUserGrantRole", "parameters": [ { "name": "shortname", @@ -4151,33 +4506,6 @@ }, "description": "The username of the user" }, - { - "$ref": "#/components/parameters/active" - }, - { - "$ref": "#/components/parameters/activeUserRolesAdd" - }, - { - "$ref": "#/components/parameters/activeUserRolesRemove" - }, - { - "$ref": "#/components/parameters/nameFirst" - }, - { - "$ref": "#/components/parameters/nameLast" - }, - { - "$ref": "#/components/parameters/nameMiddle" - }, - { - "$ref": "#/components/parameters/nameSuffix" - }, - { - "$ref": "#/components/parameters/newUsername" - }, - { - "$ref": "#/components/parameters/orgShortname" - }, { "$ref": "#/components/parameters/apiEntityHeader" }, @@ -4190,11 +4518,16 @@ ], "responses": { "200": { - "description": "Returns the updated user information", + "description": "Role granted successfully", "content": { "application/json": { "schema": { - "$ref": "../schemas/user/update-user-response.json" + "type": "object", + "properties": { + "message": { + "type": "string" + } + } } } } @@ -4249,17 +4582,38 @@ } } } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "role": { + "type": "string", + "enum": [ + "ADMIN" + ] + } + }, + "required": [ + "role" + ] + } + } + } } } }, - "/org/{shortname}/user/{username}/reset_secret": { - "put": { + "/registry/org/{shortname}/user/{username}/revoke-role": { + "post": { "tags": [ - "Users" + "Registry User" ], - "summary": "Reset the API key for a user (accessible to self, same-organization Admins, or Secretariat)", - "description": "Authenticated users can reset their own API secret. Organization admins can reset users in their organization. Secretariat users can reset any user's API secret.
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
", - "operationId": "userResetSecret", + "summary": "Revokes a role from a user (accessible to Secretariat or Org Admin)", + "description": "User must belong to an organization with the Secretariat role or be an Admin of the target organization
Admin User: Revokes a role from a user in the Admin's organization
Secretariat: Revokes a role from a user in any organization
", + "operationId": "registryUserRevokeRole", "parameters": [ { "name": "shortname", @@ -4291,11 +4645,16 @@ ], "responses": { "200": { - "description": "Returns the new API key", + "description": "Role revoked successfully", "content": { "application/json": { "schema": { - "$ref": "../schemas/user/reset-secret-response.json" + "type": "object", + "properties": { + "message": { + "type": "string" + } + } } } } @@ -4350,23 +4709,56 @@ } } } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "role": { + "type": "string", + "enum": [ + "ADMIN" + ] + } + }, + "required": [ + "role" + ] + } + } + } } } }, - "/registry/users": { - "get": { + "/registry/org/{shortname}/conversation/{index}": { + "put": { "tags": [ - "Registry User" + "Registry Organization" ], - "summary": "Retrieves information about all registered users (accessible to Secretariat)", - "description": "User must belong to an organization with the Secretariat role
Secretariat: Retrieves information about all users for all organizations
", - "operationId": "userAll", + "summary": "Update the conversation at the given index for the given organization (accessible to Secretariat or original same-organization author)", + "description": "User must belong to an organization with the Secretariat role or be the original author of the conversation in the same organization
Original Author: Allowed to update only the message body of a conversation posted by them
Secretariat: Allowed to update the message body and/or visibility of any conversation
", + "operationId": "registryOrgUpdateConversation", "parameters": [ { - "$ref": "#/components/parameters/pageQuery" + "name": "shortname", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The shortname of the organization" }, { - "$ref": "#/components/parameters/registry" + "name": "index", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The index of the conversation to update" }, { "$ref": "#/components/parameters/apiEntityHeader" @@ -4380,11 +4772,11 @@ ], "responses": { "200": { - "description": "Returns all users, along with pagination fields if results span multiple pages of data.", + "description": "Returns the updated conversation", "content": { "application/json": { "schema": { - "$ref": "../schemas/registry-user/list-registry-users-response.json" + "$ref": "../schemas/conversation/update-conversation-response.json" } } } @@ -4442,21 +4834,18 @@ } } }, - "/users": { + "/registry/users": { "get": { "tags": [ - "Users" + "Registry User" ], "summary": "Retrieves information about all registered users (accessible to Secretariat)", "description": "User must belong to an organization with the Secretariat role
Secretariat: Retrieves information about all users for all organizations
", - "operationId": "userAll", + "operationId": "registryUserAll", "parameters": [ { "$ref": "#/components/parameters/pageQuery" }, - { - "$ref": "#/components/parameters/registry" - }, { "$ref": "#/components/parameters/apiEntityHeader" }, @@ -4473,14 +4862,7 @@ "content": { "application/json": { "schema": { - "oneOf": [ - { - "$ref": "../schemas/user/list-users-response.json" - }, - { - "$ref": "../schemas/registry-user/list-registry-users-response.json" - } - ] + "$ref": "../schemas/registry-user/list-registry-users-response.json" } } } @@ -4538,21 +4920,6 @@ } } }, - "/health-check": { - "get": { - "tags": [ - "Utilities" - ], - "summary": "Checks that the system is running (accessible to all users)", - "description": "Endpoint is accessible to all
Returns a 200 response code when CVE Services are running
", - "operationId": "healthCheck", - "responses": { - "200": { - "description": "Returns a 200 response code" - } - } - } - }, "/conversation": { "get": { "tags": [ @@ -6104,15 +6471,6 @@ "minimum": 1 } }, - "registry": { - "in": "query", - "name": "registry", - "description": "When set to true, the endpoint will expect request data to conform to the applicable User Registry schema, and will provide response data conforming to the applicable User Registry schema. Defaults to false.", - "required": false, - "schema": { - "type": "boolean" - } - }, "short_name": { "in": "query", "name": "short_name", diff --git a/docker/.docker-env.example b/docker/.docker-env.example index 277b5d01b..988311f94 100644 --- a/docker/.docker-env.example +++ b/docker/.docker-env.example @@ -1,4 +1,5 @@ LOCAL_KEY=TCF25YM-39C4H6D-KA32EGF-V5XSHN3 +MONGO_CONN_STRING=mongodb://docdb:27017,docdb-read-1:27017,docdb-read-2:27017/cve_dev?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false MONGO_HOST=docdb MONGO_PORT=27017 NODE_ENV=development diff --git a/docker/.docker-env.int-example b/docker/.docker-env.int-example index 924f472ab..c3a82240d 100644 --- a/docker/.docker-env.int-example +++ b/docker/.docker-env.int-example @@ -1,3 +1,4 @@ +MONGO_CONN_STRING=mongodb://docdb:27017,docdb-read-1:27017,docdb-read-2:27017/cve_int?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false MONGO_HOST=docdb MONGO_PORT=27017 NODE_ENV=integration diff --git a/docker/README.md b/docker/README.md index 37c4b66ab..37f875d88 100644 --- a/docker/README.md +++ b/docker/README.md @@ -177,6 +177,36 @@ See the [API documentation](https://github.com/CVEProject/cve-services#api-docum The `docker-compose.yml` file exposes the default Mongo port to the host: `localhost:27017`. You can connect using any Mongo viewer such as [Mongo Express](https://github.com/mongo-express/mongo-express) or [Compass](https://www.mongodb.com/try/download/compass) on the host. +### Run the Mongo Replica Cluster Only + +The Mongo-only compose file starts one primary and two replica members without starting the CVE Services app. By default it uses `mongo:5.0`. + +```bash +cd docker/ +docker compose -f docker-compose.mongo-cluster.yml up -d docdb docdb-read-1 docdb-read-2 mongo-init +``` + +Use this connection string from the host, including MongoDB Compass: + +```text +mongodb://localhost:27017,localhost:27018,localhost:27019/cve_test?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false +``` + +To run the same local replica topology with Mongo 8, override the image: + +```bash +cd docker/ +MONGO_IMAGE=mongo:8.0 docker compose -f docker-compose.mongo-cluster.yml up -d --force-recreate docdb docdb-read-1 docdb-read-2 mongo-init +``` + +If you already created the local volumes with Mongo 5, Mongo 8 may fail to start against those files. For a fresh Mongo 8 local cluster, remove the Mongo-only volumes first. This deletes local Mongo data for this compose file: + +```bash +cd docker/ +docker compose -f docker-compose.mongo-cluster.yml down -v +MONGO_IMAGE=mongo:8.0 docker compose -f docker-compose.mongo-cluster.yml up -d docdb docdb-read-1 docdb-read-2 mongo-init +``` + ## Running unit tests You can run unit tests using the docker image by running the following command: diff --git a/docker/docker-compose.mongo-cluster.yml b/docker/docker-compose.mongo-cluster.yml new file mode 100644 index 000000000..60e925b7e --- /dev/null +++ b/docker/docker-compose.mongo-cluster.yml @@ -0,0 +1,132 @@ +services: + docdb: + image: ${MONGO_IMAGE:-mongo:5.0} + container_name: mongo + ports: + - "27017:27017" + - "27018:27018" + - "27019:27019" + volumes: + - docdb-host-data:/data/db + command: ["mongod", "--replSet", "rs0", "--bind_ip_all", "--port", "27017"] + healthcheck: + test: ["CMD-SHELL", "mongosh --quiet --port 27017 --eval 'db.adminCommand({ ping: 1 }).ok' || exit 1"] + interval: 10s + timeout: 10s + retries: 12 + start_period: 30s + + docdb-read-1: + image: ${MONGO_IMAGE:-mongo:5.0} + container_name: mongo-read-1 + network_mode: "service:docdb" + depends_on: + docdb: + condition: service_healthy + volumes: + - docdb-host-read-1-data:/data/db + command: ["mongod", "--replSet", "rs0", "--bind_ip_all", "--port", "27018"] + healthcheck: + test: ["CMD-SHELL", "mongosh --quiet --port 27018 --eval 'db.adminCommand({ ping: 1 }).ok' || exit 1"] + interval: 10s + timeout: 10s + retries: 12 + start_period: 30s + + docdb-read-2: + image: ${MONGO_IMAGE:-mongo:5.0} + container_name: mongo-read-2 + network_mode: "service:docdb" + depends_on: + docdb: + condition: service_healthy + volumes: + - docdb-host-read-2-data:/data/db + command: ["mongod", "--replSet", "rs0", "--bind_ip_all", "--port", "27019"] + healthcheck: + test: ["CMD-SHELL", "mongosh --quiet --port 27019 --eval 'db.adminCommand({ ping: 1 }).ok' || exit 1"] + interval: 10s + timeout: 10s + retries: 12 + start_period: 30s + + mongo-init: + image: ${MONGO_IMAGE:-mongo:5.0} + network_mode: "service:docdb" + depends_on: + docdb: + condition: service_healthy + docdb-read-1: + condition: service_healthy + docdb-read-2: + condition: service_healthy + command: > + sh -c " + mongosh --host localhost --port 27017 --eval ' + const desiredConfig = { + _id: \"rs0\", + members: [ + { _id: 0, host: \"localhost:27017\", priority: 2 }, + { _id: 1, host: \"localhost:27018\", priority: 1 }, + { _id: 2, host: \"localhost:27019\", priority: 1 } + ] + }; + + function memberKey(member) { + return member._id + \":\" + member.host + \":\" + (member.priority ?? 1); + } + + function configMatches(currentConfig) { + const currentMembers = currentConfig.members.map(memberKey).sort().join(\"|\"); + const desiredMembers = desiredConfig.members.map(memberKey).sort().join(\"|\"); + return currentConfig._id === desiredConfig._id && currentMembers === desiredMembers; + } + + try { + rs.status(); + const currentConfig = rs.conf(); + if (configMatches(currentConfig)) { + print(\"Replica set already initialized with the expected localhost members.\"); + } else { + print(\"Updating replica set members...\"); + rs.reconfig({ ...desiredConfig, version: currentConfig.version + 1 }); + } + } catch (e) { + if (e.codeName == \"NotYetInitialized\") { + print(\"Initiating replica set...\"); + rs.initiate(desiredConfig); + } else { + throw e; + } + } + + for (let i = 0; i < 120; i++) { + let status; + try { + status = rs.status(); + } catch (e) { + print(\"Waiting for replica set status: \" + e.message); + sleep(2000); + continue; + } + + const primaryCount = status.members.filter(member => member.stateStr === \"PRIMARY\").length; + const secondaryCount = status.members.filter(member => member.stateStr === \"SECONDARY\").length; + + if (primaryCount === 1 && secondaryCount === 2) { + print(\"Replica set is ready with one primary and two secondaries.\"); + quit(0); + } + + print(\"Waiting for replica set readiness: primary=\" + primaryCount + \", secondary=\" + secondaryCount); + sleep(2000); + } + + throw new Error(\"Replica set did not become ready in time.\"); + ' + " + +volumes: + docdb-host-data: + docdb-host-read-1-data: + docdb-host-read-2-data: diff --git a/package-lock.json b/package-lock.json index 088d21fea..18cd66a37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cve-services", - "version": "2.8.3", + "version": "2.8.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cve-services", - "version": "2.8.3", + "version": "2.8.4", "license": "(CC0)", "dependencies": { "ajv": "^8.6.2", @@ -30,7 +30,7 @@ "mongo-cursor-pagination": "^8.1.3", "mongoose": "^8.9.5", "mongoose-aggregate-paginate-v2": "1.0.6", - "morgan": "^1.9.1", + "morgan": "^1.11.0", "node-dev": "^7.4.3", "packageurl-js": "^2.0.1", "prompt-sync": "^4.2.0", @@ -432,9 +432,9 @@ "license": "Python-2.0" }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -443,9 +443,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, "funding": [ { @@ -523,9 +523,9 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -1477,9 +1477,9 @@ } }, "node_modules/body-parser": { - "version": "1.20.5", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", - "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -1516,9 +1516,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -2926,9 +2926,9 @@ } }, "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -3021,9 +3021,9 @@ } }, "node_modules/eslint-plugin-node/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -3169,9 +3169,9 @@ "license": "Python-2.0" }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -3180,9 +3180,9 @@ } }, "node_modules/eslint/node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, "funding": [ { @@ -3454,9 +3454,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", "funding": [ { "type": "github", @@ -5099,9 +5099,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz", + "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==", "dev": true, "license": "MIT", "dependencies": { @@ -5659,9 +5659,9 @@ "license": "Python-2.0" }, "node_modules/mocha/node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, "funding": [ { @@ -5793,9 +5793,9 @@ } }, "node_modules/mongoose": { - "version": "8.23.0", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.23.0.tgz", - "integrity": "sha512-Bul4Ha6J8IqzFrb0B1xpVzkC3S0sk43dmLSnhFOn8eJlZiLwL5WO6cRymmjaADdCMjUcCpj2ce8hZI6O4ZFSug==", + "version": "8.24.2", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.24.2.tgz", + "integrity": "sha512-5+H3MSHNJCcr9M+lVplekrZ4/Dyn3N1dOpvgn9gMwvHJhunc4G8SQcBVd/btBQoVdspVNPjx8Pw03YWBv6uTJg==", "license": "MIT", "dependencies": { "bson": "^6.10.4", @@ -5824,19 +5824,23 @@ } }, "node_modules/morgan": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", - "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.11.0.tgz", + "integrity": "sha512-zSkVu3t18r39pw4ixfBKvfZi3y2UOqr7d4WYwcj3m8nXpEQK4rPO6GLzs/CExoRgmX3y9EjmmcXqv6jq0SK46g==", "license": "MIT", "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", "depd": "~2.0.0", - "on-finished": "~2.3.0", + "on-finished": "~2.4.1", "on-headers": "~1.1.0" }, "engines": { "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/morgan/node_modules/debug": { @@ -5854,18 +5858,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, - "node_modules/morgan/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/mpath": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", @@ -5914,9 +5906,9 @@ } }, "node_modules/multimatch/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -5938,9 +5930,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "dev": true, "funding": [ { @@ -6196,9 +6188,9 @@ } }, "node_modules/nyc/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -7076,9 +7068,9 @@ } }, "node_modules/postcss": { - "version": "8.5.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", - "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", "dev": true, "funding": [ { @@ -7096,7 +7088,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -7561,9 +7553,9 @@ } }, "node_modules/replace-in-file/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -7764,9 +7756,9 @@ } }, "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -8451,9 +8443,9 @@ } }, "node_modules/standard/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -9151,9 +9143,9 @@ } }, "node_modules/swagger-autogen/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -9250,9 +9242,9 @@ } }, "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -10005,9 +9997,9 @@ } }, "node_modules/yamljs/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -10119,4 +10111,4 @@ } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 318a4bcec..00409c63f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cve-services", "author": "Automation Working Group", - "version": "2.8.3", + "version": "2.8.4", "license": "(CC0)", "devDependencies": { "@faker-js/faker": "^7.6.0", @@ -48,7 +48,7 @@ "mongo-cursor-pagination": "^8.1.3", "mongoose": "^8.9.5", "mongoose-aggregate-paginate-v2": "1.0.6", - "morgan": "^1.9.1", + "morgan": "^1.11.0", "node-dev": "^7.4.3", "packageurl-js": "^2.0.1", "prompt-sync": "^4.2.0", @@ -104,6 +104,7 @@ "swagger-autogen": "node src/swagger.js", "test": "NODE_ENV=test mocha --recursive --exit || true", "test:integration": "NODE_ENV=test node-dev src/scripts/populate.js y; NODE_ENV=test MONGO_CONN_STRING=mongodb://docdb:27017 MONGO_DB_NAME=cve_test node-dev src/scripts/migrate.js; NODE_ENV=test mocha test/integration-tests --recursive --exit", + "test:integration:replicas": "NODE_ENV=test MONGO_CONN_STRING='mongodb://localhost:27017,localhost:27018,localhost:27019/cve_test?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false' node-dev src/scripts/populate.js y; NODE_ENV=test MONGO_CONN_STRING='mongodb://localhost:27017,localhost:27018,localhost:27019/cve_test?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false' MONGO_DB_NAME=cve_test node-dev src/scripts/migrate.js; NODE_ENV=test MONGO_CONN_STRING='mongodb://localhost:27017,localhost:27018,localhost:27019/cve_test?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false' node src/scripts/runMocha.js test/integration-tests --recursive --exit", "test:unit-tests": "NODE_ENV=test mocha test/unit-tests --recursive --exit || true", "test:coverage": "NODE_ENV=test nyc --reporter=text mocha src/* --recursive --exit || true", "test:coverage-html": "NODE_ENV=test nyc --reporter=html mocha src/* --recursive --exit || true", diff --git a/schemas/registry-org/delete-registry-org-response.json b/schemas/registry-org/delete-registry-org-response.json new file mode 100644 index 000000000..f35e807f1 --- /dev/null +++ b/schemas/registry-org/delete-registry-org-response.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://cve.mitre.org/schema/registry-org/delete-registry-org-response.json", + "type": "object", + "title": "CVE Delete Registry Org Response", + "description": "JSON Schema for a successful registry organization deletion response", + "properties": { + "message": { + "type": "string", + "description": "Confirmation that the registry organization was deleted" + } + }, + "required": [ + "message" + ], + "additionalProperties": false +} diff --git a/schemas/registry-user/delete-registry-user-response.json b/schemas/registry-user/delete-registry-user-response.json new file mode 100644 index 000000000..caf60671d --- /dev/null +++ b/schemas/registry-user/delete-registry-user-response.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://cve.mitre.org/schema/registry-user/delete-registry-user-response.json", + "type": "object", + "title": "CVE Delete Registry User Response", + "description": "JSON Schema for a successful registry user deletion response", + "properties": { + "message": { + "type": "string", + "description": "Confirmation that the registry user was deleted" + } + }, + "required": [ + "message" + ], + "additionalProperties": false +} diff --git a/src/controller/cve-id.controller/cve-id.controller.js b/src/controller/cve-id.controller/cve-id.controller.js index ba93ea02f..e33b86411 100644 --- a/src/controller/cve-id.controller/cve-id.controller.js +++ b/src/controller/cve-id.controller/cve-id.controller.js @@ -39,8 +39,9 @@ async function getFilteredCveId (req, res, next) { const requesterOrgUUID = await authContext.getRequesterOrgUUID(req, orgRepo) // Create map of orgUUID to shortnames and users to simplify aggregation later - const orgs = await orgRepo.getAllOrgs() - const users = await userRepo.getAllUsers() + // Only project the fields needed for the maps to avoid fetching full documents + const orgs = await orgRepo.getAllOrgs({}, { UUID: 1, short_name: 1, _id: 0 }) + const users = await userRepo.getAllUsers({}, { UUID: 1, username: 1, org_UUID: 1, _id: 0 }) const orgMap = {} const userMap = {} diff --git a/src/controller/format.constants.js b/src/controller/format.constants.js new file mode 100644 index 000000000..37a215e0a --- /dev/null +++ b/src/controller/format.constants.js @@ -0,0 +1,7 @@ +const LEGACY_FORMAT = true +const REGISTRY_FORMAT = false + +module.exports = { + LEGACY_FORMAT, + REGISTRY_FORMAT +} diff --git a/src/controller/org.controller/index.js b/src/controller/org.controller/index.js index 8e362cf2f..52e4c4c77 100644 --- a/src/controller/org.controller/index.js +++ b/src/controller/org.controller/index.js @@ -3,1201 +3,13 @@ const router = express.Router() const mw = require('../../middleware/middleware') const errorMsgs = require('../../middleware/errorMessages') const controller = require('./org.controller') -const registryOrgController = require('../registry-org.controller/registry-org.controller.js') -const registryUserController = require('../registry-user.controller/registry-user.controller.js') const { body, param, query } = require('express-validator') const { parseGetParams, parsePostParams, parsePutParams, parseError, isUserRole, isValidUsername, isOrgRole, validateUpdateOrgParameters, shortCircuitLegacyCpsMitreOrgParameters } = require('./org.middleware') // Only God and Javascript know swhy its saying it is not used when it is..... // eslint-disable-next-line no-unused-vars -const { toUpperCaseArray, isFlatStringArray, handleRegistryParameter } = require('../../middleware/middleware') +const { toUpperCaseArray, isFlatStringArray } = require('../../middleware/middleware') const getConstants = require('../../../src/constants').getConstants const CONSTANTS = getConstants() - -router.get('/registry/org', - /* - #swagger.tags = ['Registry Organization'] - #swagger.operationId = 'registryOrgAll' - #swagger.summary = "Retrieves all registry organizations (accessible to Secretariat)" - #swagger.description = " -User must belong to an organization with the Secretariat role
-Secretariat: Retrieves information about all registry organizations
" - #swagger.parameters['$ref'] = [ - '#/components/parameters/pageQuery', - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.responses[200] = { - description: 'Returns information about all registry organizations, along with pagination fields if results span multiple pages of data', - content: { - "application/json": { - schema: { - $ref: '../schemas/registry-org/list-registry-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.useRegistry(), - mw.validateUser, - mw.onlySecretariat, - query().custom((query) => { return mw.validateQueryParameterNames(query, ['page']) }), - query(['page']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), - query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }), - parseError, - parseGetParams, - registryOrgController.ALL_ORGS -) - -router.get('/registry/org/:shortname/users', - /* - #swagger.tags = ['Registry User'] - #swagger.operationId = 'userOrgAll' - #swagger.summary = "Retrieves all users for the organization with the specified short name (accessible to same-organization users or Secretariat)" - #swagger.description = " -Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
-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/registry-user/list-registry-users-response.json' - }, - example: { - "totalCount": 1, - "itemsPerPage": 100, - "pageCount": 1, - "currentPage": 1, - "prevPage": null, - "nextPage": null, - "users": [ - { - "UUID": "fe566221-6a2c-4279-8800-4d3795325997", - "username": "jdoe", - "name": { - "first": "John", - "last": "Doe" - }, - "role": "ADMIN", - "status": "active", - "created": "2021-02-12T17:15:37.382Z", - "last_updated": "2021-02-12T17:15:37.382Z" - } - ] - } - } - } - } - #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.useRegistry(), - mw.validateUser, - param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - query().custom((query) => { return mw.validateQueryParameterNames(query, ['page']) }), - query(['page']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), - query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }), - parseError, - parseGetParams, - registryOrgController.USER_ALL) - -router.get('/registry/org/:shortname/id_quota', - /* - #swagger.tags = ['Registry Organization'] - #swagger.operationId = 'orgIdQuota' - #swagger.summary = "Retrieves an organization's CVE ID quota (accessible to same-organization users or Secretariat)" - #swagger.description = " -Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
-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/registry-org/get-registry-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.useRegistry(), - mw.validateUser, - param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - query().custom((query) => { return mw.validateQueryParameterNames(query, ['']) }), - parseError, - parseGetParams, - controller.ORG_ID_QUOTA) - -router.get('/registry/org/:identifier', - /* - #swagger.tags = ['Registry Organization'] - #swagger.operationId = 'registryOrgSingle' - #swagger.summary = "Retrieves information about the registry organization specified by short name or UUID (accessible to same-organization users or Secretariat)" - #swagger.description = " -Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
-Regular, CNA & Admin Users: Retrieves registry organization record for the specified shortname or UUID if it is the user's organization
-Secretariat: Retrieves information about any registry organization
" - #swagger.parameters['identifier'] = { description: 'The shortname or UUID of the registry organization' } - #swagger.parameters['expand'] = { - in: 'query', - description: 'Optional expanded related data. Accepted value: users.', - required: false, - schema: { - type: 'string', - enum: ['users'] - } - } - #swagger.parameters['$ref'] = [ - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.responses[200] = { - description: 'Returns the registry organization information', - content: { - "application/json": { - schema: { - $ref: '../schemas/registry-org/get-registry-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.useRegistry(), - mw.validateUser, - query().custom((query) => { return mw.validateQueryParameterNames(query, ['expand']) }), - query(['expand']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), - query(['expand']).optional().isIn(['users']), - parseError, - parseGetParams, - registryOrgController.SINGLE_ORG -) - -router.get('/registry/org/:shortname/user/:username', - /* - #swagger.tags = ['Registry User'] - #swagger.operationId = 'registryUserSingle' - #swagger.summary = "Retrieves information about a user for the specified username and organization short name (accessible to same-organization users or Secretariat)" - #swagger.description = " -Authenticated users can access this endpoint only for users in their own organization. Secretariat users can access any user.
-Regular, CNA & Admin Users: Retrieves information about a registry user in the same organization
-Secretariat: Retrieves any registry user's information
" - #swagger.parameters['$ref'] = [ - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.parameters['shortname'] = { - description: 'The shortname of the organization' - } - #swagger.parameters['username'] = { - description: 'The username of the registry user', - schema: { - type: 'string', - pattern: '^[a-zA-Z0-9._@-]+$' - } - } - #swagger.responses[200] = { - description: 'Returns information about the specified registry user', - content: { - "application/json": { - schema: { $ref: '../schemas/registry-user/get-registry-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.useRegistry(), - mw.validateUser, - param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - param(['username']).isString().trim().notEmpty().custom(isValidUsername), - query().custom((query) => { return mw.validateQueryParameterNames(query, ['']) }), - parseError, - parseGetParams, - registryUserController.SINGLE_USER -) - -router.post('/registry/org', - /* - #swagger.tags = ['Registry Organization'] - #swagger.operationId = 'orgCreateSingle' - #swagger.summary = "Creates an organization (accessible to Secretariat)" - #swagger.description = " -User must belong to an organization with the Secretariat role
-Secretariat: Creates a new organization
" - #swagger.parameters['$ref'] = [ - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.requestBody = { - required: true, - content: { - 'application/json': { - schema: { - anyOf: [ - { $ref: '../schemas/registry-org/SecretariatOrg.json' }, - { $ref: '../schemas/registry-org/CNAOrg.json' }, - { $ref: '../schemas/registry-org/ADPOrg.json' }, - { $ref: '../schemas/registry-org/BulkDownloadOrg.json' } - ] - }, - example: { - short_name: 'fake_company', - long_name: 'Fake Company', - id_quota: 1000, - authority: ['CNA'] - } - } - } - } - #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/registry-org/list-registry-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.useRegistry(), - mw.validateUser, - mw.onlySecretariat, - query().custom((query) => { return mw.validateQueryParameterNames(query, ['']) }), - parsePostParams, - parseError, - registryOrgController.CREATE_ORG -) - -router.put('/registry/org/:shortname', - /* - #swagger.tags = ['Registry Organization'] - #swagger.operationId = 'orgUpdateSingle' - #swagger.summary = "Updates information about the organization specified by short name (accessible to Secretariat or same-organization Admin)" - #swagger.description = " -User must belong to an organization with the Secretariat role or be an Admin of the requested organization.
-With Joint Approval required for the following fields:
-Secretariat: Updates any organization's information
-Organization Admin: Requests changes to its organization's information
-User must belong to an organization with the Secretariat role or be an Admin of the target organization
-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/registry-user/create-registry-user-request.json' } - }, - example: { - "username": "jdoe", - "status": "active", - "name": { - "first": "John", - "last": "Doe" - } - } - } - } - #swagger.responses[200] = { - description: 'Returns the new user information (with the secret)', - content: { - "application/json": { - schema: - { $ref: '../schemas/registry-user/create-registry-user-response.json' } - }, - example: { - "message": "jdoe was successfully created.", - "created": { - "UUID": "fe566221-6a2c-4279-8800-4d3795325997", - "username": "jdoe", - "name": { - "first": "John", - "last": "Doe" - }, - "status": "active", - "secret": "12345-abcde-67890", - "created": "2021-02-12T17:15:37.382Z", - "last_updated": "2021-02-12T17:15:37.382Z" - } - } - } - } - #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.useRegistry(), - mw.validateUser, - mw.onlySecretariatOrAdmin, - mw.onlyOrgWithPartnerRole, - param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - body(['org_uuid']).optional().isString().trim(), - body(['uuid']).optional().isString().trim(), - body(['name.first']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_FIRSTNAME_LENGTH }).withMessage(errorMsgs.FIRSTNAME_LENGTH), - body(['name.last']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_LASTNAME_LENGTH }).withMessage(errorMsgs.LASTNAME_LENGTH), - body(['name.middle']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_MIDDLENAME_LENGTH }).withMessage(errorMsgs.MIDDLENAME_LENGTH), - body(['name.suffix']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_SUFFIX_LENGTH }).withMessage(errorMsgs.SUFFIX_LENGTH), - body(['authority.active_roles']).optional() - .custom(mw.isFlatStringArray) - .bail() - .customSanitizer(toUpperCaseArray) - .custom(isUserRole), - parseError, - parsePostParams, - registryOrgController.USER_CREATE_SINGLE -) - -router.put('/registry/org/:shortname/user/:username', - /* - #swagger.tags = ['Registry User'] - #swagger.operationId = 'registryUserUpdateSingle' - #swagger.summary = "Updates information about a user for the specified username and organization shortname (accessible to self, same-organization Admins, or Secretariat)" - #swagger.description = " -Authenticated users can update their own name fields. Organization admins can update users in their organization. Secretariat users can update users in any organization.
-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/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/registry-user/update-registry-user-response.json' }, - example: { - "message": "jdoe was successfully updated.", - "updated": { - "UUID": "fe566221-6a2c-4279-8800-4d3795325997", - "username": "jdoe", - "name": { - "first": "John", - "last": "Doe" - }, - "status": "active", - "created": "2021-02-12T17:15:37.382Z", - "last_updated": "2021-02-12T17:15:37.382Z" - } - } - } - } - } - #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.useRegistry(), - mw.validateUser, - mw.onlyOrgWithPartnerRole, - parseError, - parsePutParams, - registryUserController.UPDATE_USER) - -router.put('/registry/org/:shortname/user/:username/reset_secret', - /* - #swagger.tags = ['Registry User'] - #swagger.operationId = 'userResetSecret' - #swagger.summary = "Reset the API key for a user (accessible to self, same-organization Admins, or Secretariat)" - #swagger.description = " -Authenticated users can reset their own API secret. Organization admins can reset users in their organization. Secretariat users can reset any user's API secret.
-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.useRegistry(), - mw.validateUser, - mw.onlyOrgWithPartnerRole, - parseError, - parsePostParams, - controller.USER_RESET_SECRET -) - -router.post('/registry/org/:shortname/user/:username/grant-role', - /* - #swagger.tags = ['Registry User'] - #swagger.operationId = 'registryUserGrantRole' - #swagger.summary = "Grants a role to a user (accessible to Secretariat or Org Admin)" - #swagger.description = " -User must belong to an organization with the Secretariat role or be an Admin of the target organization
-Admin User: Grants a role to a user in the Admin's organization
-Secretariat: Grants a role to a user in any organization
" - #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.requestBody = { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - role: { - type: 'string', - enum: ['ADMIN'] - } - }, - required: ['role'] - } - } - } - } - #swagger.responses[200] = { - description: 'Role granted successfully', - content: { - "application/json": { - schema: { type: 'object', properties: { message: { type: 'string' } } } - } - } - } - #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.useRegistry(), - mw.validateUser, - // mw.onlyOrgWithPartnerRole, // This might be too restrictive if we want Secretariat to do it for any org type - parseError, - parsePostParams, - registryUserController.GRANT_ROLE -) - -router.post('/registry/org/:shortname/user/:username/revoke-role', - /* - #swagger.tags = ['Registry User'] - #swagger.operationId = 'registryUserRevokeRole' - #swagger.summary = "Revokes a role from a user (accessible to Secretariat or Org Admin)" - #swagger.description = " -User must belong to an organization with the Secretariat role or be an Admin of the target organization
-Admin User: Revokes a role from a user in the Admin's organization
-Secretariat: Revokes a role from a user in any organization
" - #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.requestBody = { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - role: { - type: 'string', - enum: ['ADMIN'] - } - }, - required: ['role'] - } - } - } - } - #swagger.responses[200] = { - description: 'Role revoked successfully', - content: { - "application/json": { - schema: { type: 'object', properties: { message: { type: 'string' } } } - } - } - } - #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.useRegistry(), - mw.validateUser, - // mw.onlyOrgWithPartnerRole, - parseError, - parsePostParams, - registryUserController.REVOKE_ROLE -) - -router.put('/registry/org/:shortname/conversation/:index', - /* - #swagger.tags = ['Registry Organization'] - #swagger.operationId = 'registryUserUpdateConversation' - #swagger.summary = "Update the conversation at the given index for the given organization (accessible to Secretariat or original same-organization author)" - #swagger.description = " -User must belong to an organization with the Secretariat role or be the original author of the conversation in the same organization
-Original Author: Allowed to update only the message body of a conversation posted by them
-Secretariat: Allowed to update the message body and/or visibility of any conversation
" - #swagger.parameters['shortname'] = { description: 'The shortname of the organization' } - #swagger.parameters['index'] = { description: 'The index of the conversation to update' } - #swagger.parameters['$ref'] = [ - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.responses[200] = { - description: 'Returns the updated conversation', - content: { - "application/json": { - schema: { $ref: '../schemas/conversation/update-conversation-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.useRegistry(), - mw.validateUser, - mw.onlyOrgWithPartnerRole, - parseError, - parsePostParams, - registryOrgController.EDIT_CONVERSATION -) - router.get('/org', /* #swagger.tags = ['Organization'] @@ -1268,7 +80,6 @@ router.get('/org', } } */ - mw.handleRegistryParameter, mw.validateUser, mw.onlySecretariat, query().custom((query) => { return mw.validateQueryParameterNames(query, ['page']) }), @@ -1670,7 +481,6 @@ router.get('/org/:shortname/users', } } */ - mw.handleRegistryParameter, mw.validateUser, param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), query().custom((query) => { return mw.validateQueryParameterNames(query, ['page']) }), diff --git a/src/controller/org.controller/org.controller.js b/src/controller/org.controller/org.controller.js index cdadb284e..19fcd0bac 100644 --- a/src/controller/org.controller/org.controller.js +++ b/src/controller/org.controller/org.controller.js @@ -7,6 +7,11 @@ const error = new errors.OrgControllerError() const validateUUID = require('uuid').validate const _ = require('lodash') const authContext = require('../../utils/authContext') +const { LEGACY_FORMAT, REGISTRY_FORMAT } = require('../format.constants') + +function getObjectFormatForRequest (req) { + return req.useRegistry ? REGISTRY_FORMAT : LEGACY_FORMAT +} /** * Get the details of all orgs. @@ -32,7 +37,7 @@ async function getOrgs (req, res, next) { 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 returnValue = await repo.getAllOrgs({ ...options }, true) + const returnValue = await repo.getAllOrgs({ ...options }, LEGACY_FORMAT) logger.info({ uuid: req.ctx.uuid, message: 'The orgs were sent to the user.' }) return res.status(200).json(returnValue) @@ -58,21 +63,20 @@ async function getOrg (req, res, next) { const requesterOrgShortName = req.ctx.org const identifier = req.ctx.params.identifier const identifierIsUUID = validateUUID(identifier) - const returnLegacyFormat = true let returnValue try { - const requesterOrg = await authContext.getRequesterOrg(req, repo, {}, returnLegacyFormat) + const requesterOrg = await authContext.getRequesterOrg(req, repo, {}, LEGACY_FORMAT) // Ensure requester org exists if (!requesterOrg) { return res.status(404).json(error.orgDne(requesterOrgShortName, 'requesterOrgShortName', 'header')) } - const isSecretariat = await authContext.isRequesterSecretariat(req, repo, {}, returnLegacyFormat) + const isSecretariat = await authContext.isRequesterSecretariat(req, repo, {}, LEGACY_FORMAT) const isRequesterSameOrg = identifierIsUUID ? requesterOrg.UUID === identifier - : await authContext.isRequesterSameOrg(req, repo, identifier, {}, returnLegacyFormat) + : await authContext.isRequesterSameOrg(req, repo, identifier, {}, LEGACY_FORMAT) // Ensure that if the requester is not Secretariat, they can't view orgs other than their own if (!isRequesterSameOrg && !isSecretariat) { @@ -80,7 +84,7 @@ async function getOrg (req, res, next) { return res.status(403).json(error.notSameOrgOrSecretariat()) } - returnValue = await repo.getOrg(identifier, identifierIsUUID, {}, returnLegacyFormat) + returnValue = await repo.getOrg(identifier, identifierIsUUID, {}, LEGACY_FORMAT) } catch (err) { // Handle the specific error thrown by BaseOrgRepository.getOrg if (err.message && err.message.includes('Unknown Org type requested')) { @@ -139,13 +143,13 @@ async function getUsers (req, res, next) { return res.status(404).json(error.orgDnePathParam(orgShortName)) } - const isSameOrg = await authContext.isRequesterSameOrg(req, orgRepo, { UUID: orgUUID, short_name: orgShortName }, {}, !req.useRegistry) + const isSameOrg = await authContext.isRequesterSameOrg(req, orgRepo, { UUID: orgUUID, short_name: orgShortName }, {}, LEGACY_FORMAT) if (!isSameOrg && !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 payload = await userRepo.getAllUsersByOrgShortname(orgShortName, options, !!req.useRegistry) + const payload = await userRepo.getAllUsersByOrgShortname(orgShortName, options, LEGACY_FORMAT) logger.info({ uuid: req.ctx.uuid, message: `The users of ${orgShortName} organization were sent to the user.` }) return res.status(200).json(payload) @@ -169,10 +173,10 @@ async function getUser (req, res, next) { const orgShortName = req.ctx.params.shortname const orgRepo = req.ctx.repositories.getBaseOrgRepository() - const isSecretariat = await authContext.isRequesterSecretariat(req, orgRepo, {}, !req.useRegistry) + const isSecretariat = await authContext.isRequesterSecretariat(req, orgRepo, {}, LEGACY_FORMAT) const orgUUID = await orgRepo.getOrgUUID(orgShortName) - const isSameOrg = await authContext.isRequesterSameOrg(req, orgRepo, { UUID: orgUUID, short_name: orgShortName }, {}, !req.useRegistry) + const isSameOrg = await authContext.isRequesterSameOrg(req, orgRepo, { UUID: orgUUID, short_name: orgShortName }, {}, LEGACY_FORMAT) if (!isSameOrg && !isSecretariat) { logger.info({ uuid: req.ctx.uuid, message: req.ctx.org + ' organization can only be viewed by that organization\'s users or the Secretariat.' }) return res.status(403).json(error.notSameOrgOrSecretariat()) @@ -185,7 +189,7 @@ async function getUser (req, res, next) { const userRepo = req.ctx.repositories.getBaseUserRepository() // This is simple, we can just call our function - const result = await userRepo.findOneByUsernameAndOrgShortname(username, orgShortName, {}, !!req.useRegistry) + const result = await userRepo.findOneByUsernameAndOrgShortname(username, orgShortName, {}, LEGACY_FORMAT) if (!result) { logger.info({ uuid: req.ctx.uuid, message: username + ' does not exist.' }) @@ -218,22 +222,22 @@ async function getOrgIdQuota (req, res, next) { try { const orgRepo = req.ctx.repositories.getBaseOrgRepository() const shortName = req.ctx.params.shortname - - const isSecretariat = await authContext.isRequesterSecretariat(req, orgRepo, {}, !req.useRegistry) - const isSameOrg = await authContext.isRequesterSameOrg(req, orgRepo, shortName, {}, !req.useRegistry) + const objectFormat = getObjectFormatForRequest(req) + const isSecretariat = await authContext.isRequesterSecretariat(req, orgRepo, {}, objectFormat) + const isSameOrg = await authContext.isRequesterSameOrg(req, orgRepo, shortName, {}, objectFormat) if (!isSameOrg && !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()) } - const org = await orgRepo.getOrg(shortName, false, {}, !req.useRegistry) + const org = await orgRepo.getOrg(shortName, false, {}, objectFormat) if (!org) { // a null org 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 = await orgRepo.getOrgIdQuota(org, !req.useRegistry) + const returnPayload = await orgRepo.getOrgIdQuota(org, objectFormat) 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) { @@ -264,21 +268,8 @@ async function createOrg (req, res, next) { try { session.startTransaction({ readPreference: 'primary' }) - if (req.useRegistry) { - // If we are creating an org via the registry flag, we can do a full validation. - const result = await repo.validateOrg(body, { session }) - if (!result.isValid) { - logger.error(JSON.stringify({ uuid: req.ctx.uuid, message: 'CVE JSON schema validation FAILED.' })) - await session.abortTransaction() - if (!Array.isArray(body?.authority) || body?.authority.some(item => typeof item !== 'string')) { - return res.status(400).json({ error: 'BAD_INPUT', message: 'Parameters were invalid', details: [{ param: 'authority', msg: 'Parameter must be a one-dimensional array of strings' }] }) - } - return res.status(400).json({ error: 'BAD_INPUT', message: 'Parameters were invalid', errors: result.errors }) - } - } - // Check to see if the org already exits - if (await repo.orgExists(body?.short_name, { session }, !req.useRegistry)) { + if (await repo.orgExists(body?.short_name, { session }, LEGACY_FORMAT)) { logger.info({ uuid: req.ctx.uuid, message: body?.short_name + ' organization was not created because it already exists.' }) await session.abortTransaction() return res.status(400).json(error.orgExists(body?.short_name)) @@ -295,9 +286,9 @@ async function createOrg (req, res, next) { return res.status(400).json(error.aliasCollision(collisionString)) } const userRepo = req.ctx.repositories.getBaseUserRepository() - const isSecretariat = await authContext.isRequesterSecretariat(req, repo, { session }, !req.useRegistry) - const requestingUserUUID = await authContext.getRequesterUserUUID(req, userRepo, repo, { session }, !!req.useRegistry) - returnValue = await repo.createOrg(req.ctx.body, { session, upsert: true }, !req.useRegistry, requestingUserUUID, isSecretariat) + const isSecretariat = await authContext.isRequesterSecretariat(req, repo, { session }, LEGACY_FORMAT) + const requestingUserUUID = await authContext.getRequesterUserUUID(req, userRepo, repo, { session }, LEGACY_FORMAT) + returnValue = await repo.createOrg(req.ctx.body, { session, upsert: true }, LEGACY_FORMAT, requestingUserUUID, isSecretariat) await session.commitTransaction() } catch (error) { @@ -351,23 +342,6 @@ async function updateOrg (req, res, next) { try { session.startTransaction({ readPreference: 'primary' }) - // TODO: Check to see if this check is needed for both options - if (req.useRegistry) { - if (queryParametersJson['active_roles.add']) { - if (!Array.isArray(queryParametersJson.active_roles?.add) || queryParametersJson.active_roles?.add.some(item => typeof item !== 'string')) { - await session.abortTransaction() - return res.status(400).json({ message: 'Parameters were invalid', details: [{ param: 'authority', msg: 'Parameter must be a one-dimensional array of strings' }] }) - } - } - - if (queryParametersJson['active_roles.remove']) { - if (!Array.isArray(queryParametersJson.active_roles?.remove) || queryParametersJson.active_roles?.remove.some(item => typeof item !== 'string')) { - await session.abortTransaction() - return res.status(400).json({ message: 'Parameters were invalid', details: [{ param: 'authority', msg: 'Parameter must be a one-dimensional array of strings' }] }) - } - } - } - if (!(await orgRepository.orgExists(shortNameUrlParameter, { session }))) { logger.info({ uuid: req.ctx.uuid, message: `Organization ${shortNameUrlParameter} not found.` }) await session.abortTransaction() @@ -392,9 +366,9 @@ async function updateOrg (req, res, next) { } const userRepo = req.ctx.repositories.getBaseUserRepository() - const requestingUserUUID = await authContext.getRequesterUserUUID(req, userRepo, orgRepository, { session }, !!req.useRegistry) - const isSecretariat = await authContext.isRequesterSecretariat(req, orgRepository, { session }, !req.useRegistry) - const isAdmin = await authContext.isRequesterAdmin(req, userRepo, orgRepository, { session }, !!req.useRegistry) + const requestingUserUUID = await authContext.getRequesterUserUUID(req, userRepo, orgRepository, { session }, LEGACY_FORMAT) + const isSecretariat = await authContext.isRequesterSecretariat(req, orgRepository, { session }, LEGACY_FORMAT) + const isAdmin = await authContext.isRequesterAdmin(req, userRepo, orgRepository, { session }, LEGACY_FORMAT) if (!isSecretariat) { const secretariatOnlyFields = getConstants().SECRETARIAT_ONLY_FIELDS @@ -405,7 +379,7 @@ async function updateOrg (req, res, next) { return res.status(403).json(error.secretariatOnlyEditing(restrictedFieldsSent)) } } - const updatedOrg = await orgRepository.updateOrg(shortNameUrlParameter, queryParametersJson, { session }, !req.useRegistry, requestingUserUUID, isAdmin, isSecretariat) + const updatedOrg = await orgRepository.updateOrg(shortNameUrlParameter, queryParametersJson, { session }, LEGACY_FORMAT, requestingUserUUID, isAdmin, isSecretariat) responseMessage = { message: `${updatedOrg.short_name} organization was successfully updated.`, updated: updatedOrg } // Clarify message payload = { @@ -433,7 +407,7 @@ async function updateOrg (req, res, next) { /** * Creates a user only if the org exists and the user does not exist for the specified shortname and username. - * Called by POST /api/registry/org/{shortname}/user, POST /api/org/{shortname}/user + * Called by POST /api/org/{shortname}/user * * @param {Object} req - The request object * @param {Object} res - The response object @@ -446,7 +420,6 @@ async function createUser (req, res, next) { const userRepo = req.ctx.repositories.getBaseUserRepository() const orgRepo = req.ctx.repositories.getBaseOrgRepository() const orgShortName = req.ctx.params.shortname - const constants = getConstants() let returnValue // Check to make sure Org Exists first @@ -469,30 +442,13 @@ async function createUser (req, res, next) { try { session.startTransaction({ readPreference: 'primary' }) - if (req.useRegistry) { - const result = await userRepo.validateUser(body) - if (body?.role && typeof body?.role !== 'string') { - await session.abortTransaction() - return res.status(400).json({ message: 'Parameters were invalid', details: [{ param: 'role', msg: 'Parameter must be a string' }] }) - } - if (body?.role && !constants.USER_ROLES.includes(body?.role)) { - await session.abortTransaction() - return res.status(400).json({ message: 'Parameters were invalid', details: [{ param: 'role', msg: `Role must be one of the following: ${constants.USER_ROLES}` }] }) - } - if (!result.isValid) { - logger.error(JSON.stringify({ uuid: req.ctx.uuid, message: 'User JSON schema validation FAILED.' })) - await session.abortTransaction() - return res.status(400).json({ message: 'Parameters were invalid', errors: result.errors }) - } - } else { - if (!body?.username || typeof body?.username !== 'string') { - await session.abortTransaction() - return res.status(400).json({ message: 'Parameters were invalid', details: [{ param: 'username', msg: 'Parameter must be a non empty string' }] }) - } + if (!body?.username || typeof body?.username !== 'string') { + await session.abortTransaction() + return res.status(400).json({ message: 'Parameters were invalid', details: [{ param: 'username', msg: 'Parameter must be a non empty string' }] }) } // Ask repo if user already exists - if (await userRepo.orgHasUser(orgShortName, body?.username, { session }, !!req.useRegistry)) { + if (await userRepo.orgHasUser(orgShortName, body?.username, { session }, LEGACY_FORMAT)) { logger.info({ uuid: req.ctx.uuid, message: `${body?.username} user was not created because it already exists.` }) await session.abortTransaction() return res.status(400).json(error.userExists(body?.username)) @@ -500,10 +456,10 @@ async function createUser (req, res, next) { let isRequesterAdminOrSecretariat if (!req.ctx.authenticated && !req.ctx.orgUUID && typeof userRepo.isAdminOrSecretariat === 'function') { - isRequesterAdminOrSecretariat = await userRepo.isAdminOrSecretariat(orgShortName, req.ctx.user, req.ctx.org, { session }, !!req.useRegistry) + isRequesterAdminOrSecretariat = await userRepo.isAdminOrSecretariat(orgShortName, req.ctx.user, req.ctx.org, { session }, LEGACY_FORMAT) } else { - const isRequesterSecretariat = await authContext.isRequesterSecretariat(req, orgRepo, { session }, !req.useRegistry) - const isRequesterAdminOfTargetOrg = await authContext.isRequesterAdminOfOrg(req, userRepo, orgRepo, orgShortName, { session }, !!req.useRegistry) + const isRequesterSecretariat = await authContext.isRequesterSecretariat(req, orgRepo, { session }, LEGACY_FORMAT) + const isRequesterAdminOfTargetOrg = await authContext.isRequesterAdminOfOrg(req, userRepo, orgRepo, orgShortName, { session }, LEGACY_FORMAT) isRequesterAdminOrSecretariat = isRequesterSecretariat || isRequesterAdminOfTargetOrg } @@ -518,8 +474,8 @@ async function createUser (req, res, next) { return res.status(400).json(error.userLimitReached()) } - const requestingUserUUID = await authContext.getRequesterUserUUID(req, userRepo, orgRepo, { session }, !!req.useRegistry) - returnValue = await userRepo.createUser(orgShortName, body, { session, upsert: true }, !!req.useRegistry, requestingUserUUID) + const requestingUserUUID = await authContext.getRequesterUserUUID(req, userRepo, orgRepo, { session }, LEGACY_FORMAT) + returnValue = await userRepo.createUser(orgShortName, body, { session, upsert: true }, LEGACY_FORMAT, requestingUserUUID) await session.commitTransaction() } catch (error) { await session.abortTransaction() @@ -552,7 +508,7 @@ async function createUser (req, res, next) { /** * 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}, PUT /org/{shortname}/user/{username} + * Called by PUT /api/org/{shortname}/user/{username} * * @param {Object} req - The request object * @param {Object} res - The response object @@ -575,26 +531,20 @@ async function updateUser (req, res, next) { const queryParametersJson = req.ctx.query // Get requester UUID for later - const requesterUUID = await authContext.getRequesterUserUUID(req, userRepo, orgRepo, { session }, !!req.useRegistry) - const targetUserUUID = await userRepo.getUserUUID(usernameParams, shortNameParams, { session }, !!req.useRegistry) + const requesterUUID = await authContext.getRequesterUserUUID(req, userRepo, orgRepo, { session }, LEGACY_FORMAT) + const targetUserUUID = await userRepo.getUserUUID(usernameParams, shortNameParams, { session }, LEGACY_FORMAT) - const isRequesterSecretariat = await authContext.isRequesterSecretariat(req, orgRepo, { session }, !req.useRegistry) - const isAdmin = await authContext.isRequesterAdmin(req, userRepo, orgRepo, { session }, !!req.useRegistry) + const isRequesterSecretariat = await authContext.isRequesterSecretariat(req, orgRepo, { session }, LEGACY_FORMAT) + const isAdmin = await authContext.isRequesterAdmin(req, userRepo, orgRepo, { session }, LEGACY_FORMAT) const targetOrgUUID = await orgRepo.getOrgUUID(shortNameParams, { session }) - // if (req.useRegistry) { - // if (body?.role && typeof body?.role !== 'string') { - // return res.status(400).json({ message: 'Parameters were invalid', details: [{ param: 'role', msg: 'Parameter must be a string' }] }) - // } - // } - if (!targetOrgUUID) { logger.info({ uuid: req.ctx.uuid, message: `Target organization ${shortNameParams} does not exist.` }) await session.abortTransaction() return res.status(404).json(error.orgDnePathParam(shortNameParams)) } - const requesterSameOrg = await authContext.isRequesterSameOrg(req, orgRepo, { UUID: targetOrgUUID, short_name: shortNameParams }, { session }, !req.useRegistry) + const requesterSameOrg = await authContext.isRequesterSameOrg(req, orgRepo, { UUID: targetOrgUUID, short_name: shortNameParams }, { session }, LEGACY_FORMAT) if (!requesterSameOrg && !isRequesterSecretariat) { logger.info({ uuid: req.ctx.uuid, message: `${shortNameParams} organization data can only be modified by users of the same organization or the Secretariat.` }) await session.abortTransaction() @@ -694,7 +644,7 @@ async function updateUser (req, res, next) { } } - const payload = await userRepo.updateUser(usernameParams, shortNameParams, queryParametersJson, { session }, !!req.useRegistry, requesterUUID) + const payload = await userRepo.updateUser(usernameParams, shortNameParams, queryParametersJson, { session }, LEGACY_FORMAT, requesterUUID) await session.commitTransaction() return res.status(200).json({ message: `${usernameParams} was successfully updated.`, updated: payload }) } catch (err) { @@ -715,7 +665,7 @@ async function updateUser (req, res, next) { /** * Resets API secret for specified user. - * Called by PUT /org/{shortname}/user/{username}/reset_secret, PUT /registry/org/{shortname}/user/{username}/reset_secret + * Called by PUT /api/org/{shortname}/user/{username}/reset_secret, PUT /api/registry/org/{shortname}/user/{username}/reset_secret * * @param {Object} req - The request object * @param {Object} res - The response object @@ -730,12 +680,13 @@ async function resetSecret (req, res, next) { const orgRepo = req.ctx.repositories.getBaseOrgRepository() const userRepo = req.ctx.repositories.getBaseUserRepository() + const objectFormat = getObjectFormatForRequest(req) try { session.startTransaction({ readPreference: 'primary' }) // Check if target org exists - const targetOrgUUID = await orgRepo.getOrgUUID(targetOrgShortName, { session }, !req.useRegistry) + const targetOrgUUID = await orgRepo.getOrgUUID(targetOrgShortName, { session }, objectFormat) if (!targetOrgUUID) { logger.info({ uuid: req.ctx.uuid, message: 'Org DNE' }) await session.abortTransaction() @@ -743,11 +694,11 @@ async function resetSecret (req, res, next) { } const targetOrg = { UUID: targetOrgUUID, short_name: targetOrgShortName } - const requesterUserUUID = await authContext.getRequesterUserUUID(req, userRepo, orgRepo, { session }, !!req.useRegistry) - const isRequesterSecretariat = await authContext.isRequesterSecretariat(req, orgRepo, { session }, !req.useRegistry) + const requesterUserUUID = await authContext.getRequesterUserUUID(req, userRepo, orgRepo, { session }, objectFormat) + const isRequesterSecretariat = await authContext.isRequesterSecretariat(req, orgRepo, { session }, objectFormat) if (!isRequesterSecretariat) { - const requesterSameOrg = await authContext.isRequesterSameOrg(req, orgRepo, targetOrg, { session }, !req.useRegistry) + const requesterSameOrg = await authContext.isRequesterSameOrg(req, orgRepo, targetOrg, { session }, objectFormat) if (!requesterSameOrg) { 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.' }) await session.abortTransaction() @@ -756,7 +707,7 @@ async function resetSecret (req, res, next) { } // Check if target user exists in target org - const targetUserUUID = await userRepo.getUserUUID(targetUsername, targetOrgShortName, { session }, !!req.useRegistry) + const targetUserUUID = await userRepo.getUserUUID(targetUsername, targetOrgShortName, { session }, objectFormat) if (!targetUserUUID) { logger.info({ uuid: req.ctx.uuid, message: 'User DNE' }) await session.abortTransaction() @@ -768,7 +719,7 @@ async function resetSecret (req, res, next) { // 1. WE are not the same user if (requesterUserUUID !== targetUserUUID) { // Check to see if we are the admin of the target organization - const isAdminOfTargetOrg = await authContext.isRequesterAdminOfOrg(req, userRepo, orgRepo, targetOrg, { session }, !!req.useRegistry) + const isAdminOfTargetOrg = await authContext.isRequesterAdminOfOrg(req, userRepo, orgRepo, targetOrg, { session }, objectFormat) if (!isAdminOfTargetOrg) { 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.' }) @@ -778,7 +729,7 @@ async function resetSecret (req, res, next) { } } - const updatedSecret = await userRepo.resetSecret(targetUsername, targetOrgShortName, { session }, !!req.useRegistry) + const updatedSecret = await userRepo.resetSecret(targetUsername, targetOrgShortName, { session }, objectFormat) logger.info({ uuid: req.ctx.uuid, message: `The API secret was successfully reset and sent to ${targetUsername}` }) const payload = { diff --git a/src/controller/org.controller/org.middleware.js b/src/controller/org.controller/org.middleware.js index d8331ecc9..fdff50735 100644 --- a/src/controller/org.controller/org.middleware.js +++ b/src/controller/org.controller/org.middleware.js @@ -2,7 +2,7 @@ const getConstants = require('../../constants').getConstants const { validationResult } = require('express-validator') const errors = require('./error') const error = new errors.OrgControllerError() -const { body, param, query } = require('express-validator') +const { param, query } = require('express-validator') const { toUpperCaseArray, isFlatStringArray } = require('../../middleware/middleware') const CONSTANTS = getConstants() const errorMsgs = require('../../middleware/errorMessages') @@ -22,186 +22,8 @@ function isOrgRole (val) { return true } -function validateCreateOrgParameters () { - return async (req, res, next) => { - const useRegistry = req.query.registry === 'true' - let validations = [] - if (useRegistry) { - // Not allowed - // users, , in_use, created, last_updated - const orgOptions = ['CNA', 'Secretariat', 'Bulk Download', 'ADP'] - validations = [ - body(['short_name']).isString() - .trim() - .notEmpty() - .isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - body(['long_name']).isString() - .trim() - .notEmpty(), - body(['cve_program_org_function']) - .default('CNA') - .isString() - .isIn(orgOptions), - body(['oversees']).default([]) - .isArray(), - body(['top_level_root']).default('') - .isString(), - body(['advisory_locations']) - .default([]) - .custom(isFlatStringArray), - body(['advisory_location_require_credentials']) - .default(false) - .isBoolean(), - body(['vulnerability_advisory_location_for_web_scraping']) - .default([]) - .custom(isFlatStringArray), - body(['tl_root_start_date']) - .default(null) - .isDate(), - body(['is_cna_discussion_list']) - .default(false) - .isBoolean(), - body([ - 'program_data.cve_website_update_date', - 'program_data.partner_active_date', - 'program_data.partner_inactive_date' - ]) - .optional({ nullable: true }) - .isDate(), - body(['program_data.cve_website_update_needed']) - .optional() - .isBoolean(), - body( - [ - 'charter_or_scope', - 'disclosure_policy', - 'product_list', - 'contact_info.websites', - 'contact_info.emails', - 'contact_info.phone', - '', - '', - 'partner_role_type', - 'partner_number', - 'partner_country', - 'program_data.status', - 'industry' - ]) - .default('') - .isString(), - body(['authority.active_roles']) - .default([CONSTANTS.AUTH_ROLE_ENUM.CNA]) - .custom(isFlatStringArray) - .customSanitizer(toUpperCaseArray) - .custom(isOrgRole), - body(['id_quota']) - .default(CONSTANTS.DEFAULT_ID_QUOTA) - .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), - ...isNotAllowed('reports_to', 'name', 'users', '', 'in_use', 'created', 'last_updated', 'policies.id_quota') - ] - } else { - validations = [ - body(['short_name']).isString() - .trim() - .notEmpty() - .isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - body(['name']).isString() - .trim() - .notEmpty(), - body(['authority.active_roles']) - .default([CONSTANTS.AUTH_ROLE_ENUM.CNA]) - .custom(isFlatStringArray) - .customSanitizer(toUpperCaseArray) - .custom(isOrgRole), - body(['policies.id_quota']) - .default(CONSTANTS.DEFAULT_ID_QUOTA) - .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), - ...isNotAllowed( - 'oversees', - 'long_name', - 'cve_program_org_function', - 'in_use', - 'created', - 'top_level_root', - 'aliases', - 'id_quota', - 'contact_info.phone', - 'contact_info.websites', - 'contact_info.emails', - 'contact_info', - 'users', - 'charter_or_scope', - 'disclosure_policy', - 'product_list', - 'contact_info.websites', - 'contact_info.emails', - 'contact_info.phone', - 'private_contacts', - 'partner_role_type', - 'partner_number', - 'partner_country', - 'program_data.cve_website_update_date', - 'program_data.cve_website_update_needed', - 'program_data.status', - 'advisory_locations', - 'advisory_location_require_credentials', - 'vulnerability_advisory_location_for_web_scraping', - 'industry', - 'tl_root_start_date', - 'is_cna_discussion_list') - ] - } - - const results = [] - for (const validation of validations) { - const result = await validation.run(req) - if (!result.isEmpty()) { - results.push(...result.errors) - } - } - if (results.length > 0) { - return res.status(400).json({ message: 'Parameters were invalid', details: results }) - } - next() - } -} - -function validateUserIdOrUsername () { - return async (req, res, next) => { - const useRegistry = req.query.registry === 'true' - const validations = [] - if (useRegistry) { - validations.push( - body('user_id') // Condition to run validation - .isString() - .trim() - .notEmpty(isValidUsername)) - } else { - validations.push(body('username').isString().trim().notEmpty(isValidUsername)) - } - const results = [] - for (const validation of validations) { - const result = await validation.run(req) - if (!result.isEmpty()) { - results.push(...result.errors) - } - } - if (results.length > 0) { - return res.status(400).json({ message: 'Parameters were invalid', details: results }) - } - next() - } -} - function validateUpdateOrgParameters () { return async (req, res, next) => { - const useRegistry = req.query.registry === 'true' const allowedParams = [...QUERY_PARAMETERS.shared] const registryParametersOnly = [...QUERY_PARAMETERS.registryOnly] @@ -218,43 +40,8 @@ function validateUpdateOrgParameters () { .custom(isFlatStringArray) .customSanitizer(toUpperCaseArray), // Path parameter validation - param(['shortname']).isString().trim().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH })] - if (useRegistry) { - validations.push( - query(['oversees']).optional().isArray(), - query(['top_level_root']).optional().isString(), - query([ - 'charter_or_scope', - 'disclosure_policy', - 'product_list', - 'contact_info.websites', - 'contact_info.emails', - 'contact_info.phone', - '', - '', - 'partner_role_type', - 'partner_number', - 'partner_country', - 'program_data.cve_website_update_date', - 'program_data.cve_website_update_needed', - 'program_data.status', - 'advisory_location_require_credentials', - 'vulnerability_advisory_location_for_web_scraping', - 'advisory_locations', - 'industry', - 'tl_root_start_date', - 'is_cna_discussion_list' - ]) - .optional() - .isString() - .trim() - ) - } else { - validations.push( - // Block registry-only parameters - ...isNotAllowedQuery(...registryParametersOnly) - ) - } + param(['shortname']).isString().trim().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + ...isNotAllowedQuery(...registryParametersOnly)] const results = [] for (const validation of validations) { @@ -270,16 +57,6 @@ function validateUpdateOrgParameters () { } } -function isNotAllowed (...fields) { - return fields.map(field => - body(field) - .if((value, { req }) => _.has(req.body, field)) - .custom(() => { - throw new Error(`${field} must not be present`) - }) - ) -} - function isNotAllowedQuery (...fields) { return fields.map(field => query(field) @@ -391,6 +168,11 @@ function parseGetParams (req, res, next) { next() } +function parseDeleteParams (req, res, next) { + utils.reqCtxMapping(req, 'params', ['shortname', 'username']) + next() +} + function parseError (req, res, next) { const err = validationResult(req).formatWith(({ location, msg, param, value, nestedErrors }) => { return { msg: msg, param: param, location: location } @@ -413,12 +195,11 @@ module.exports = { parsePutParams, parsePostParams, parseGetParams, + parseDeleteParams, parseError, isOrgRole, isUserRole, isValidUsername, - validateCreateOrgParameters, validateUpdateOrgParameters, - validateUserIdOrUsername, shortCircuitLegacyCpsMitreOrgParameters } diff --git a/src/controller/registry-org.controller/index.js b/src/controller/registry-org.controller/index.js deleted file mode 100644 index aa9acc93b..000000000 --- a/src/controller/registry-org.controller/index.js +++ /dev/null @@ -1,552 +0,0 @@ -const express = require('express') -const router = express.Router() -const mw = require('../../middleware/middleware') -const { param, query, body } = require('express-validator') -const controller = require('./registry-org.controller') -const { parseGetParams, parsePostParams, parseDeleteParams, parseError } = require('./registry-org.middleware') -const getConstants = require('../../constants').getConstants -const CONSTANTS = getConstants() - -router.get('/registryOrg', - /* - #swagger.tags = ['Registry Organization'] - #swagger.operationId = 'getAllRegistryOrgs' - #swagger.ignore = true - #swagger.summary = "Retrieves information about all registry organizations (accessible to Secretariat only)" - #swagger.description = " -Only users with Secretariat role can access this endpoint
-Secretariat: Retrieves a list of all registry organizations
- #swagger.parameters['$ref'] = [ - '#/components/parameters/pageQuery', - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.responses[200] = { - description: 'A list of all registry organizations, along with pagination fields if results span multiple pages of data', - content: { - "application/json": { - schema: { $ref: '../schemas/registry-org/list-registry-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[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']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), - query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }), - parseError, - parseGetParams, - controller.ALL_ORGS -) - -router.get('/registryOrg/:identifier', - /* - #swagger.tags = ['Registry Organization'] - #swagger.operationId = 'getSingleRegistryOrg' - #swagger.ignore = true - #swagger.summary = "Retrieves information about a specific registry organization (accessible to Secretariat only)" - #swagger.description = " -Only users with Secretariat role can access this endpoint
-Secretariat: Retrieves information about the specified registry organization
- #swagger.parameters['identifier'] = { - in: 'path', - description: 'The identifier of the registry organization', - required: true, - type: 'string' - } - #swagger.parameters['$ref'] = [ - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.responses[200] = { - description: 'The requested registry organization information is returned', - content: { - "application/json": { - schema: { $ref: '../schemas/registry-org/get-registry-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[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(['identifier']).isString().trim(), - parseError, - parseGetParams, - controller.SINGLE_ORG -) - -router.post('/registryOrg', - /* - #swagger.tags = ['Registry Organization'] - #swagger.operationId = 'createRegistryOrg' - #swagger.ignore = true - #swagger.summary = "Creates a new registry organization (accessible to Secretariat only)" - #swagger.description = " -Only users with Secretariat role can access this endpoint
-Secretariat: Creates a new registry organization
- #swagger.parameters['$ref'] = [ - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.requestBody = { - required: true, - content: { - 'application/json': { - schema: { $ref: '../schemas/registry-org/create-registry-org-request.json' } - } - } - } - #swagger.responses[201] = { - description: 'The registry organization was successfully created', - content: { - "application/json": { - schema: { $ref: '../schemas/registry-org/create-registry-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[500] = { - description: 'Internal Server Error', - content: { - "application/json": { - schema: { $ref: '../schemas/errors/generic.json' } - } - } - } - */ - mw.useRegistry(), - mw.validateUser, - mw.onlySecretariat, - body(['reports_to']).not().exists().withMessage('reports_to must not be present'), - parseError, - parsePostParams, - controller.CREATE_ORG -) - -router.put('/registryOrg/:shortname', - /* - #swagger.tags = ['Registry Organization'] - #swagger.operationId = 'updateRegistryOrg' - #swagger.ignore = true - #swagger.summary = "Updates an existing registry organization (accessible to Secretariat only)" - #swagger.description = " -Only users with Secretariat role can access this endpoint
-Secretariat: Updates an existing registry organization
- #swagger.parameters['shortname'] = { - in: 'path', - description: 'The Shortname of the registry organization to update', - required: true, - type: 'string' - } - #swagger.parameters['$ref'] = [ - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.requestBody = { - required: true, - content: { - 'application/json': { - schema: { $ref: '../schemas/registry-org/update-registry-org-request.json' } - } - } - } - #swagger.responses[200] = { - description: 'The registry organization was successfully updated', - content: { - "application/json": { - schema: { $ref: '../schemas/registry-org/update-registry-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.useRegistry(), - mw.validateUser, - mw.onlySecretariat, - param(['shortname']).isString().trim(), - body(['reports_to']).not().exists().withMessage('reports_to must not be present'), - parseError, - parsePostParams, - controller.UPDATE_ORG -) - -router.delete( - '/registryOrg/:identifier', - /* - #swagger.tags = ['Registry Organization'] - #swagger.operationId = 'deleteRegistryOrg' - #swagger.ignore = true - #swagger.summary = "Deletes an existing registry organization (accessible to Secretariat only)" - #swagger.description = " -Only users with Secretariat role can access this endpoint
-Secretariat: Deletes an existing registry organization
- #swagger.parameters['identifier'] = { - in: 'path', - description: 'The identifier of the registry organization to delete', - required: true, - type: 'string' - } - #swagger.parameters['$ref'] = [ - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.responses[200] = { - description: 'The registry organization was successfully deleted', - content: { - "application/json": { - schema: { - type: 'object', - properties: { - message: { - type: 'string', - description: 'Message describing successful deletion operation' - } - } - } - } - } - } - #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' } - } - } - } - */ - mw.useRegistry(), - mw.validateUser, - mw.onlySecretariat, - param(['identifier']).isString().trim(), - parseError, - parseDeleteParams, - controller.DELETE_ORG -) - -router.get('/registryOrg/:shortname/users', - /* - #swagger.tags = ['Registry User'] - #swagger.operationId = 'registryUserOrgAll' - #swagger.ignore = true - #swagger.summary = "Retrieves all users for the organization with the specified short name (accessible to Secretariat only)" - #swagger.description = " -Only users with Secretariat role can access this endpoint
-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/registry-user/list-registry-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.useRegistry(), - mw.validateUser, - mw.onlySecretariat, - param(['shortname']).isString().trim().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('/registryOrg/:shortname/user', - /* - #swagger.tags = ['Registry User'] - #swagger.operationId = 'RegistryUserCreateSingle' - #swagger.ignore = true - #swagger.summary = "Create a user with the provided short name as the owning organization (accessible to Secretariat only)" - #swagger.description = " -Only users with Secretariat role can access this endpoint
-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/registry-user/create-registry-user-request.json' }, - } - } - } - #swagger.responses[200] = { - description: 'Returns the new user information (with the secret)', - content: { - "application/json": { - schema: { $ref: '../schemas/registry-user/create-registry-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.useRegistry(), - mw.validateUser, - mw.onlySecretariat, - param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), - - parseError, - parsePostParams, - controller.USER_CREATE_SINGLE) - -module.exports = router diff --git a/src/controller/registry-org.controller/registry-org.middleware.js b/src/controller/registry-org.controller/registry-org.middleware.js deleted file mode 100644 index 2d79faf6a..000000000 --- a/src/controller/registry-org.controller/registry-org.middleware.js +++ /dev/null @@ -1,74 +0,0 @@ -const utils = require('../../utils/utils') -const getConstants = require('../../constants').getConstants -const { validationResult } = require('express-validator') -const errors = require('./error') -const error = new errors.RegistryOrgControllerError() - -function parsePostParams (req, res, next) { - req.body = utils.deepRemoveEmpty(req.body) - utils.reqCtxMapping(req, 'body', []) - utils.reqCtxMapping(req, 'params', ['identifier', 'shortname']) - utils.reqCtxMapping(req, 'query', [ - 'long_name', 'short_name', 'aliases', - 'cve_program_org_function', 'authority.active_roles', - 'oversees', - 'top_level_root', 'users', - 'charter_or_scope', 'disclosure_policy', 'product_list', - 'id_quota', - 'private_contacts', 'contact_info.websites', 'contact_info.emails', 'contact_info.phone', - 'partner_role_type', - 'partner_number', - 'partner_country', - 'program_data.cve_website_update_date', - 'program_data.cve_website_update_needed', - 'program_data.status', - 'advisory_locations', - 'advisory_location_require_credentials', - 'vulnerability_advisory_location_for_web_scraping', - 'industry', - 'tl_root_start_date', - 'is_cna_discussion_list' - ]) - next() -} - -function parseGetParams (req, res, next) { - utils.reqCtxMapping(req, 'params', ['identifier', 'shortname']) - utils.reqCtxMapping(req, 'query', ['page']) - next() -} - -function parseDeleteParams (req, res, next) { - utils.reqCtxMapping(req, 'params', ['identifier']) - next() -} - -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 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() -} - -module.exports = { - parsePostParams, - parseGetParams, - parseError, - parseDeleteParams, - isOrgRole -} diff --git a/src/controller/registry-user.controller/index.js b/src/controller/registry-user.controller/index.js deleted file mode 100644 index 179987a83..000000000 --- a/src/controller/registry-user.controller/index.js +++ /dev/null @@ -1,397 +0,0 @@ -const express = require('express') -const router = express.Router() -const mw = require('../../middleware/middleware') -const { param, query } = require('express-validator') -const controller = require('./registry-user.controller') -const { parseGetParams, parsePostParams, parseDeleteParams, parseError } = require('./registry-user.middleware') -const getConstants = require('../../constants').getConstants -const CONSTANTS = getConstants() - -router.get('/registryUser', - /* - #swagger.tags = ['Secretariat Only Utility Endpoints'] - #swagger.operationId = 'getAllRegistryUsers' - #swagger.ignore = true - #swagger.summary = "Retrieves information about all registry users (accessible to Secretariat only)" - #swagger.description = " -Only users with Secretariat role can access this endpoint
-Secretariat: Retrieves a list of all registry users
- #swagger.parameters['$ref'] = [ - '#/components/parameters/pageQuery', - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.responses[200] = { - description: 'A list of all registry users, along with pagination fields if results span multiple pages of data', - content: { - "application/json": { - schema: { $ref: '../schemas/registry-user/list-registry-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[500] = { - description: 'Internal Server Error', - content: { - "application/json": { - schema: { $ref: '../schemas/errors/generic.json' } - } - } - } - */ - mw.validateUser, - mw.onlySecretariat, - query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }), - query(['page']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), - parseError, - parseGetParams, - controller.ALL_USERS -) - -router.get('/registryUser/:identifier', -/* - #swagger.tags = ['Secretariat Only Utility Endpoints'] - #swagger.operationId = 'getSingleRegistryUser' - #swagger.ignore = true - #swagger.summary = "Retrieves information about a specific registry user (accessible to Secretariat only)" - #swagger.description = " -Only users with Secretariat role can access this endpoint
-Secretariat: Retrieves information about the specified registry user
- #swagger.parameters['identifier'] = { - in: 'path', - description: 'The identifier of the registry user', - required: true, - type: 'string' - } - #swagger.parameters['$ref'] = [ - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.responses[200] = { - description: 'The requested registry user information is returned', - content: { - "application/json": { - schema: { $ref: '../schemas/registry-user/get-registry-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[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(['identifier']).isString().trim(), - parseError, - parseGetParams, - controller.SINGLE_USER -) - -router.post('/registryUser/:shortname', - /* - #swagger.tags = ['Secretariat Only Utility Endpoints'] - #swagger.operationId = 'createRegistryUser' - #swagger.ignore = true - #swagger.summary = "Creates a new registry user (accessible to Secretariat only)" - #swagger.description = " -Only users with Secretariat role can access this endpoint
-Secretariat: Creates a new registry user
- #swagger.parameters['$ref'] = [ - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.requestBody = { - required: true, - content: { - 'application/json': { - schema: { $ref: '../schemas/registry-user/create-registry-user-request.json' } - } - } - } - #swagger.responses[201] = { - description: 'The registry user was successfully created', - content: { - "application/json": { - schema: { $ref: '../schemas/registry-user/create-registry-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[500] = { - description: 'Internal Server Error', - content: { - "application/json": { - schema: { $ref: '../schemas/errors/generic.json' } - } - } - } - */ - mw.validateUser, - mw.onlySecretariat, - param(['shortname']).isString().trim(), - parseError, - parsePostParams, - controller.CREATE_USER -) - -router.put('/registryUser/:identifier', - /* - #swagger.tags = ['Secretariat Only Utility Endpoints'] - #swagger.operationId = 'updateRegistryUser' - #swagger.ignore = true - #swagger.summary = "Updates an existing registry user (accessible to Secretariat only)" - #swagger.description = " -Only users with Secretariat role can access this endpoint
-Secretariat: Updates an existing registry user
- #swagger.parameters['identifier'] = { - in: 'path', - description: 'The identifier of the registry user to update', - required: true, - type: 'string' - } - #swagger.parameters['$ref'] = [ - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.requestBody = { - required: true, - content: { - 'application/json': { - schema: { $ref: '../schemas/registry-user/update-registry-user-request.json' } - } - } - } - #swagger.responses[200] = { - description: 'The registry user was successfully updated', - content: { - "application/json": { - schema: { $ref: '../schemas/registry-user/update-registry-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.onlySecretariat, - param(['identifier']).isString().trim(), - // TODO: do more validation here - parseError, - parsePostParams, - controller.UPDATE_USER -) - -router.delete( - '/registryUser/:identifier', - /* - #swagger.tags = ['Secretariat Only Utility Endpoints'] - #swagger.operationId = 'deleteRegistryUser' - #swagger.ignore = true - #swagger.summary = "Deletes an existing registry user (accessible to Secretariat only)" - #swagger.description = " -Only users with Secretariat role can access this endpoint
-Secretariat: Deletes an existing registry user
- #swagger.parameters['identifier'] = { - in: 'path', - description: 'The identifier of the registry user to delete', - required: true, - type: 'string' - } - #swagger.parameters['$ref'] = [ - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.responses[200] = { - description: 'The registry user was successfully deleted', - content: { - "application/json": { - schema: { - type: 'object', - properties: { - message: { - type: 'string', - description: 'Message describing successful deletion operation' - } - } - } - } - } - } - #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(['identifier']).isString().trim(), - parseError, - parseDeleteParams, - controller.DELETE_USER -) - -module.exports = router diff --git a/src/controller/registry-user.controller/registry-user.middleware.js b/src/controller/registry-user.controller/registry-user.middleware.js deleted file mode 100644 index cffcb7e6f..000000000 --- a/src/controller/registry-user.controller/registry-user.middleware.js +++ /dev/null @@ -1,42 +0,0 @@ -const utils = require('../../utils/utils') -const { validationResult } = require('express-validator') -const errors = require('../registry-org.controller/error') -const error = new errors.RegistryOrgControllerError() - -function parsePostParams (req, res, next) { - utils.reqCtxMapping(req, 'body', []) - utils.reqCtxMapping(req, 'params', ['identifier', 'shortname']) - utils.reqCtxMapping(req, 'query', [ - 'new_username', - 'name.first', 'name.last', 'name.middle', 'name.suffix' - ]) - next() -} - -function parseGetParams (req, res, next) { - utils.reqCtxMapping(req, 'params', ['identifier']) - utils.reqCtxMapping(req, 'query', ['page']) - next() -} - -function parseDeleteParams (req, res, next) { - utils.reqCtxMapping(req, 'params', ['identifier']) - 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() -} - -module.exports = { - parsePostParams, - parseGetParams, - parseDeleteParams, - parseError -} diff --git a/src/controller/registry.controller/index.js b/src/controller/registry.controller/index.js new file mode 100644 index 000000000..2735f8cdf --- /dev/null +++ b/src/controller/registry.controller/index.js @@ -0,0 +1,1501 @@ +const express = require('express') +const router = express.Router() +const mw = require('../../middleware/middleware') +const errorMsgs = require('../../middleware/errorMessages') +const controller = require('../org.controller/org.controller') +const registryOrgController = require('./org.registry.controller') +const registryUserController = require('./user.registry.controller') +const { body, param, query } = require('express-validator') +const { parseGetParams, parsePostParams, parsePutParams, parseDeleteParams, parseError, isUserRole, isValidUsername } = require('../org.controller/org.middleware') +// Only God and Javascript know swhy its saying it is not used when it is..... +// eslint-disable-next-line no-unused-vars +const { toUpperCaseArray, isFlatStringArray } = require('../../middleware/middleware') +const getConstants = require('../../../src/constants').getConstants +const CONSTANTS = getConstants() +const { parseGetParams: parseUserGetParams, parseError: parseUserError } = require('../user.controller/user.middleware') + +router.get('/registry/org', + /* + #swagger.tags = ['Registry Organization'] + #swagger.operationId = 'registryOrgAll' + #swagger.summary = "Retrieves all registry organizations (accessible to Secretariat)" + #swagger.description = " +User must belong to an organization with the Secretariat role
+Secretariat: Retrieves information about all registry organizations
" + #swagger.parameters['$ref'] = [ + '#/components/parameters/pageQuery', + '#/components/parameters/apiEntityHeader', + '#/components/parameters/apiUserHeader', + '#/components/parameters/apiSecretHeader' + ] + #swagger.responses[200] = { + description: 'Returns information about all registry organizations, along with pagination fields if results span multiple pages of data', + content: { + "application/json": { + schema: { + $ref: '../schemas/registry-org/list-registry-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.useRegistry(), + mw.validateUser, + mw.onlySecretariat, + query().custom((query) => { return mw.validateQueryParameterNames(query, ['page']) }), + query(['page']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), + query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }), + parseError, + parseGetParams, + registryOrgController.ALL_ORGS +) + +router.get('/registry/org/:shortname/users', + /* + #swagger.tags = ['Registry User'] + #swagger.operationId = 'registryOrgUsersAll' + #swagger.summary = "Retrieves all users for the organization with the specified short name (accessible to same-organization users or Secretariat)" + #swagger.description = " +Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
+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/registry-user/list-registry-users-response.json' + }, + example: { + "totalCount": 1, + "itemsPerPage": 100, + "pageCount": 1, + "currentPage": 1, + "prevPage": null, + "nextPage": null, + "users": [ + { + "UUID": "fe566221-6a2c-4279-8800-4d3795325997", + "username": "jdoe", + "name": { + "first": "John", + "last": "Doe" + }, + "role": "ADMIN", + "status": "active", + "created": "2021-02-12T17:15:37.382Z", + "last_updated": "2021-02-12T17:15:37.382Z" + } + ] + } + } + } + } + #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.useRegistry(), + mw.validateUser, + param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + query().custom((query) => { return mw.validateQueryParameterNames(query, ['page']) }), + query(['page']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), + query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }), + parseError, + parseGetParams, + registryOrgController.USER_ALL) + +router.get('/registry/org/:shortname/id_quota', + /* + #swagger.tags = ['Registry Organization'] + #swagger.operationId = 'registryOrgIdQuota' + #swagger.summary = "Retrieves an organization's CVE ID quota (accessible to same-organization users or Secretariat)" + #swagger.description = " +Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
+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/registry-org/get-registry-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.useRegistry(), + mw.validateUser, + param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + query().custom((query) => { return mw.validateQueryParameterNames(query, ['']) }), + parseError, + parseGetParams, + controller.ORG_ID_QUOTA) + +router.get('/registry/org/:identifier', + /* + #swagger.tags = ['Registry Organization'] + #swagger.operationId = 'registryOrgSingle' + #swagger.summary = "Retrieves information about the registry organization specified by short name or UUID (accessible to same-organization users or Secretariat)" + #swagger.description = " +Authenticated users can access this endpoint only for their own organization. Secretariat users can access any organization.
+Regular, CNA & Admin Users: Retrieves registry organization record for the specified shortname or UUID if it is the user's organization
+Secretariat: Retrieves information about any registry organization
" + #swagger.parameters['identifier'] = { description: 'The shortname or UUID of the registry organization' } + #swagger.parameters['expand'] = { + in: 'query', + description: 'Optional expanded related data. Accepted value: users.', + required: false, + schema: { + type: 'string', + enum: ['users'] + } + } + #swagger.parameters['$ref'] = [ + '#/components/parameters/apiEntityHeader', + '#/components/parameters/apiUserHeader', + '#/components/parameters/apiSecretHeader' + ] + #swagger.responses[200] = { + description: 'Returns the registry organization information', + content: { + "application/json": { + schema: { + $ref: '../schemas/registry-org/get-registry-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.useRegistry(), + mw.validateUser, + query().custom((query) => { return mw.validateQueryParameterNames(query, ['expand']) }), + query(['expand']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), + query(['expand']).optional().isIn(['users']), + parseError, + parseGetParams, + registryOrgController.SINGLE_ORG +) + +router.get('/registry/org/:shortname/user/:username', + /* + #swagger.tags = ['Registry User'] + #swagger.operationId = 'registryUserSingle' + #swagger.summary = "Retrieves information about a user for the specified username and organization short name (accessible to same-organization users or Secretariat)" + #swagger.description = " +Authenticated users can access this endpoint only for users in their own organization. Secretariat users can access any user.
+Regular, CNA & Admin Users: Retrieves information about a registry user in the same organization
+Secretariat: Retrieves any registry user's information
" + #swagger.parameters['$ref'] = [ + '#/components/parameters/apiEntityHeader', + '#/components/parameters/apiUserHeader', + '#/components/parameters/apiSecretHeader' + ] + #swagger.parameters['shortname'] = { + description: 'The shortname of the organization' + } + #swagger.parameters['username'] = { + description: 'The username of the registry user', + schema: { + type: 'string', + pattern: '^[a-zA-Z0-9._@-]+$' + } + } + #swagger.responses[200] = { + description: 'Returns information about the specified registry user', + content: { + "application/json": { + schema: { $ref: '../schemas/registry-user/get-registry-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.useRegistry(), + mw.validateUser, + param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + param(['username']).isString().trim().notEmpty().custom(isValidUsername), + query().custom((query) => { return mw.validateQueryParameterNames(query, ['']) }), + parseError, + parseGetParams, + registryUserController.SINGLE_USER +) + +router.post('/registry/org', + /* + #swagger.tags = ['Registry Organization'] + #swagger.operationId = 'registryOrgCreateSingle' + #swagger.summary = "Creates an organization (accessible to Secretariat)" + #swagger.description = " +User must belong to an organization with the Secretariat role
+Secretariat: Creates a new organization
" + #swagger.parameters['$ref'] = [ + '#/components/parameters/apiEntityHeader', + '#/components/parameters/apiUserHeader', + '#/components/parameters/apiSecretHeader' + ] + #swagger.requestBody = { + required: true, + content: { + 'application/json': { + schema: { + anyOf: [ + { $ref: '../schemas/registry-org/SecretariatOrg.json' }, + { $ref: '../schemas/registry-org/CNAOrg.json' }, + { $ref: '../schemas/registry-org/ADPOrg.json' }, + { $ref: '../schemas/registry-org/BulkDownloadOrg.json' } + ] + }, + example: { + short_name: 'fake_company', + long_name: 'Fake Company', + id_quota: 1000, + authority: ['CNA'] + } + } + } + } + #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/registry-org/list-registry-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.useRegistry(), + mw.validateUser, + mw.onlySecretariat, + query().custom((query) => { return mw.validateQueryParameterNames(query, ['']) }), + parsePostParams, + parseError, + registryOrgController.CREATE_ORG +) + +router.put('/registry/org/:shortname', + /* + #swagger.tags = ['Registry Organization'] + #swagger.operationId = 'registryOrgUpdateSingle' + #swagger.summary = "Updates information about the organization specified by short name (accessible to Secretariat or same-organization Admin)" + #swagger.description = " +User must belong to an organization with the Secretariat role or be an Admin of the requested organization.
+With Joint Approval required for the following fields:
+Secretariat: Updates any organization's information
+Organization Admin: Requests changes to its organization's information
+User must belong to an organization with the Secretariat role
+Secretariat: Deletes the specified registry organization
" + #swagger.parameters['shortname'] = { description: 'The shortname of the registry organization' } + #swagger.parameters['$ref'] = [ + '#/components/parameters/apiEntityHeader', + '#/components/parameters/apiUserHeader', + '#/components/parameters/apiSecretHeader' + ] + #swagger.responses[200] = { + description: 'Confirms deletion of the registry organization', + content: { + "application/json": { + schema: { $ref: '../schemas/registry-org/delete-registry-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.useRegistry(), + mw.validateUser, + mw.onlySecretariat, + param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + parseError, + parseDeleteParams, + registryOrgController.DELETE_ORG +) + +router.post('/registry/org/:shortname/user', + /* + #swagger.tags = ['Registry User'] + #swagger.operationId = 'registryUserCreateSingle' + #swagger.summary = "Create a user with the provided short name as the owning organization (accessible to Secretariat or target organization Admin)" + #swagger.description = " +User must belong to an organization with the Secretariat role or be an Admin of the target organization
+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/registry-user/create-registry-user-request.json' } + }, + example: { + "username": "jdoe", + "status": "active", + "name": { + "first": "John", + "last": "Doe" + } + } + } + } + #swagger.responses[200] = { + description: 'Returns the new user information (with the secret)', + content: { + "application/json": { + schema: + { $ref: '../schemas/registry-user/create-registry-user-response.json' } + }, + example: { + "message": "jdoe was successfully created.", + "created": { + "UUID": "fe566221-6a2c-4279-8800-4d3795325997", + "username": "jdoe", + "name": { + "first": "John", + "last": "Doe" + }, + "status": "active", + "secret": "12345-abcde-67890", + "created": "2021-02-12T17:15:37.382Z", + "last_updated": "2021-02-12T17:15:37.382Z" + } + } + } + } + #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.useRegistry(), + mw.validateUser, + mw.onlySecretariatOrAdmin, + mw.onlyOrgWithPartnerRole, + param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + body(['org_uuid']).optional().isString().trim(), + body(['uuid']).optional().isString().trim(), + body(['name.first']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_FIRSTNAME_LENGTH }).withMessage(errorMsgs.FIRSTNAME_LENGTH), + body(['name.last']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_LASTNAME_LENGTH }).withMessage(errorMsgs.LASTNAME_LENGTH), + body(['name.middle']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_MIDDLENAME_LENGTH }).withMessage(errorMsgs.MIDDLENAME_LENGTH), + body(['name.suffix']).optional().isString().trim().isLength({ max: CONSTANTS.MAX_SUFFIX_LENGTH }).withMessage(errorMsgs.SUFFIX_LENGTH), + body(['authority.active_roles']).optional() + .custom(mw.isFlatStringArray) + .bail() + .customSanitizer(toUpperCaseArray) + .custom(isUserRole), + parseError, + parsePostParams, + registryOrgController.USER_CREATE_SINGLE +) + +router.put('/registry/org/:shortname/user/:username', + /* + #swagger.tags = ['Registry User'] + #swagger.operationId = 'registryUserUpdateSingle' + #swagger.summary = "Updates information about a user for the specified username and organization shortname (accessible to self, same-organization Admins, or Secretariat)" + #swagger.description = " +Authenticated users can update their own name fields. Organization admins can update users in their organization. Secretariat users can update users in any organization.
+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/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/registry-user/update-registry-user-response.json' }, + example: { + "message": "jdoe was successfully updated.", + "updated": { + "UUID": "fe566221-6a2c-4279-8800-4d3795325997", + "username": "jdoe", + "name": { + "first": "John", + "last": "Doe" + }, + "status": "active", + "created": "2021-02-12T17:15:37.382Z", + "last_updated": "2021-02-12T17:15:37.382Z" + } + } + } + } + } + #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.useRegistry(), + mw.validateUser, + mw.onlyOrgWithPartnerRole, + parseError, + parsePutParams, + registryUserController.UPDATE_USER) + +router.delete('/registry/org/:shortname/user/:username', + /* + #swagger.tags = ['Registry User'] + #swagger.operationId = 'registryUserDeleteSingle' + #swagger.summary = "Deletes the registry user specified by organization and username (accessible to Secretariat only)" + #swagger.description = " +User must belong to an organization with the Secretariat role
+Secretariat: Deletes the specified user from the specified organization
" + #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: 'Confirms deletion of the registry user', + content: { + "application/json": { + schema: { $ref: '../schemas/registry-user/delete-registry-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.useRegistry(), + mw.validateUser, + mw.onlySecretariat, + param(['shortname']).isString().trim().notEmpty().isLength({ min: CONSTANTS.MIN_SHORTNAME_LENGTH, max: CONSTANTS.MAX_SHORTNAME_LENGTH }), + param(['username']).isString().trim().notEmpty().custom(isValidUsername), + parseError, + parseDeleteParams, + registryUserController.DELETE_USER +) + +router.put('/registry/org/:shortname/user/:username/reset_secret', + /* + #swagger.tags = ['Registry User'] + #swagger.operationId = 'registryUserResetSecret' + #swagger.summary = "Reset the API key for a user (accessible to self, same-organization Admins, or Secretariat)" + #swagger.description = " +Authenticated users can reset their own API secret. Organization admins can reset users in their organization. Secretariat users can reset any user's API secret.
+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.useRegistry(), + mw.validateUser, + mw.onlyOrgWithPartnerRole, + parseError, + parsePostParams, + controller.USER_RESET_SECRET +) + +router.post('/registry/org/:shortname/oversees/:shortname2/add', + /* + #swagger.tags = ['Registry Organization'] + #swagger.operationId = 'registryOrgAddOverseeRelationship' + #swagger.summary = 'Assigns an organization to report to a ROOT organization (accessible to Secretariat only)' + #swagger.description = ' +User must belong to an organization with the Secretariat role.
+The organization identified by shortname must have ROOT authority. The organization identified by shortname2 will + report to it. If shortname2 already reported to a different ROOT. It will be removed from that and both will receive an audit entry.
' + #swagger.parameters['shortname'] = { description: 'The shortname of the overseeing ROOT organization' } + #swagger.parameters['shortname2'] = { description: 'The shortname of the reporting organization' } + #swagger.parameters['$ref'] = [ + '#/components/parameters/apiEntityHeader', + '#/components/parameters/apiUserHeader', + '#/components/parameters/apiSecretHeader' + ] + #swagger.responses[200] = { + description: 'Reports-to relationship added successfully', + content: { 'application/json': { schema: { type: 'object', properties: { message: { type: 'string' } } } } } + } + #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.useRegistry(), + mw.validateUser, + mw.onlySecretariat, + parseError, + parsePostParams, + registryOrgController.ADD_OVERSEE_RELATIONSHIP +) + +router.post('/registry/org/:shortname/oversees/:shortname2/remove', + /* + #swagger.tags = ['Registry Organization'] + #swagger.operationId = 'registryOrgRemoveOverseeRelationship' + #swagger.summary = 'Removes an organization reports-to relationship (accessible to Secretariat only)' + #swagger.description = ' +User must belong to an organization with the Secretariat role.
+The organization identified by shortname must have ROOT authority. The organization identified by shortname2 will no longer report to it.
' + #swagger.parameters['shortname'] = { description: 'The shortname of the overseeing ROOT organization' } + #swagger.parameters['shortname2'] = { description: 'The shortname of the reporting organization' } + #swagger.parameters['$ref'] = [ + '#/components/parameters/apiEntityHeader', + '#/components/parameters/apiUserHeader', + '#/components/parameters/apiSecretHeader' + ] + #swagger.responses[200] = { + description: 'Reports-to relationship removed successfully', + content: { 'application/json': { schema: { type: 'object', properties: { message: { type: 'string' } } } } } + } + #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.useRegistry(), + mw.validateUser, + mw.onlySecretariat, + parseError, + parsePostParams, + registryOrgController.REMOVE_OVERSEE_RELATIONSHIP +) + +router.post('/registry/org/:shortname/user/:username/grant-role', + /* + #swagger.tags = ['Registry User'] + #swagger.operationId = 'registryUserGrantRole' + #swagger.summary = "Grants a role to a user (accessible to Secretariat or Org Admin)" + #swagger.description = " +User must belong to an organization with the Secretariat role or be an Admin of the target organization
+Admin User: Grants a role to a user in the Admin's organization
+Secretariat: Grants a role to a user in any organization
" + #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.requestBody = { + required: true, + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + role: { + type: 'string', + enum: ['ADMIN'] + } + }, + required: ['role'] + } + } + } + } + #swagger.responses[200] = { + description: 'Role granted successfully', + content: { + "application/json": { + schema: { type: 'object', properties: { message: { type: 'string' } } } + } + } + } + #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.useRegistry(), + mw.validateUser, + // mw.onlyOrgWithPartnerRole, // This might be too restrictive if we want Secretariat to do it for any org type + parseError, + parsePostParams, + registryUserController.GRANT_ROLE +) + +router.post('/registry/org/:shortname/user/:username/revoke-role', + /* + #swagger.tags = ['Registry User'] + #swagger.operationId = 'registryUserRevokeRole' + #swagger.summary = "Revokes a role from a user (accessible to Secretariat or Org Admin)" + #swagger.description = " +User must belong to an organization with the Secretariat role or be an Admin of the target organization
+Admin User: Revokes a role from a user in the Admin's organization
+Secretariat: Revokes a role from a user in any organization
" + #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.requestBody = { + required: true, + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + role: { + type: 'string', + enum: ['ADMIN'] + } + }, + required: ['role'] + } + } + } + } + #swagger.responses[200] = { + description: 'Role revoked successfully', + content: { + "application/json": { + schema: { type: 'object', properties: { message: { type: 'string' } } } + } + } + } + #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.useRegistry(), + mw.validateUser, + // mw.onlyOrgWithPartnerRole, + parseError, + parsePostParams, + registryUserController.REVOKE_ROLE +) + +router.put('/registry/org/:shortname/conversation/:index', + /* + #swagger.tags = ['Registry Organization'] + #swagger.operationId = 'registryOrgUpdateConversation' + #swagger.summary = "Update the conversation at the given index for the given organization (accessible to Secretariat or original same-organization author)" + #swagger.description = " +User must belong to an organization with the Secretariat role or be the original author of the conversation in the same organization
+Original Author: Allowed to update only the message body of a conversation posted by them
+Secretariat: Allowed to update the message body and/or visibility of any conversation
" + #swagger.parameters['shortname'] = { description: 'The shortname of the organization' } + #swagger.parameters['index'] = { description: 'The index of the conversation to update' } + #swagger.parameters['$ref'] = [ + '#/components/parameters/apiEntityHeader', + '#/components/parameters/apiUserHeader', + '#/components/parameters/apiSecretHeader' + ] + #swagger.responses[200] = { + description: 'Returns the updated conversation', + content: { + "application/json": { + schema: { $ref: '../schemas/conversation/update-conversation-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.useRegistry(), + mw.validateUser, + mw.onlyOrgWithPartnerRole, + parseError, + parsePostParams, + registryOrgController.EDIT_CONVERSATION +) + +router.get('/registry/users', + /* + #swagger.tags = ['Registry User'] + #swagger.operationId = 'registryUserAll' + #swagger.summary = "Retrieves information about all registered users (accessible to Secretariat)" + #swagger.description = " +User must belong to an organization with the Secretariat role
+Secretariat: Retrieves information about all users for all organizations
" + #swagger.parameters['$ref'] = [ + '#/components/parameters/pageQuery', + '#/components/parameters/apiEntityHeader', + '#/components/parameters/apiUserHeader', + '#/components/parameters/apiSecretHeader' + ] + #swagger.responses[200] = { + description: 'Returns all users, along with pagination fields if results span multiple pages of data.', + content:{ + "application/json":{ + schema: { + $ref: '../schemas/registry-user/list-registry-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.useRegistry(), + mw.validateUser, + mw.onlySecretariat, + query().custom((query) => { return mw.validateQueryParameterNames(query, ['page']) }), + query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }), + query(['page']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), + parseUserError, + parseUserGetParams, + registryUserController.ALL_USERS +) + +module.exports = router diff --git a/src/controller/registry-org.controller/error.js b/src/controller/registry.controller/org.error.js similarity index 94% rename from src/controller/registry-org.controller/error.js rename to src/controller/registry.controller/org.error.js index 1b350b417..c48ec8a77 100644 --- a/src/controller/registry-org.controller/error.js +++ b/src/controller/registry.controller/org.error.js @@ -1,5 +1,6 @@ const idrErr = require('../../utils/error') +/** Registry organization controller errors. */ class RegistryOrgControllerError extends idrErr.IDRError { orgDnePathParam (shortname) { // org const err = {} @@ -140,6 +141,13 @@ class RegistryOrgControllerError extends idrErr.IDRError { err.message = `The following fields can only be modified by the Secretariat: ${fields.join(', ')}.` return err } + + overseeingOrgMustBeRoot (shortname) { + const err = {} + err.error = 'OVERSEEING_ORG_MUST_BE_ROOT' + err.message = `The '${shortname}' organization must have ROOT authority to oversee another organization.` + return err + } } module.exports = { diff --git a/src/controller/registry-org.controller/registry-org.controller.js b/src/controller/registry.controller/org.registry.controller.js similarity index 89% rename from src/controller/registry-org.controller/registry-org.controller.js rename to src/controller/registry.controller/org.registry.controller.js index cf739a819..56ff62871 100644 --- a/src/controller/registry-org.controller/registry-org.controller.js +++ b/src/controller/registry.controller/org.registry.controller.js @@ -1,13 +1,15 @@ +/** Registry organization route handlers. */ const mongoose = require('mongoose') const logger = require('../../middleware/logger') const { getConstants } = require('../../constants') const _ = require('lodash') -const errors = require('./error') +const errors = require('./org.error') const error = new errors.RegistryOrgControllerError() const conversationErrors = require('../conversation.controller/error') const convoError = new conversationErrors.ConversationControllerError() const validateUUID = require('uuid').validate const authContext = require('../../utils/authContext') +const { REGISTRY_FORMAT } = require('../format.constants') function addUUIDsToSet (uuidSet, values) { if (!Array.isArray(values)) return @@ -106,7 +108,7 @@ function removeAdditionalContactUUIDFields (org) { * @param {function} next - The next middleware function. * @returns {PromiseUser must belong to an organization with the Secretariat role
-Secretariat: Retrieves information about all users for all organizations
" - #swagger.parameters['$ref'] = [ - '#/components/parameters/pageQuery', - '#/components/parameters/registry', - '#/components/parameters/apiEntityHeader', - '#/components/parameters/apiUserHeader', - '#/components/parameters/apiSecretHeader' - ] - #swagger.responses[200] = { - description: 'Returns all users, along with pagination fields if results span multiple pages of data.', - content:{ - "application/json":{ - schema: { - $ref: '../schemas/registry-user/list-registry-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.useRegistry(), - mw.validateUser, - mw.onlySecretariat, - query().custom((query) => { return mw.validateQueryParameterNames(query, ['page']) }), - query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }), - query(['page']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), - parseError, - parseGetParams, - registryUserController.ALL_USERS -) - router.get('/users', /* #swagger.tags = ['Users'] @@ -102,7 +19,6 @@ router.get('/users',Secretariat: Retrieves information about all users for all organizations
" #swagger.parameters['$ref'] = [ '#/components/parameters/pageQuery', - '#/components/parameters/registry', '#/components/parameters/apiEntityHeader', '#/components/parameters/apiUserHeader', '#/components/parameters/apiSecretHeader' @@ -112,10 +28,7 @@ router.get('/users', content:{ "application/json":{ schema: { - oneOf: [ - { $ref: '../schemas/user/list-users-response.json' }, - { $ref: '../schemas/registry-user/list-registry-users-response.json' } - ] + $ref: '../schemas/user/list-users-response.json' } } } @@ -161,12 +74,11 @@ router.get('/users', } } */ - param(['registry']).optional().isBoolean(), - mw.handleRegistryParameter, mw.validateUser, mw.onlySecretariat, + query().custom((query) => { return mw.validateQueryParameterNames(query, ['page']) }), query(['page']).optional().isInt({ min: CONSTANTS.PAGINATOR_PAGE }), - query(['page', 'registry']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), + query(['page']).custom((val) => { return mw.containsNoInvalidCharacters(val) }), parseError, parseGetParams, controller.ALL_USERS) diff --git a/src/controller/user.controller/user.controller.js b/src/controller/user.controller/user.controller.js index 3d78e4a43..a5a8f1a4a 100644 --- a/src/controller/user.controller/user.controller.js +++ b/src/controller/user.controller/user.controller.js @@ -2,6 +2,7 @@ require('dotenv').config() const logger = require('../../middleware/logger') const getConstants = require('../../constants').getConstants +const { LEGACY_FORMAT, REGISTRY_FORMAT } = require('../format.constants') /** * Get the details of all users @@ -22,7 +23,7 @@ async function getAllUsers (req, res, next) { 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 returnValue = await repo.getAllUsers(options, !!req.useRegistry) + const returnValue = await repo.getAllUsers(options, req.useRegistry ? REGISTRY_FORMAT : LEGACY_FORMAT) logger.info({ uuid: req.ctx.uuid, message: 'The user information was sent to the secretariat user.' }) return res.status(200).json(returnValue) diff --git a/src/controller/user.controller/user.middleware.js b/src/controller/user.controller/user.middleware.js index e9477fb70..95a900313 100644 --- a/src/controller/user.controller/user.middleware.js +++ b/src/controller/user.controller/user.middleware.js @@ -4,7 +4,7 @@ const error = new errors.UserControllerError() const utils = require('../../utils/utils') function parseGetParams (req, res, next) { - utils.reqCtxMapping(req, 'query', ['page', 'registry']) + utils.reqCtxMapping(req, 'query', ['page']) next() } diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js index aa32a66b6..a02604c2e 100644 --- a/src/middleware/middleware.js +++ b/src/middleware/middleware.js @@ -96,11 +96,6 @@ async function optionallyValidateUser (req, res, next) { } } -const handleRegistryParameter = (req, res, next) => { - req.useRegistry = req.query.registry === 'true' - next() -} - const useRegistry = () => { return (req, res, next) => { req.useRegistry = true @@ -488,10 +483,6 @@ function isFlatStringArray (val) { return true } -function isCveProgramOrgMembershipObject (val) { - console.log(val) -} - /** * Recursively casts to strings and upper-cases all items in array * @@ -513,27 +504,6 @@ function toUpperCaseArray (val) { return newArr } -/** - * Recursively casts to strings and lower-cases all items in array - * - * @param {Array} val - */ -function toLowerCaseArray (val) { - if (!Array.isArray(val)) { - return val.toString().toLowerCase() - } - - const newArr = val.map(k => { - if (Array.isArray(k)) { - return toLowerCaseArray(k) - } else { - return k.toString().toLowerCase() - } - }) - - return newArr -} - // Check for the invalid characters <, >, and " function containsNoInvalidCharacters (val) { const invalidCharacterList = ['<', '>', '"'] @@ -547,32 +517,10 @@ function containsNoInvalidCharacters (val) { return true } -/** - * Middleware factory that rejects any keys in the request body - * that are not listed in the allowedKeys array. - * - * @param {Array