diff --git a/api/source/controllers/Collection.js b/api/source/controllers/Collection.js index 6d6ad4880..583c5dd21 100644 --- a/api/source/controllers/Collection.js +++ b/api/source/controllers/Collection.js @@ -627,7 +627,12 @@ module.exports.putAssetsByCollectionLabelId = async function (req, res, next) { const collectionId = getCollectionIdAndCheckPermission(req) const labelId = req.params.labelId const assetIds = req.body - let collection = await CollectionService.getCollection( collectionId, ['assets'], false, req.userObject) + let collection = await CollectionService.getCollection( collectionId, ['assets','labels'], false, req.userObject) + + if (!collection.labels.find( l => l.labelId === labelId)) { + throw new SmError.PrivilegeError('The labelId is not associated with this Collection.') + } + let collectionAssets = collection.assets.map( a => a.assetId) if (assetIds.every( a => collectionAssets.includes(a))) { await CollectionService.putAssetsByCollectionLabelId( collectionId, labelId, assetIds, res.svcStatus ) diff --git a/api/source/service/mysql/AssetService.js b/api/source/service/mysql/AssetService.js index 848e481a2..340385f00 100644 --- a/api/source/service/mysql/AssetService.js +++ b/api/source/service/mysql/AssetService.js @@ -439,8 +439,8 @@ exports.addOrUpdateAsset = async function ( {writeAction, assetId, body, project FROM collection_label WHERE - uuid IN (?)` - await connection.query(sqlInsertLabels, [assetId, uuidBinds]) + uuid IN (?) and collectionId = ?` + await connection.query(sqlInsertLabels, [assetId, uuidBinds, assetFields.collectionId]) } } diff --git a/test/api/postman_collection.json b/test/api/postman_collection.json index 6d49b430e..c0e84cc06 100644 --- a/test/api/postman_collection.json +++ b/test/api/postman_collection.json @@ -27778,7 +27778,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"name\": \"TEST_{{$randomNoun}}-{{$randomJobType}}\",\n \"collectionId\": \"{{scrapCollection}}\",\n \"description\": \"test desc\",\n \"ip\": \"1.1.1.1\",\n \"labelIds\": [\"{{testLabel}}\"],\n \"noncomputing\": true,\n \"metadata\": {\n \"pocName\": \"poc2Put\",\n \"pocEmail\": \"pocEmailPut@email.com\",\n \"pocPhone\": \"12342\",\n \"reqRar\": \"true\"\n },\n \"stigs\": [\n \"VPN_SRG_TEST\",\n \"Windows_10_STIG_TEST\"\n ]\n}" + "raw": "{\n \"name\": \"TEST_{{$randomNoun}}-{{$randomJobType}}\",\n \"collectionId\": \"{{scrapCollection}}\",\n \"description\": \"test desc\",\n \"ip\": \"1.1.1.1\",\n \"labelIds\": [\"{{scrapLabel}}\"],\n \"noncomputing\": true,\n \"metadata\": {\n \"pocName\": \"poc2Put\",\n \"pocEmail\": \"pocEmailPut@email.com\",\n \"pocPhone\": \"12342\",\n \"reqRar\": \"true\"\n },\n \"stigs\": [\n \"VPN_SRG_TEST\",\n \"Windows_10_STIG_TEST\"\n ]\n}" }, "url": { "raw": "{{baseUrl}}/assets?projection=stigs", @@ -63025,6 +63025,560 @@ "response": [] } ] + }, + { + "name": "valid label checks", + "item": [ + { + "name": "Import and overwrite application data (as elevated Admin) Copy", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let user = pm.environment.get(\"user\");\r", + "console.log(\"user: \" + user);\r", + "\r", + "if (pm.request.url.getQueryString().match(/elevate=true/)) {\r", + " user = \"elevated\";\r", + " console.log(\"setting user to 'elevated'\");\r", + "}\r", + "\r", + "if (user == \"elevated\") { //placeholder for \"users\" that should fail\r", + " pm.test(\"Status should be is 200 for elevated stigmanadmin user\", function () {\r", + " pm.response.to.have.status(200);\r", + " });\r", + "}\r", + "else {\r", + " pm.test(\"Status code is 403\", function () {\r", + " pm.response.to.have.status(403);\r", + " });\r", + "}\r", + "if (pm.response.code !== 200) {\r", + " return;\r", + "}\r", + "\r", + "\r", + "let response = pm.response.text();\r", + "console.log(response)\r", + "\r", + "pm.test(\"Body contains string\",() => {\r", + " pm.expect(response).to.include(\"Commit successful\");\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "oauth2", + "oauth2": [ + { + "key": "accessToken", + "value": "{{token.stigmanadmin}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "importFile", + "type": "file", + "src": "./{{formDataFiles}}/{{appDataFile}}" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/op/appdata?elevate=true", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "op", + "appdata" + ], + "query": [ + { + "key": "elevate", + "value": "true", + "description": "Elevate the user context for this request if user is permitted (canAdmin)" + } + ] + } + }, + "response": [] + }, + { + "name": "Merge provided properties with an Asset Copy", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let user = pm.environment.get(\"user\");\r", + "console.log(\"user: \" + user);\r", + "\r", + "if (pm.request.url.getQueryString().match(/elevate=true/)) {\r", + " user = \"elevated\";\r", + " console.log(\"setting user to 'elevated'\");\r", + "}\r", + "\r", + "if (user == \"lvl1\" || user == \"lvl2\" || user == \"collectioncreator\" || user == \"globular\") { //placeholder for \"users\" that should fail\r", + " pm.test(\"Status should be is 403 for all users except stigmanAdmin(elevated), lvl3 and lvl4\", function () {\r", + " pm.response.to.have.status(403);\r", + " });\r", + " return;\r", + "}\r", + "else {\r", + " pm.test(\"Status code is 200\", function () {\r", + " pm.response.to.have.status(200);\r", + " });\r", + "}\r", + "if (pm.response.code !== 200) {\r", + " return;\r", + "}\r", + "\r", + "\r", + "let jsonData = pm.response.json();\r", + "\r", + "\r", + "pm.test(\"Response JSON is an object\", function () {\r", + " pm.expect(jsonData).to.be.an('object');\r", + " // pm.expect(jsonData).to.have.lengthOf.at.least(1);\r", + " // pm.expect(jsonData).to.have.lengthOf(1);\r", + "\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token.stigmanadmin}}", + "type": "string" + } + ] + }, + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"collectionId\": \"{{scrapCollection}}\",\n \"description\": \"test desc\",\n \"ip\": \"1.1.1.1\",\n \"noncomputing\": true,\n \"labelIds\": [\n \"{{testLabel}}\"\n ], \n \"metadata\": {\n \"pocName\": \"poc2Put\",\n \"pocEmail\": \"pocEmailPut@email.com\",\n \"pocPhone\": \"12342\",\n \"reqRar\": \"true\"\n },\n \"stigs\": [\n \"VPN_SRG_TEST\",\n \"Windows_10_STIG_TEST\",\n \"RHEL_7_STIG_TEST\"\n ]\n}" + }, + "url": { + "raw": "{{baseUrl}}/assets/:assetId?projection=statusStats&projection=stigs&projection=stigGrants", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "assets", + ":assetId" + ], + "query": [ + { + "key": "elevate", + "value": "{{elevated}}", + "description": "Elevate the user context for this request if user is permitted (canAdmin)", + "disabled": true + }, + { + "key": "projection", + "value": "statusStats", + "description": "Additional properties to include in the response.\n" + }, + { + "key": "projection", + "value": "stigs", + "description": "Additional properties to include in the response.\n" + }, + { + "key": "projection", + "value": "stigGrants" + } + ], + "variable": [ + { + "key": "assetId", + "value": "{{scrapAsset}}", + "description": "(Required) A path parameter that indentifies an Asset" + } + ] + } + }, + "response": [] + }, + { + "name": "Replace a Labels Asset Mappings in a Collection Copy", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let user = pm.environment.get(\"user\");\r", + "console.log(\"user: \" + user);\r", + "\r", + "// if (pm.request.url.getQueryString().match(/elevate=true/)) {\r", + "// user = \"elevated\";\r", + "// console.log(\"setting user to 'elevated'\");\r", + "// }\r", + "\r", + "// if ( user == \"collectioncreator\" || user == \"lvl1\" || user ==\"lvl2\" ) { //placeholder for \"users\" that should fail\r", + "// pm.test(\"Status should be 403 for collectioncreator\", function () {\r", + "// pm.response.to.have.status(403);\r", + "// });\r", + "// return;\r", + "// }\r", + "// else {\r", + "// pm.test(\"Status code is 200 for all users but collectioncreator, lvl1, or lvl2. user=\" + user, function () {\r", + "// pm.response.to.have.status(200);\r", + "// });\r", + "// }\r", + "\r", + " pm.test(\"Status code is 403 for all users\", function () {\r", + " pm.response.to.have.status(403);\r", + " });\r", + "\r", + "\r", + "\r", + "\r", + "\r", + "\r", + "\r", + "\r", + "\r", + "\r", + "\r", + "\r", + "\r", + "\r", + "\r", + "\r", + "\r", + "\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token.stigmanadmin}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "[\"{{testAsset}}\"]" + }, + "url": { + "raw": "{{baseUrl}}/collections/:collectionId/labels/:labelId/assets", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "collections", + ":collectionId", + "labels", + ":labelId", + "assets" + ], + "query": [ + { + "key": "projection", + "value": "rule", + "description": "Additional properties to include in the response.\n", + "disabled": true + }, + { + "key": "projection", + "value": "history", + "description": "Additional properties to include in the response.\n", + "disabled": true + }, + { + "key": "projection", + "value": "stigs", + "disabled": true + }, + { + "key": "projection", + "value": "metadata", + "disabled": true + } + ], + "variable": [ + { + "key": "collectionId", + "value": "{{testCollection}}", + "description": "(Required) A path parameter that indentifies a Collection" + }, + { + "key": "labelId", + "value": "{{scrapLabel}}" + } + ] + }, + "description": "Create a new Review, or update all properties of an existing Review, setting missing properties to null" + }, + "response": [] + }, + { + "name": "Create an Asset Copy", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let user = pm.environment.get(\"user\");\r", + "console.log(\"user: \" + user);\r", + "\r", + "if (pm.request.url.getQueryString().match(/elevate=true/)) {\r", + " user = \"elevated\";\r", + " console.log(\"setting user to 'elevated'\");\r", + "}\r", + "\r", + "if (user == \"lvl1\" || user == \"lvl2\" || user == \"collectioncreator\" || user == \"globular\") { //placeholder for \"users\" that should fail\r", + " pm.test(\"Status should be is 403 for all users except stigmanAdmin(elevated), lvl3 and lvl4\", function () {\r", + " pm.response.to.have.status(403);\r", + " });\r", + " return;\r", + "}\r", + "else {\r", + " pm.test(\"Status code is 201\", function () {\r", + " pm.response.to.have.status(201);\r", + " });\r", + "}\r", + "if (pm.response.code !== 201) {\r", + " return;\r", + "}\r", + "\r", + "let respJson = pm.response.json();\r", + "let reqJson = JSON.parse(pm.request.body.raw)\r", + "reqJson.labelIds = []\r", + "\r", + "pm.test(\"Response matches request\", function () {\r", + " pm.expect(assetGetToPost(respJson))\r", + " .to.eql(reqJson)\r", + "})\r", + "\r", + "function assetGetToPost(assetGet) {\r", + " // extract the transformed and unposted properties\r", + " const {assetId, collection, stigs, mac, fqdn, ...assetPost} = assetGet\r", + " \r", + " // add transformed properties to the derived post \r", + " assetPost.collectionId = collection.collectionId\r", + " assetPost.stigs = stigsGetToPost(stigs)\r", + "\r", + " // the derived post object\r", + " return assetPost\r", + "}\r", + "\r", + "function stigsGetToPost(stigsGetArray) {\r", + " const stigsPostArray = []\r", + " for (const stig of stigsGetArray) {\r", + " stigsPostArray.push(stig.benchmarkId)\r", + " }\r", + " return stigsPostArray\r", + "}\r", + "\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token.stigmanadmin}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"TEST_x{{$randomNoun}}-{{$randomJobType}}\",\n \"collectionId\": \"{{scrapCollection}}\",\n \"description\": \"test desc\",\n \"ip\": \"1.1.1.1\",\n \"labelIds\": [\n \"8fd5f19e-9b5e-11ec-adb1-0242c0a86004\",\n \"1630332d-f4d5-4634-9d67-314d774050de\"\n ],\n \"noncomputing\": true,\n \"metadata\": {\n \"pocName\": \"poc2Put\",\n \"pocEmail\": \"pocEmailPut@email.com\",\n \"pocPhone\": \"12342\",\n \"reqRar\": \"true\"\n },\n \"stigs\": [\n \"VPN_SRG_TEST\",\n \"Windows_10_STIG_TEST\"\n ]\n}" + }, + "url": { + "raw": "{{baseUrl}}/assets?projection=stigs", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "assets" + ], + "query": [ + { + "key": "elevate", + "value": "{{elevated}}", + "description": "Elevate the user context for this request if user is permitted (canAdmin)", + "disabled": true + }, + { + "key": "projection", + "value": "stigs", + "description": "Additional properties to include in the response.\n" + } + ] + } + }, + "response": [] + }, + { + "name": "Set all properties of an Asset Copy", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let user = pm.environment.get(\"user\");\r", + "console.log(\"user: \" + user);\r", + "\r", + "if (pm.request.url.getQueryString().match(/elevate=true/)) {\r", + " user = \"elevated\";\r", + " console.log(\"setting user to 'elevated'\");\r", + "}\r", + "\r", + "if (user == \"lvl1\" || user == \"lvl2\" || user == \"collectioncreator\" || user == \"globular\") { //placeholder for \"users\" that should fail\r", + " pm.test(\"Status should be is 403 for all users except stigmanAdmin(elevated), lvl3 and lvl4\", function () {\r", + " pm.response.to.have.status(403);\r", + " });\r", + " return;\r", + "}\r", + "else {\r", + " pm.test(\"Status code is 200\", function () {\r", + " pm.response.to.have.status(200);\r", + " });\r", + "}\r", + "if (pm.response.code !== 200) {\r", + " return;\r", + "}\r", + "\r", + "\r", + "let jsonData = pm.response.json();\r", + "\r", + "\r", + "pm.test(\"Response JSON is an object\", function () {\r", + " pm.expect(jsonData).to.be.an('object');\r", + " // pm.expect(jsonData).to.have.lengthOf.at.least(1);\r", + " // pm.expect(jsonData).to.have.lengthOf(1);\r", + "\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token.stigmanadmin}}", + "type": "string" + } + ] + }, + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"TEST_{{$randomNoun}}-{{$randomJobType}}\",\n \"collectionId\": \"{{scrapCollection}}\",\n \"description\": \"test desc\",\n \"ip\": \"1.1.1.1\",\n \"noncomputing\": true,\n \"labelIds\": [\n \"8fd5f19e-9b5e-11ec-adb1-0242c0a86004\",\n \"1630332d-f4d5-4634-9d67-314d774050de\"\n ],\n \"metadata\": {\n \"pocName\": \"poc2Put\",\n \"pocEmail\": \"pocEmailPut@email.com\",\n \"pocPhone\": \"12342\",\n \"reqRar\": \"true\"\n },\n \"stigs\": [\n \"VPN_SRG_TEST\",\n \"Windows_10_STIG_TEST\",\n \"RHEL_7_STIG_TEST\"\n ]\n}" + }, + "url": { + "raw": "{{baseUrl}}/assets/:assetId?projection=statusStats&projection=stigs&projection=stigGrants", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "assets", + ":assetId" + ], + "query": [ + { + "key": "elevate", + "value": "{{elevated}}", + "description": "Elevate the user context for this request if user is permitted (canAdmin)", + "disabled": true + }, + { + "key": "projection", + "value": "statusStats", + "description": "Additional properties to include in the response.\n" + }, + { + "key": "projection", + "value": "stigs", + "description": "Additional properties to include in the response.\n" + }, + { + "key": "projection", + "value": "stigGrants" + } + ], + "variable": [ + { + "key": "assetId", + "value": "{{scrapAsset}}", + "description": "(Required) A path parameter that indentifies an Asset" + } + ] + } + }, + "response": [] + } + ] } ], "description": "These tests should be self contained, provide their own authorization, and repopulate test data if required.",