diff --git a/.secrets.baseline b/.secrets.baseline index ffa18c6e..857d311a 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "^.secrets.baseline$|^.*pgsslrootcert.yml$", "lines": null }, - "generated_at": "2019-12-18T15:29:41Z", + "generated_at": "2019-12-18T22:26:52Z", "plugins_used": [ { "base64_limit": 4.5, @@ -98,7 +98,7 @@ "hashed_secret": "afc848c316af1a89d49826c5ae9d00ed769415f3", "is_secret": false, "is_verified": false, - "line_number": 29, + "line_number": 30, "type": "Secret Keyword" } ], diff --git a/Pipfile b/Pipfile index edc8bbfc..ed87d000 100644 --- a/Pipfile +++ b/Pipfile @@ -18,7 +18,6 @@ flask-session = "*" flask-wtf = "*" pyopenssl = "*" requests = "*" -apache-libcloud = "*" lockfile = "*" werkzeug = "*" PyYAML = "*" @@ -30,6 +29,7 @@ azure-graphrbac = "*" msrestazure = "*" azure-mgmt-authorization = "*" azure-mgmt-managementgroups = "*" +azure-mgmt-resource = "*" [dev-packages] bandit = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 80f6fc96..d30bcbb2 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "c2b19c436646705ea3bf4df8c35c2833083f048da37fc619e66f7236153607c5" + "sha256": "c203c47b00f413fd40056ef6d2d8e51b37ad3ff5f7693db5eb170b7f8fd43234" }, "pipfile-spec": 6, "requires": { @@ -25,10 +25,10 @@ }, "alembic": { "hashes": [ - "sha256:49277bb7242192bbb9eac58fed4fe02ec6c3a2a4b4345d2171197459266482b2" + "sha256:3b0cb1948833e062f4048992fbc97ecfaaaac24aaa0d83a1202a99fb58af8c6d" ], "index": "pypi", - "version": "==1.3.1" + "version": "==1.3.2" }, "amqp": { "hashes": [ @@ -37,14 +37,6 @@ ], "version": "==2.5.2" }, - "apache-libcloud": { - "hashes": [ - "sha256:9bc5cd5c32151bb7a04a7c7de0be9b4a4b8271e348ac91dd79eaaeeae627115f", - "sha256:fcc165f2cc2db9a379c6d3a17b3beb9081bb64ba5c0bf7bbb58da864810092f0" - ], - "index": "pypi", - "version": "==2.6.1" - }, "azure-common": { "hashes": [ "sha256:53b1195b8f20943ccc0e71a17849258f7781bc6db1c72edc7d6c055f79bd54e3", @@ -76,6 +68,14 @@ "index": "pypi", "version": "==0.2.0" }, + "azure-mgmt-resource": { + "hashes": [ + "sha256:20b3394e4dc76fbd9459723cb8c0300fb18a8c32100076f023b5470426b9f104", + "sha256:eaea8b5d05495d1b74220052275d46b6bed93b59245bcaa747279a52e41c3bdf" + ], + "index": "pypi", + "version": "==7.0.0" + }, "azure-mgmt-subscription": { "hashes": [ "sha256:504b4c42ba859070c3c50637ec07ca36aca600e613fcccaa398db22822fe21f1", @@ -117,11 +117,11 @@ }, "celery": { "hashes": [ - "sha256:65f4d67fc1037edacecbf39fcf956da68b984cf2a6d89bd73a8a5a80e35e3dd7", - "sha256:8a59d80235b876881d9893751f2a87936165fc1347efee66095620b3cadf533b" + "sha256:7c544f37a84a5eadc44cab1aa8c9580dff94636bb81978cdf9bf8012d9ea7d8f", + "sha256:d3363bb5df72d74420986a435449f3c3979285941dff57d5d97ecba352a0e3e2" ], "index": "pypi", - "version": "==4.4.0rc4" + "version": "==4.4.0" }, "certifi": { "hashes": [ @@ -256,10 +256,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:b044f07694ef14a6683b097ba56bd081dbc7cdc7c7fe46011e499dfecc082f21", - "sha256:e6ac600a142cf2db707b1998382cc7fc3b02befb7273876e01b8ad10b9652742" + "sha256:073a852570f92da5f744a3472af1b61e28e9f78ccf0c9117658dc32b15de7b45", + "sha256:d95141fbfa7ef2ec65cfd945e2af7e5a6ddbd7c8d9a25e66ff3be8e3daf9f60f" ], - "version": "==1.1.0" + "markers": "python_version < '3.8'", + "version": "==1.3.0" }, "isodate": { "hashes": [ @@ -284,10 +285,10 @@ }, "kombu": { "hashes": [ - "sha256:1760b54b1d15a547c9a26d3598a1c8cdaf2436386ac1f5561934bc8a3cbbbd86", - "sha256:e7465aa85a1db889116819f08c5de29520d2fa103324dcdca5e90af345f01771" + "sha256:2a9e7adff14d046c9996752b2c48b6d9185d0b992106d5160e1a179907a5d4ac", + "sha256:67b32ccb6fea030f8799f8fd50dd08e03a4b99464ebc4952d71d8747b1a52ad1" ], - "version": "==4.6.6" + "version": "==4.6.7" }, "lockfile": { "hashes": [ @@ -338,10 +339,10 @@ }, "more-itertools": { "hashes": [ - "sha256:53ff73f186307d9c8ef17a9600309154a6ae27f25579e80af4db8f047ba14bc2", - "sha256:a0ea684c39bc4315ba7aae406596ef191fd84f873d2d2751f84d64e81a7a2d45" + "sha256:b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d", + "sha256:c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564" ], - "version": "==8.0.0" + "version": "==8.0.2" }, "msrest": { "hashes": [ @@ -514,10 +515,10 @@ }, "sqlalchemy": { "hashes": [ - "sha256:afa5541e9dea8ad0014251bc9d56171ca3d8b130c9627c6cb3681cff30be3f8a" + "sha256:bfb8f464a5000b567ac1d350b9090cf081180ec1ab4aa87e7bca12dab25320ec" ], "index": "pypi", - "version": "==1.3.11" + "version": "==1.3.12" }, "unipath": { "hashes": [ @@ -677,44 +678,46 @@ }, "colorama": { "hashes": [ - "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", - "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48" + "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", + "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1" ], - "version": "==0.4.1" + "version": "==0.4.3" }, "coverage": { "hashes": [ - "sha256:2358e685d0253125da42a48396038d4c7b4cd1448c00bbc4bda0cb8c43c2a870", - "sha256:25017cf384eeed2e6caf72efd3ec4124e32a8b7a4387600499104387975400c7", - "sha256:2e2de9423ff8b14303a97eafddd16c479fbcc9a0b8b0be3b7c3843a3e0cf6d69", - "sha256:324ed908e4e40a6e2451056fe502470ad4e79495cb7a03ecab94e6309c3e117e", - "sha256:34f865a0cf6255b694a46e4383a7131c61ea72c5b4c4f81d20e522fb1e440b4b", - "sha256:3a2bcc464b60a18f1f7167b95b2773ede93bf3722bfa59e0802717f652b6cc25", - "sha256:48d70865266d649b6602e2ba94820d7972ef470d3b72a8fd41a3d17321feed3a", - "sha256:50cf23523ab3a724c6905d3b60f87fa8250d9bae3995e09f49f63effa2b54f15", - "sha256:54c84a68abd8c4c5b71878b35eb85321df41f3d144c78181867d5b026ec74994", - "sha256:5b59d661ee7f3200aedd7b71882b7927ea7ed522df75e3853f316a79ad872a2e", - "sha256:5ffb39624bc573177888a21fb301ccee46838c600b27d58c3e9dae495f44d34a", - "sha256:699b3072b7f0e69ed175a88fa8b2ec7eefc4f34d490c54ed9a52feff21a15fdc", - "sha256:79ef4a2bb862110bd585174e551a783bee5c3aa461734a2ac7429193be357589", - "sha256:8210a6f93c4a8c6d460b402e20e38399529b99200c3318542faf6a520c9b6a5c", - "sha256:8d30c10cfd0a6fdf0a2d5023de00ef7b329cd6ead2310c9e53eab79c209acb70", - "sha256:97ac79ff28f2cda6ac00a803ee582b965951755f61ab43377482bfba450b619a", - "sha256:9fe4aacacff9028ed167db108bf013510654f148d83c4857fed61d2ce0588bf2", - "sha256:a5b6395d5957d638f8b1870561607e3c39b1a236ea6cff9eafe5b9bb1db913f2", - "sha256:ab32c5fad6905986a7e34e3acf01180a69bb60c2aa7331815b46e51c776a1943", - "sha256:ad67f0cfdfecbd49b9da46a7e488e6dc32a69388740b85c36a4ef4b33082cbad", - "sha256:aedad67c30326a1af324f45833a40b97180664912deb29942459ddbe9fa0ce19", - "sha256:b077cd0e70f41366ac1f9d09275258fa1906758a5d4f31cacc18b10dfcf90784", - "sha256:b8ea210810d3c14aec7561f8fe0d3eec582d1088100aaa0bb8153d53d867d20f", - "sha256:bf572722326ce6704e863447a070039a827072b7179352570859be899b9e6551", - "sha256:c0df57e189dacd2606cae6386acf127d01d85b2bf49acd9a65543b5d6c359ddc", - "sha256:d523e75f2a8a0b4a6a8be1287c0e0e3a561b8832b05ddd987d4cd7c62f3ad3bc", - "sha256:e10593c60c5f0bfd8b241bf9f27ef2191a3005b73dde8ada0424f642543a1e59", - "sha256:e9128444c83bc260aea988bf1ca6278a33ba730955bf94720468c656b61353eb", - "sha256:f7162f2e3711f3a08a8a741f92e1f63afd58d0713177979f2cf9723dd50161cf" + "sha256:0cd13a6e98c37b510a2d34c8281d5e1a226aaf9b65b7d770ef03c63169965351", + "sha256:1a4b6b6a2a3a6612e6361130c2cc3dc4378d8c221752b96167ccbad94b47f3cd", + "sha256:2ee55e6dba516ddf6f484aa83ccabbb0adf45a18892204c23486938d12258cde", + "sha256:3be5338a2eb4ef03c57f20917e1d12a1fd10e3853fed060b6d6b677cb3745898", + "sha256:44b783b02db03c4777d8cf71bae19eadc171a6f2a96777d916b2c30a1eb3d070", + "sha256:475bf7c4252af0a56e1abba9606f1e54127cdf122063095c75ab04f6f99cf45e", + "sha256:47c81ee687eafc2f1db7f03fbe99aab81330565ebc62fb3b61edfc2216a550c8", + "sha256:4a7f8e72b18f2aca288ff02255ce32cc830bc04d993efbc87abf6beddc9e56c0", + "sha256:50197163a22fd17f79086e087a787883b3ec9280a509807daf158dfc2a7ded02", + "sha256:56b13000acf891f700f5067512b804d1ec8c301d627486c678b903859d07f798", + "sha256:79388ae29c896299b3567965dbcd93255f175c17c6c7bca38614d12718c47466", + "sha256:79fd5d3d62238c4f583b75d48d53cdae759fe04d4fb18fe8b371d88ad2b6f8be", + "sha256:7fe3e2fde2bf1d7ce25ebcd2d3de3650b8d60d9a73ce6dcef36e20191291613d", + "sha256:81042a24f67b96e4287774014fa27220d8a4d91af1043389e4d73892efc89ac6", + "sha256:81326f1095c53111f8afc95da281e1414185f4a538609a77ca50bdfa39a6c207", + "sha256:8873dc0d8f42142ea9f20c27bbdc485190fff93823c6795be661703369e5877d", + "sha256:88d2cbcb0a112f47eef71eb95460b6995da18e6f8ca50c264585abc2c473154b", + "sha256:91f2491aeab9599956c45a77c5666d323efdec790bfe23fcceafcd91105d585a", + "sha256:979daa8655ae5a51e8e7a24e7d34e250ae8309fd9719490df92cbb2fe2b0422b", + "sha256:9c871b006c878a890c6e44a5b2f3c6291335324b298c904dc0402ee92ee1f0be", + "sha256:a6d092545e5af53e960465f652e00efbf5357adad177b2630d63978d85e46a72", + "sha256:b5ed7837b923d1d71c4f587ae1539ccd96bfd6be9788f507dbe94dab5febbb5d", + "sha256:ba259f68250f16d2444cbbfaddaa0bb20e1560a4fdaad50bece25c199e6af864", + "sha256:be1d89614c6b6c36d7578496dc8625123bda2ff44f224cf8b1c45b810ee7383f", + "sha256:c1b030a79749aa8d1f1486885040114ee56933b15ccfc90049ba266e4aa2139f", + "sha256:c95bb147fab76f2ecde332d972d8f4138b8f2daee6c466af4ff3b4f29bd4c19e", + "sha256:d52c1c2d7e856cecc05aa0526453cb14574f821b7f413cc279b9514750d795c1", + "sha256:d609a6d564ad3d327e9509846c2c47f170456344521462b469e5cb39e48ba31c", + "sha256:e1bad043c12fb58e8c7d92b3d7f2f49977dcb80a08a6d1e7a5114a11bf819fca", + "sha256:e5a675f6829c53c87d79117a8eb656cc4a5f8918185a32fc93ba09778e90f6db", + "sha256:fec32646b98baf4a22fdceb08703965bd16dea09051fbeb31a04b5b6e72b846c" ], - "version": "==5.0b1" + "version": "==5.0" }, "decorator": { "hashes": [ @@ -747,10 +750,10 @@ }, "faker": { "hashes": [ - "sha256:48c03580720e0b46538d528b1296e4e5b24a809dcaf33a7dddec719489a9edb8", - "sha256:6327c665c0d8721280b3036d9c9e851c60092bc1f30c8394cc433f8723e2bda5" + "sha256:202ad3b2ec16ae7c51c02904fb838831f8d2899e61bf18db1e91a5a582feab11", + "sha256:92c84a10bec81217d9cb554ee12b3838c8986ce0b5d45f72f769da22e4bb5432" ], - "version": "==2.0.4" + "version": "==3.0.0" }, "flask": { "hashes": [ @@ -791,25 +794,26 @@ }, "importlib-metadata": { "hashes": [ - "sha256:b044f07694ef14a6683b097ba56bd081dbc7cdc7c7fe46011e499dfecc082f21", - "sha256:e6ac600a142cf2db707b1998382cc7fc3b02befb7273876e01b8ad10b9652742" + "sha256:073a852570f92da5f744a3472af1b61e28e9f78ccf0c9117658dc32b15de7b45", + "sha256:d95141fbfa7ef2ec65cfd945e2af7e5a6ddbd7c8d9a25e66ff3be8e3daf9f60f" ], - "version": "==1.1.0" + "markers": "python_version < '3.8'", + "version": "==1.3.0" }, "ipdb": { "hashes": [ - "sha256:473fdd798a099765f093231a8b1fabfa95b0b682fce12de0c74b61a4b4d8ee57" + "sha256:5d9a4a0e3b7027a158fc6f2929934341045b9c3b0b86ed5d7e84e409653f72fd" ], "index": "pypi", - "version": "==0.12.2" + "version": "==0.12.3" }, "ipython": { "hashes": [ - "sha256:c66c7e27239855828a764b1e8fc72c24a6f4498a2637572094a78c5551fb9d51", - "sha256:f186b01b36609e0c5d0de27c7ef8e80c990c70478f8c880863004b3489a9030e" + "sha256:190a279bd3d4fc585a611e9358a88f1048cc57fd688254a86f9461889ee152a6", + "sha256:762d79a62b6aa96b04971e920543f558dfbeedc0468b899303c080c8068d4ac2" ], "index": "pypi", - "version": "==7.10.1" + "version": "==7.10.2" }, "ipython-genutils": { "hashes": [ @@ -914,30 +918,29 @@ }, "more-itertools": { "hashes": [ - "sha256:53ff73f186307d9c8ef17a9600309154a6ae27f25579e80af4db8f047ba14bc2", - "sha256:a0ea684c39bc4315ba7aae406596ef191fd84f873d2d2751f84d64e81a7a2d45" + "sha256:b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d", + "sha256:c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564" ], - "version": "==8.0.0" + "version": "==8.0.2" }, "mypy": { "hashes": [ - "sha256:02d9bdd3398b636723ecb6c5cfe9773025a9ab7f34612c1cde5c7f2292e2d768", - "sha256:088f758a50af31cf8b42688118077292370c90c89232c783ba7979f39ea16646", - "sha256:28e9fbc96d13397a7ddb7fad7b14f373f91b5cff538e0772e77c270468df083c", - "sha256:30e123b24931f02c5d99307406658ac8f9cd6746f0d45a3dcac2fe5fbdd60939", - "sha256:3294821b5840d51a3cd7a2bb63b40fc3f901f6a3cfb3c6046570749c4c7ef279", - "sha256:41696a7d912ce16fdc7c141d87e8db5144d4be664a0c699a2b417d393994b0c2", - "sha256:4f42675fa278f3913340bb8c3371d191319704437758d7c4a8440346c293ecb2", - "sha256:54d205ccce6ed930a8a2ccf48404896d456e8b87812e491cb907a355b1a9c640", - "sha256:6992133c95a2847d309b4b0c899d7054adc60481df6f6b52bb7dee3d5fd157f7", - "sha256:6ecbd0e8e371333027abca0922b0c2c632a5b4739a0c61ffbd0733391e39144c", - "sha256:83fa87f556e60782c0fc3df1b37b7b4a840314ba1ac27f3e1a1e10cb37c89c17", - "sha256:c87ac7233c629f305602f563db07f5221950fe34fe30af072ac838fa85395f78", - "sha256:de9ec8dba773b78c49e7bec9a35c9b6fc5235682ad1fc2105752ae7c22f4b931", - "sha256:f385a0accf353ca1bca4bbf473b9d83ed18d923fdb809d3a70a385da23e25b6a" + "sha256:0308c35fd16c96a81b8dfc4d09ec63b8fa607cfec087acf5aafb44c2c45197de", + "sha256:39f7be2f89668d21b2bbab45ce5aa15e69bf8d6f3b46f9e1cc1a88e4fcc84f3d", + "sha256:4223f576813c79a10d0fd14192c86f1b85e3bd235c93792f22ed811a20b5ee4e", + "sha256:4c8f812a2fbefa96185933fbe05aa035e9cf791cf3a23bbdb6a219c80b60e0b1", + "sha256:4ea9ee847ea5bb38ea275441f3aea7eeba1b96187a3f968ee359d33d9dcc0eda", + "sha256:573c68df69f0e399fa57866a0b72989acf0a56c4008eee59c789c2ca5ea9df03", + "sha256:588c0e38466306aa7dbe6522ceacf37dde8b13cfa5edde90be2ce382f078875f", + "sha256:6d1bd2e675823a19e6bf72149540ab9851bfe698b796aea698fb926ab2bedd02", + "sha256:aa8e3bd1540dd5c39ef580ec2146a9c99c45f7c62af890095fec9e87b5ca19fb", + "sha256:b978ba1ea90d0abe2fc720ec9a41824b7d3a1304569bd58c9038d8d61dc4dfdb", + "sha256:c85c5367c2e8247e06cc0aba84e3633e90f48e8a0677bc51b351e138b5ff80b1", + "sha256:ce69577b424058bfa177df27213869f37c1e964c3e1ebd3b3d54f1d10b234c4d", + "sha256:ec6eaf98a57624d96d9916352a5bad2d73959f6358fabf43838f7d1a4d2f8389" ], "index": "pypi", - "version": "==0.750" + "version": "==0.760" }, "mypy-extensions": { "hashes": [ @@ -948,10 +951,10 @@ }, "parso": { "hashes": [ - "sha256:63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", - "sha256:666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c" + "sha256:55cf25df1a35fd88b878715874d2c4dc1ad3f0eebd1e0266a67e1f55efccfbe1", + "sha256:5c1f7791de6bd5dbbeac8db0ef5594b36799de198b3f7f7014643b0c5536b9d3" ], - "version": "==0.5.1" + "version": "==0.5.2" }, "pathspec": { "hashes": [ @@ -1063,11 +1066,11 @@ }, "pytest-mock": { "hashes": [ - "sha256:96a0cebc66e09930be2a15b03333d90b59584d3fb011924f81c14b50ee0afbba", - "sha256:e5381be2608e49547f5e47633c5f81241ebf6206d17ce516a7a18d5a917e3859" + "sha256:67e414b3caef7bff6fc6bd83b22b5bc39147e4493f483c2679bc9d4dc485a94d", + "sha256:e24a911ec96773022ebcc7030059b57cd3480b56d4f5d19b7c370ec635e6aed5" ], "index": "pypi", - "version": "==1.12.1" + "version": "==1.13.0" }, "pytest-watch": { "hashes": [ @@ -1102,21 +1105,29 @@ }, "regex": { "hashes": [ - "sha256:15454b37c5a278f46f7aa2d9339bda450c300617ca2fca6558d05d870245edc7", - "sha256:1ad40708c255943a227e778b022c6497c129ad614bb7a2a2f916e12e8a359ee7", - "sha256:5e00f65cc507d13ab4dfa92c1232d004fa202c1d43a32a13940ab8a5afe2fb96", - "sha256:604dc563a02a74d70ae1f55208ddc9bfb6d9f470f6d1a5054c4bd5ae58744ab1", - "sha256:720e34a539a76a1fedcebe4397290604cc2bdf6f81eca44adb9fb2ea071c0c69", - "sha256:7caf47e4a9ac6ef08cabd3442cc4ca3386db141fb3c8b2a7e202d0470028e910", - "sha256:7faf534c1841c09d8fefa60ccde7b9903c9b528853ecf41628689793290ca143", - "sha256:b4e0406d822aa4993ac45072a584d57aa4931cf8288b5455bbf30c1d59dbad59", - "sha256:c31eaf28c6fe75ea329add0022efeed249e37861c19681960f99bbc7db981fb2", - "sha256:c7393597191fc2043c744db021643549061e12abe0b3ff5c429d806de7b93b66", - "sha256:d2b302f8cdd82c8f48e9de749d1d17f85ce9a0f082880b9a4859f66b07037dc6", - "sha256:e3d8dd0ec0ea280cf89026b0898971f5750a7bd92cb62c51af5a52abd020054a", - "sha256:ec032cbfed59bd5a4b8eab943c310acfaaa81394e14f44454ad5c9eba4f24a74" + "sha256:0472acc4b6319801c1bc681d838c88ba1446f9ae199e01f6e41091c701fb3d42", + "sha256:16709434c4e2332ee8ba26ae339aceb8ab0b24b8398ebd0f52ebc943f45c4fc2", + "sha256:223fb63ec8dcab20b3318e93dcec4aee89e98b062934090bf29ffc374d2000a2", + "sha256:23c3ebf05d1cd3adb26723fd598e75724e0cdb7d6a35185ac0caf061cc6edb49", + "sha256:2404a50fb48badaf214b700f08822b68d93d79200e0aefd9569d0332d21fbfcb", + "sha256:2af3a7a16fed6eff85c25da106effa36f61cbbe801d00ade349b53ce7619eb15", + "sha256:37e018d3746baf159aedfc9773c3cafacbd10d354ba15484f5cfc8ed9da5748b", + "sha256:3c9c2988d02a9238a1975c70e87c6ce94e6f36dd8e372b66f468990cfe077434", + "sha256:47298bc8b89d1c747f0f5974aa528fc0b6b17396f1694136a224d51461279d83", + "sha256:4eeb0fe936797ae00a085f99802642bfc722b3b4ea557e9e7849cb621ea10c91", + "sha256:6881be0218b47ed76db033f252bab3f912dfe7ed1fe7baa9daebf51de08546a0", + "sha256:7ac08cee5055f548eed3889e9aaef15fd00172d037949496f1f0b34acb8a7c3e", + "sha256:7c5e2efcf079c35ff266c3f3a6708834f88f9fd04a3c16b855e036b2b7b1b543", + "sha256:8355eaa64724a0fdb010a1654b77cb3e375dc08b7f592cc4a1c05ac606aa481c", + "sha256:999a885f7f5194464238ad5d74b05982acee54002f3aa775d8e0e8c5fb74c06c", + "sha256:9fd2f4813eaa3e421e82819d38e5b634d900faff7ae5a80cd89ccff407175e69", + "sha256:a2e1e53df7dd27943da2b512895125b33fb20f81862c9fed7b3bab2a1de684d1", + "sha256:ab43bc0836820b7900dfffc025b996784aec26ec87dc1df4f95a40398760223f", + "sha256:ba449b56fa419fb19bf2a2438adbd2433f27087a6fe115917eaf9cfca684d5b6", + "sha256:d3f632cefad2cf247bd845794002585e3772288bfcb0dbac59fdecd32cd38b67", + "sha256:d51311496061863caae2cfe120cf1ef37900019b86c89c2d75f0918e0b4b8bf3" ], - "version": "==2019.11.1" + "version": "==2019.12.19" }, "requests": { "hashes": [ diff --git a/atst/domain/csp/cloud.py b/atst/domain/csp/cloud.py index ea48ba4e..36c91c95 100644 --- a/atst/domain/csp/cloud.py +++ b/atst/domain/csp/cloud.py @@ -6,6 +6,7 @@ from atst.models.user import User from atst.models.application import Application from atst.models.environment import Environment from atst.models.environment_role import EnvironmentRole +from .policy import AzurePolicyManager class GeneralCSPException(Exception): @@ -401,6 +402,7 @@ REMOTE_ROOT_ROLE_DEF_ID = "/providers/Microsoft.Authorization/roleDefinitions/00 class AzureSDKProvider(object): def __init__(self): from azure.mgmt import subscription, authorization, managementgroups + from azure.mgmt.resource import policy import azure.graphrbac as graphrbac import azure.common.credentials as credentials from msrestazure.azure_cloud import AZURE_PUBLIC_CLOUD @@ -410,6 +412,7 @@ class AzureSDKProvider(object): self.managementgroups = managementgroups self.graphrbac = graphrbac self.credentials = credentials + self.policy = policy # may change to a JEDI cloud self.cloud = AZURE_PUBLIC_CLOUD @@ -427,6 +430,8 @@ class AzureCloudProvider(CloudProviderInterface): else: self.sdk = azure_sdk_provider + self.policy_manager = AzurePolicyManager(config["AZURE_POLICY_LOCATION"]) + def create_environment( self, auth_credentials: Dict, user: User, environment: Environment ): @@ -561,6 +566,51 @@ class AzureCloudProvider(CloudProviderInterface): # but we just don't have a valid ID pass + AZURE_MANAGEMENT_API = "https://management.azure.com" + + def _create_policy_definition( + self, credentials, subscription_id, management_group_id, properties, + ): + """ + Requires credentials that have AZURE_MANAGEMENT_API + specified as the resource. The Service Principal + specified in the credentials must have the "Resource + Policy Contributor" role assigned with a scope at least + as high as the management group specified by + management_group_id. + + Arguments: + credentials -- ServicePrincipalCredentials + subscription_id -- str, ID of the subscription (just the UUID, not the path) + management_group_id -- str, ID of the management group (just the UUID, not the path) + properties -- dictionary, the "properties" section of a valid Azure policy definition document + + Returns: + azure.mgmt.resource.policy.[api version].models.PolicyDefinition: the PolicyDefinition object provided to Azure + + Raises: + TBD + """ + # TODO: which subscription would this be? + client = self.sdk.policy.PolicyClient(credentials, subscription_id) + + definition = client.policy_definitions.models.PolicyDefinition( + policy_type=properties.get("policyType"), + mode=properties.get("mode"), + display_name=properties.get("displayName"), + description=properties.get("description"), + policy_rule=properties.get("policyRule"), + parameters=properties.get("parameters"), + ) + + name = properties.get("displayName") + + return client.policy_definitions.create_or_update_at_management_group( + policy_definition_name=name, + parameters=definition, + management_group_id=management_group_id, + ) + def _get_management_service_principal(self): # we really should be using graph.microsoft.com, but i'm getting # "expired token" errors for that diff --git a/atst/domain/csp/policy.py b/atst/domain/csp/policy.py new file mode 100644 index 00000000..cfff9aae --- /dev/null +++ b/atst/domain/csp/policy.py @@ -0,0 +1,47 @@ +from glob import glob +import json +from dataclasses import dataclass +from os.path import join as path_join + + +class AzurePolicyManager: + def __init__(self, static_policy_location): + self._static_policy_location = static_policy_location + + @property + def portfolio_definitions(self): + if getattr(self, "_portfolio_definitions", None) is None: + portfolio_files = self._glob_json("portfolios") + self._portfolio_definitions = self._load_policies(portfolio_files) + + return self._portfolio_definitions + + @property + def application_definitions(self): + pass + + @property + def environment_definitions(self): + pass + + def _glob_json(self, path): + return glob(path_join(self._static_policy_location, "portfolios", "*.json")) + + def _load_policies(self, json_policies): + return [self._load_policy(pol) for pol in json_policies] + + def _load_policy(self, policy_file): + with open(policy_file, "r") as file_: + doc = json.loads(file_.read()) + return AzurePolicy( + definition_point=doc["definitionPoint"], + definition=doc["policyDefinition"], + parameters=doc["parameters"], + ) + + +@dataclass +class AzurePolicy: + definition_point: str + definition: dict + parameters: dict diff --git a/config/base.ini b/config/base.ini index 2cc8fd93..0feba51f 100644 --- a/config/base.ini +++ b/config/base.ini @@ -3,6 +3,7 @@ ASSETS_URL AZURE_ACCOUNT_NAME AZURE_STORAGE_KEY AZURE_TO_BUCKET_NAME +AZURE_POLICY_LOCATION=policies BLOB_STORAGE_URL=http://localhost:8000/ CAC_URL = http://localhost:8000/login-redirect CA_CHAIN = ssl/server-certs/ca-chain.pem diff --git a/policies/portfolios/allowed-resource-types.json b/policies/portfolios/allowed-resource-types.json new file mode 100644 index 00000000..1358a9f4 --- /dev/null +++ b/policies/portfolios/allowed-resource-types.json @@ -0,0 +1,40 @@ +{ + "definitionPoint": "portfolio", + "policyDefinition": { + "properties": { + "displayName": "Allowed resource types", + "policyType": "Custom", + "mode": "Indexed", + "description": "This policy enables you to specify the resource types that your organization can deploy.", + "parameters": { + "listOfResourceTypesAllowed": { + "type": "Array", + "metadata": { + "description": "The list of resource types that can be deployed.", + "displayName": "Allowed resource types", + "strongType": "resourceTypes" + } + } + }, + "policyRule": { + "if": { + "not": { + "field": "type", + "in": "[parameters('listOfResourceTypesAllowed')]" + } + }, + "then": { + "effect": "deny" + } + } + }, + "type": "Microsoft.Authorization/policyDefinitions" + }, + "parameters": { + "listOfResourceTypesAllowed": { + "value": [ + "Microsoft.Cache" + ] + } + } +} diff --git a/policies/portfolios/region-restriction.json b/policies/portfolios/region-restriction.json new file mode 100644 index 00000000..a828c40e --- /dev/null +++ b/policies/portfolios/region-restriction.json @@ -0,0 +1,51 @@ +{ + "definitionPoint": "portfolio", + "policyDefinition": { + "properties": { + "displayName": "Custom - Region Restriction", + "policyType": "Custom", + "mode": "Indexed", + "parameters": { + "listOfAllowedLocations": { + "type": "Array", + "metadata": { + "displayName": "Allowed locations", + "description": "The list of locations that can be specified when deploying resources.", + "strongType": "location" + } + } + }, + "policyRule": { + "if": { + "allOf": [ + { + "field": "location", + "notIn": "[parameters('listOfAllowedLocations')]" + }, + { + "field": "location", + "notEquals": "global" + }, + { + "field": "type", + "notEquals": "Microsoft.AzureActiveDirectory/b2cDirectories" + } + ] + }, + "then": { + "effect": "Deny" + } + } + }, + "type": "Microsoft.Authorization/policyDefinitions" + }, + "parameters": { + "listOfAllowedLocations": { + "value": [ + "eastus", + "southcentralus", + "westus" + ] + } + } +} diff --git a/tests/domain/cloud/test_azure_csp.py b/tests/domain/cloud/test_azure_csp.py index 39c6655e..dc10a6df 100644 --- a/tests/domain/cloud/test_azure_csp.py +++ b/tests/domain/cloud/test_azure_csp.py @@ -90,3 +90,28 @@ def test_create_atat_admin_user_succeeds(mock_azure: AzureCloudProvider): result = mock_azure.create_atat_admin_user(AUTH_CREDENTIALS, environment_id) assert result.get("csp_user_id") == csp_user_id + + +def test_create_policy_definition_succeeds(mock_azure: AzureCloudProvider): + subscription_id = str(uuid4()) + management_group_id = str(uuid4()) + properties = { + "policyType": "test", + "displayName": "test policy", + } + + result = mock_azure._create_policy_definition( + AUTH_CREDENTIALS, subscription_id, management_group_id, properties + ) + azure_sdk_method = ( + mock_azure.sdk.policy.PolicyClient.return_value.policy_definitions.create_or_update_at_management_group + ) + mock_policy_definition = ( + mock_azure.sdk.policy.PolicyClient.return_value.policy_definitions.models.PolicyDefinition() + ) + assert azure_sdk_method.called + azure_sdk_method.assert_called_with( + management_group_id=management_group_id, + policy_definition_name=properties.get("displayName"), + parameters=mock_policy_definition, + ) diff --git a/tests/domain/cloud/test_policy.py b/tests/domain/cloud/test_policy.py new file mode 100644 index 00000000..c0189262 --- /dev/null +++ b/tests/domain/cloud/test_policy.py @@ -0,0 +1,8 @@ +from atst.domain.csp.policy import AzurePolicyManager, AzurePolicy + + +def test_portfolio_definitions(): + manager = AzurePolicyManager("policies") + assert len(manager.portfolio_definitions) > 0 + policy = manager.portfolio_definitions[0] + assert isinstance(policy, AzurePolicy) diff --git a/tests/mock_azure.py b/tests/mock_azure.py index a360df64..417e69fb 100644 --- a/tests/mock_azure.py +++ b/tests/mock_azure.py @@ -7,6 +7,7 @@ AZURE_CONFIG = { "AZURE_CLIENT_ID": "MOCK", "AZURE_SECRET_KEY": "MOCK", "AZURE_TENANT_ID": "MOCK", + "AZURE_POLICY_LOCATION": "policies", } AUTH_CREDENTIALS = { @@ -46,6 +47,12 @@ def mock_credentials(): return Mock(spec=credentials) +def mock_policy(): + from azure.mgmt.resource import policy + + return Mock(spec=policy) + + class MockAzureSDK(object): def __init__(self): from msrestazure.azure_cloud import AZURE_PUBLIC_CLOUD @@ -55,6 +62,7 @@ class MockAzureSDK(object): self.managementgroups = mock_managementgroups() self.graphrbac = mock_graphrbac() self.credentials = mock_credentials() + self.policy = mock_policy() # may change to a JEDI cloud self.cloud = AZURE_PUBLIC_CLOUD