From 2b8cbd177c6344fe990a51e89c2bbf8bec5a60d7 Mon Sep 17 00:00:00 2001 From: Zakir Gowani Date: Tue, 5 Mar 2019 16:53:12 -0600 Subject: [PATCH] Use authutils module --- Pipfile | 4 +- Pipfile.lock | 596 +++++++++++++++++++++++++ manifest_service/api.py | 43 +- manifest_service/dev_settings.py | 1 + manifest_service/errors.py | 7 - manifest_service/manifests/__init__.py | 158 ++++--- tests/app_test.py | 112 +++-- tests/conftest.py | 8 +- 8 files changed, 806 insertions(+), 123 deletions(-) create mode 100644 Pipfile.lock diff --git a/Pipfile b/Pipfile index 23f030e..9fdd39d 100644 --- a/Pipfile +++ b/Pipfile @@ -10,6 +10,8 @@ pytest="*" flask = "*" authutils = "*" boto3 = "*" +pytest-mock = "*" +pytest-flask = "*" [requires] -python_version = "3.6" +python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..8d48877 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,596 @@ +{ + "_meta": { + "hash": { + "sha256": "ec4bc0d554eff479ad9639e99c70f03983baade982db25c9211fb2d4aac62417" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.7" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "addict": { + "hashes": [ + "sha256:57c41c427cb355e17f01d836a47aeae51e2175cf334e5d58e78476c77b224da1" + ], + "version": "==2.2.0" + }, + "argparse": { + "hashes": [ + "sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4", + "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314" + ], + "version": "==1.4.0" + }, + "asn1crypto": { + "hashes": [ + "sha256:2f1adbb7546ed199e3c90ef23ec95c5cf3585bac7d11fb7eb562a3fe89c64e87", + "sha256:9d5c20441baf0cb60a4ac34cc447c6c189024b6b4c6cd7877034f4965c464e49" + ], + "version": "==0.24.0" + }, + "atomicwrites": { + "hashes": [ + "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", + "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" + ], + "version": "==1.3.0" + }, + "attrs": { + "hashes": [ + "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", + "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" + ], + "version": "==19.1.0" + }, + "authlib": { + "hashes": [ + "sha256:b61c6c6fd230c4ba8602fd85ee9a40e6dc859387699a1cd1f7247c4b109dcc17", + "sha256:eda3e5af921a368091fef721d6d169bcff2aa0003d05113bc26e127f58c9a5e8" + ], + "version": "==0.10" + }, + "authutils": { + "hashes": [ + "sha256:7da5672d668bf0453fe35898693e0e3832352ac25e7ea968d9f3bb41e08ee6e2" + ], + "index": "pypi", + "version": "==3.0.3" + }, + "babel": { + "hashes": [ + "sha256:6778d85147d5d85345c14a26aada5e478ab04e39b078b0745ee6870c2b5cf669", + "sha256:8cba50f48c529ca3fa18cf81fa9403be176d374ac4d60738b839122dfaaa3d23" + ], + "version": "==2.6.0" + }, + "boto3": { + "hashes": [ + "sha256:3b817da4a804f7ee5ce252e38b890ec84752c51488360a9dc492eb27c258ff2b", + "sha256:76d6c2d955d9a799a8d9cf8754ad377059e7809dd1820bace07e600f0a7c4ad6" + ], + "index": "pypi", + "version": "==1.9.107" + }, + "botocore": { + "hashes": [ + "sha256:0c2bd4cf18c8790983e9df3198125d16578195e93401c28edbda4e4d6d0475ae", + "sha256:479e9c89653380d546be52dfea562be03a41665ef4758e98d4f30b1148946a92" + ], + "version": "==1.12.107" + }, + "cached-property": { + "hashes": [ + "sha256:3a026f1a54135677e7da5ce819b0c690f156f37976f3e30c5430740725203d7f", + "sha256:9217a59f14a5682da7c4b8829deadbfc194ac22e9908ccf7c8820234e80a1504" + ], + "version": "==1.5.1" + }, + "cdiserrors": { + "hashes": [ + "sha256:d6a91162aa76c7af37b79a8bba1bc5ebd06beb331ac522d36a74c4b300f6f333" + ], + "version": "==0.1.2" + }, + "cdislogging": { + "hashes": [ + "sha256:4868a18c294e9796479824496ebaa7c4b9ae6175e5bade2e5051c838f84f42e9" + ], + "version": "==0.1.1" + }, + "certifi": { + "hashes": [ + "sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7", + "sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033" + ], + "version": "==2018.11.29" + }, + "cffi": { + "hashes": [ + "sha256:00b97afa72c233495560a0793cdc86c2571721b4271c0667addc83c417f3d90f", + "sha256:0ba1b0c90f2124459f6966a10c03794082a2f3985cd699d7d63c4a8dae113e11", + "sha256:0bffb69da295a4fc3349f2ec7cbe16b8ba057b0a593a92cbe8396e535244ee9d", + "sha256:21469a2b1082088d11ccd79dd84157ba42d940064abbfa59cf5f024c19cf4891", + "sha256:2e4812f7fa984bf1ab253a40f1f4391b604f7fc424a3e21f7de542a7f8f7aedf", + "sha256:2eac2cdd07b9049dd4e68449b90d3ef1adc7c759463af5beb53a84f1db62e36c", + "sha256:2f9089979d7456c74d21303c7851f158833d48fb265876923edcb2d0194104ed", + "sha256:3dd13feff00bddb0bd2d650cdb7338f815c1789a91a6f68fdc00e5c5ed40329b", + "sha256:4065c32b52f4b142f417af6f33a5024edc1336aa845b9d5a8d86071f6fcaac5a", + "sha256:51a4ba1256e9003a3acf508e3b4f4661bebd015b8180cc31849da222426ef585", + "sha256:59888faac06403767c0cf8cfb3f4a777b2939b1fbd9f729299b5384f097f05ea", + "sha256:59c87886640574d8b14910840327f5cd15954e26ed0bbd4e7cef95fa5aef218f", + "sha256:610fc7d6db6c56a244c2701575f6851461753c60f73f2de89c79bbf1cc807f33", + "sha256:70aeadeecb281ea901bf4230c6222af0248c41044d6f57401a614ea59d96d145", + "sha256:71e1296d5e66c59cd2c0f2d72dc476d42afe02aeddc833d8e05630a0551dad7a", + "sha256:8fc7a49b440ea752cfdf1d51a586fd08d395ff7a5d555dc69e84b1939f7ddee3", + "sha256:9b5c2afd2d6e3771d516045a6cfa11a8da9a60e3d128746a7fe9ab36dfe7221f", + "sha256:9c759051ebcb244d9d55ee791259ddd158188d15adee3c152502d3b69005e6bd", + "sha256:b4d1011fec5ec12aa7cc10c05a2f2f12dfa0adfe958e56ae38dc140614035804", + "sha256:b4f1d6332339ecc61275bebd1f7b674098a66fea11a00c84d1c58851e618dc0d", + "sha256:c030cda3dc8e62b814831faa4eb93dd9a46498af8cd1d5c178c2de856972fd92", + "sha256:c2e1f2012e56d61390c0e668c20c4fb0ae667c44d6f6a2eeea5d7148dcd3df9f", + "sha256:c37c77d6562074452120fc6c02ad86ec928f5710fbc435a181d69334b4de1d84", + "sha256:c8149780c60f8fd02752d0429246088c6c04e234b895c4a42e1ea9b4de8d27fb", + "sha256:cbeeef1dc3c4299bd746b774f019de9e4672f7cc666c777cd5b409f0b746dac7", + "sha256:e113878a446c6228669144ae8a56e268c91b7f1fafae927adc4879d9849e0ea7", + "sha256:e21162bf941b85c0cda08224dade5def9360f53b09f9f259adb85fc7dd0e7b35", + "sha256:fb6934ef4744becbda3143d30c6604718871495a5e36c408431bf33d9c146889" + ], + "version": "==1.12.2" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "click": { + "hashes": [ + "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", + "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" + ], + "version": "==7.0" + }, + "cryptography": { + "hashes": [ + "sha256:066f815f1fe46020877c5983a7e747ae140f517f1b09030ec098503575265ce1", + "sha256:210210d9df0afba9e000636e97810117dc55b7157c903a55716bb73e3ae07705", + "sha256:26c821cbeb683facb966045e2064303029d572a87ee69ca5a1bf54bf55f93ca6", + "sha256:2afb83308dc5c5255149ff7d3fb9964f7c9ee3d59b603ec18ccf5b0a8852e2b1", + "sha256:2db34e5c45988f36f7a08a7ab2b69638994a8923853dec2d4af121f689c66dc8", + "sha256:409c4653e0f719fa78febcb71ac417076ae5e20160aec7270c91d009837b9151", + "sha256:45a4f4cf4f4e6a55c8128f8b76b4c057027b27d4c67e3fe157fa02f27e37830d", + "sha256:48eab46ef38faf1031e58dfcc9c3e71756a1108f4c9c966150b605d4a1a7f659", + "sha256:6b9e0ae298ab20d371fc26e2129fd683cfc0cfde4d157c6341722de645146537", + "sha256:6c4778afe50f413707f604828c1ad1ff81fadf6c110cb669579dea7e2e98a75e", + "sha256:8c33fb99025d353c9520141f8bc989c2134a1f76bac6369cea060812f5b5c2bb", + "sha256:9873a1760a274b620a135054b756f9f218fa61ca030e42df31b409f0fb738b6c", + "sha256:9b069768c627f3f5623b1cbd3248c5e7e92aec62f4c98827059eed7053138cc9", + "sha256:9e4ce27a507e4886efbd3c32d120db5089b906979a4debf1d5939ec01b9dd6c5", + "sha256:acb424eaca214cb08735f1a744eceb97d014de6530c1ea23beb86d9c6f13c2ad", + "sha256:c8181c7d77388fe26ab8418bb088b1a1ef5fde058c6926790c8a0a3d94075a4a", + "sha256:d4afbb0840f489b60f5a580a41a1b9c3622e08ecb5eec8614d4fb4cd914c4460", + "sha256:d9ed28030797c00f4bc43c86bf819266c76a5ea61d006cd4078a93ebf7da6bfd", + "sha256:e603aa7bb52e4e8ed4119a58a03b60323918467ef209e6ff9db3ac382e5cf2c6" + ], + "version": "==2.6.1" + }, + "debtcollector": { + "hashes": [ + "sha256:721b508130c2f133dcc14145c1e213967a84e31a15619b73d51dee79baef7f54", + "sha256:f6ce5a383ad73c23e1138dbb69bf45d33f4a4bdec38f02dbf2b89477ec5e55bc" + ], + "version": "==1.21.0" + }, + "docutils": { + "hashes": [ + "sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6", + "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274", + "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6" + ], + "version": "==0.14" + }, + "flask": { + "hashes": [ + "sha256:2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48", + "sha256:a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05" + ], + "index": "pypi", + "version": "==1.0.2" + }, + "idna": { + "hashes": [ + "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", + "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" + ], + "version": "==2.8" + }, + "iso8601": { + "hashes": [ + "sha256:210e0134677cc0d02f6028087fee1df1e1d76d372ee1db0bf30bf66c5c1c89a3", + "sha256:49c4b20e1f38aa5cf109ddcd39647ac419f928512c869dc01d5c7098eddede82", + "sha256:bbbae5fb4a7abfe71d4688fd64bff70b91bbd74ef6a99d964bab18f7fdf286dd" + ], + "version": "==0.1.12" + }, + "itsdangerous": { + "hashes": [ + "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", + "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749" + ], + "version": "==1.1.0" + }, + "jinja2": { + "hashes": [ + "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", + "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4" + ], + "version": "==2.10" + }, + "jmespath": { + "hashes": [ + "sha256:3720a4b1bd659dd2eecad0666459b9788813e032b83e7ba58578e48254e0a0e6", + "sha256:bde2aef6f44302dfb30320115b17d030798de8c4110e28d5cf6cf91a7a31074c" + ], + "version": "==0.9.4" + }, + "markupsafe": { + "hashes": [ + "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", + "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", + "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", + "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", + "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", + "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", + "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", + "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", + "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", + "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", + "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", + "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", + "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", + "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", + "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7" + ], + "version": "==1.1.1" + }, + "more-itertools": { + "hashes": [ + "sha256:0125e8f60e9e031347105eb1682cef932f5e97d7b9a1a28d9bf00c22a5daef40", + "sha256:590044e3942351a1bdb1de960b739ff4ce277960f2425ad4509446dbace8d9d1" + ], + "markers": "python_version > '2.7'", + "version": "==6.0.0" + }, + "msgpack": { + "hashes": [ + "sha256:26cb40116111c232bc235ce131cc3b4e76549088cb154e66a2eb8ff6fcc907ec", + "sha256:300fd3f2c664a3bf473d6a952f843b4a71454f4c592ed7e74a36b205c1782d28", + "sha256:3129c355342853007de4a2a86e75eab966119733eb15748819b6554363d4e85c", + "sha256:31f6d645ee5a97d59d3263fab9e6be76f69fa131cddc0d94091a3c8aca30d67a", + "sha256:3ce7ef7ee2546c3903ca8c934d09250531b80c6127e6478781ae31ed835aac4c", + "sha256:4008c72f5ef2b7936447dcb83db41d97e9791c83221be13d5e19db0796df1972", + "sha256:62bd8e43d204580308d477a157b78d3fee2fb4c15d32578108dc5d89866036c8", + "sha256:70cebfe08fb32f83051971264466eadf183101e335d8107b80002e632f425511", + "sha256:72cb7cf85e9df5251abd7b61a1af1fb77add15f40fa7328e924a9c0b6bc7a533", + "sha256:7c55649965c35eb32c499d17dadfb8f53358b961582846e1bc06f66b9bccc556", + "sha256:86b963a5de11336ec26bc4f839327673c9796b398b9f1fe6bb6150c2a5d00f0f", + "sha256:8c73c9bcdfb526247c5e4f4f6cf581b9bb86b388df82cfcaffde0a6e7bf3b43a", + "sha256:8e68c76c6aff4849089962d25346d6784d38e02baa23ffa513cf46be72e3a540", + "sha256:97ac6b867a8f63debc64f44efdc695109d541ecc361ee2dce2c8884ab37360a1", + "sha256:9d4f546af72aa001241d74a79caec278bcc007b4bcde4099994732e98012c858", + "sha256:a28e69fe5468c9f5251c7e4e7232286d71b7dfadc74f312006ebe984433e9746", + "sha256:fd509d4aa95404ce8d86b4e32ce66d5d706fd6646c205e1c2a715d87078683a2" + ], + "version": "==0.6.1" + }, + "netaddr": { + "hashes": [ + "sha256:38aeec7cdd035081d3a4c306394b19d677623bf76fa0913f6695127c7753aefd", + "sha256:56b3558bd71f3f6999e4c52e349f38660e54a7a8a9943335f73dfc96883e08ca" + ], + "version": "==0.7.19" + }, + "netifaces": { + "hashes": [ + "sha256:078986caf4d6a602a4257d3686afe4544ea74362b8928e9f4389b5cd262bc215", + "sha256:0c4304c6d5b33fbd9b20fdc369f3a2fef1a8bbacfb6fd05b9708db01333e9e7b", + "sha256:2dee9ffdd16292878336a58d04a20f0ffe95555465fee7c9bd23b3490ef2abf3", + "sha256:3095218b66d359092b82f07c5422293c2f6559cf8d36b96b379cc4cdc26eeffa", + "sha256:30ed89ab8aff715caf9a9d827aa69cd02ad9f6b1896fd3fb4beb998466ed9a3c", + "sha256:4921ed406386246b84465950d15a4f63480c1458b0979c272364054b29d73084", + "sha256:563a1a366ee0fb3d96caab79b7ac7abd2c0a0577b157cc5a40301373a0501f89", + "sha256:5b3167f923f67924b356c1338eb9ba275b2ba8d64c7c2c47cf5b5db49d574994", + "sha256:6d84e50ec28e5d766c9911dce945412dc5b1ce760757c224c71e1a9759fa80c2", + "sha256:755050799b5d5aedb1396046f270abfc4befca9ccba3074f3dbbb3cb34f13aae", + "sha256:75d3a4ec5035db7478520ac547f7c176e9fd438269e795819b67223c486e5cbe", + "sha256:7a25a8e28281504f0e23e181d7a9ed699c72f061ca6bdfcd96c423c2a89e75fc", + "sha256:7cc6fd1eca65be588f001005446a47981cbe0b2909f5be8feafef3bf351a4e24", + "sha256:86b8a140e891bb23c8b9cb1804f1475eb13eea3dbbebef01fcbbf10fbafbee42", + "sha256:ad10acab2ef691eb29a1cc52c3be5ad1423700e993cc035066049fa72999d0dc", + "sha256:b2ff3a0a4f991d2da5376efd3365064a43909877e9fabfa801df970771161d29", + "sha256:b47e8f9ff6846756be3dc3fb242ca8e86752cd35a08e06d54ffc2e2a2aca70ea", + "sha256:da298241d87bcf468aa0f0705ba14572ad296f24c4fda5055d6988701d6fd8e1", + "sha256:db881478f1170c6dd524175ba1c83b99d3a6f992a35eca756de0ddc4690a1940", + "sha256:f0427755c68571df37dc58835e53a4307884a48dec76f3c01e33eb0d4a3a81d7", + "sha256:f8885cc48c8c7ad51f36c175e462840f163cb4687eeb6c6d7dfaf7197308e36b", + "sha256:f911b7f0083d445c8d24cfa5b42ad4996e33250400492080f5018a28c026db2b" + ], + "version": "==0.10.9" + }, + "oslo.config": { + "hashes": [ + "sha256:7ea34cf642df4d18f92637b58dee92c1667c141aa004ce472e257fafc278ce11", + "sha256:cb237123b8215337467a687a2628291641599b070c0f16c4a5b2d926b83bd386" + ], + "version": "==6.8.1" + }, + "oslo.i18n": { + "hashes": [ + "sha256:2669908357e1e49a754dc0c279512246341ae889176c568b89fd9233e65a6ae1", + "sha256:7ecec04b682209292cf4dcfa29015956d15466af82352d92bfc442c8454f1ba2" + ], + "version": "==3.23.1" + }, + "oslo.serialization": { + "hashes": [ + "sha256:0ac58e6ef6427c800f33c9b344c3cd93aa6892562cac5efa9e0584dea20f1859", + "sha256:c3c73eb1fa45aaf4cdf3b82a0cf85497a245c7e88e803fdafa29e4315d991eb3" + ], + "version": "==2.28.2" + }, + "oslo.utils": { + "hashes": [ + "sha256:0eb69576a12b0f43bdf1cf1e0adb648bceae7a5b5b161f53ba6e7805aee000fd", + "sha256:92fdd8b7f5cb079f03648015c186c8292a0a7bba918cdab32fdd1133b83280a1" + ], + "version": "==3.40.3" + }, + "pbr": { + "hashes": [ + "sha256:8257baf496c8522437e8a6cfe0f15e00aedc6c0e0e7c9d55eeeeab31e0853843", + "sha256:8c361cc353d988e4f5b998555c88098b9d5964c2e11acf7b0d21925a66bb5824" + ], + "version": "==5.1.3" + }, + "pluggy": { + "hashes": [ + "sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f", + "sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746" + ], + "version": "==0.9.0" + }, + "prettytable": { + "hashes": [ + "sha256:2d5460dc9db74a32bcc8f9f67de68b2c4f4d2f01fa3bd518764c69156d9cacd9", + "sha256:853c116513625c738dc3ce1aee148b5b5757a86727e67eff6502c7ca59d43c36", + "sha256:a53da3b43d7a5c229b5e3ca2892ef982c46b7923b51e98f0db49956531211c4f" + ], + "version": "==0.7.2" + }, + "py": { + "hashes": [ + "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", + "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53" + ], + "version": "==1.8.0" + }, + "pycparser": { + "hashes": [ + "sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3" + ], + "version": "==2.19" + }, + "pyjwt": { + "hashes": [ + "sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e", + "sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96" + ], + "version": "==1.7.1" + }, + "pyparsing": { + "hashes": [ + "sha256:66c9268862641abcac4a96ba74506e594c884e3f57690a696d21ad8210ed667a", + "sha256:f6c5ef0d7480ad048c054c37632c67fca55299990fff127850181659eea33fc3" + ], + "version": "==2.3.1" + }, + "pytest": { + "hashes": [ + "sha256:067a1d4bf827ffdd56ad21bd46674703fce77c5957f6c1eef731f6146bfcef1c", + "sha256:9687049d53695ad45cf5fdc7bbd51f0c49f1ea3ecfc4b7f3fde7501b541f17f4" + ], + "version": "==4.3.0" + }, + "pytest-flask": { + "hashes": [ + "sha256:df69f2b552098227d7b7a8a48d6df2742a4d865d0807eac4916fb622c2d47e1a", + "sha256:e9b120d23f73e0495d8fa1bc3b580b18cc9f98b8d6808bc45fdbcbab7d718242" + ], + "index": "pypi", + "version": "==0.14.0" + }, + "pytest-mock": { + "hashes": [ + "sha256:4d0d06d173eecf172703219a71dbd4ade0e13904e6bbce1ce660e2e0dc78b5c4", + "sha256:bfdf02789e3d197bd682a758cae0a4a18706566395fbe2803badcd1335e0173e" + ], + "index": "pypi", + "version": "==1.10.1" + }, + "python-dateutil": { + "hashes": [ + "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", + "sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e" + ], + "markers": "python_version >= '2.7'", + "version": "==2.8.0" + }, + "python-keystoneclient": { + "hashes": [ + "sha256:4429b973fc45636d1f7117791d930391a432b4d0db76eafb75f918a8e6d68cf0", + "sha256:6835a58e0a533947fca317fff487f040af5fbc1d7b78c75d9f812c09b0307740" + ], + "version": "==1.8.1" + }, + "pytz": { + "hashes": [ + "sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9", + "sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c" + ], + "version": "==2018.9" + }, + "pyyaml": { + "hashes": [ + "sha256:3d7da3009c0f3e783b2c873687652d83b1bbfd5c88e9813fb7e5b03c0dd3108b", + "sha256:3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf", + "sha256:40c71b8e076d0550b2e6380bada1f1cd1017b882f7e16f09a65be98e017f211a", + "sha256:558dd60b890ba8fd982e05941927a3911dc409a63dcb8b634feaa0cda69330d3", + "sha256:a7c28b45d9f99102fa092bb213aa12e0aaf9a6a1f5e395d36166639c1f96c3a1", + "sha256:aa7dd4a6a427aed7df6fb7f08a580d68d9b118d90310374716ae90b710280af1", + "sha256:bc558586e6045763782014934bfaf39d48b8ae85a2713117d16c39864085c613", + "sha256:d46d7982b62e0729ad0175a9bc7e10a566fc07b224d2c79fafb5e032727eaa04", + "sha256:d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f", + "sha256:e01d3203230e1786cd91ccfdc8f8454c8069c91bee3962ad93b87a4b2860f537", + "sha256:e170a9e6fcfd19021dd29845af83bb79236068bf5fd4df3327c1be18182b2531" + ], + "version": "==3.13" + }, + "requests": { + "hashes": [ + "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", + "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b" + ], + "version": "==2.21.0" + }, + "rfc3986": { + "hashes": [ + "sha256:5ad82677b02b88c8d24f6511b4ee9baa5e7da675599b479fbbc5c9c578b5b737", + "sha256:bc3ae4b7cd88a99eff2d3900fcb858d44562fd7f273fc07aeef568b9bb6fc4e1" + ], + "version": "==1.2.0" + }, + "s3transfer": { + "hashes": [ + "sha256:7b9ad3213bff7d357f888e0fab5101b56fa1a0548ee77d121c3a3dbfbef4cb2e", + "sha256:f23d5cb7d862b104401d9021fc82e5fa0e0cf57b7660a1331425aab0c691d021" + ], + "version": "==0.2.0" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + }, + "stevedore": { + "hashes": [ + "sha256:7be098ff53d87f23d798a7ce7ae5c31f094f3deb92ba18059b1aeb1ca9fec0a0", + "sha256:7d1ce610a87d26f53c087da61f06f9b7f7e552efad2a7f6d2322632b5f932ea2" + ], + "version": "==1.30.1" + }, + "urllib3": { + "hashes": [ + "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", + "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22" + ], + "markers": "python_version >= '3.4'", + "version": "==1.24.1" + }, + "werkzeug": { + "hashes": [ + "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c", + "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b" + ], + "version": "==0.14.1" + }, + "wrapt": { + "hashes": [ + "sha256:4aea003270831cceb8a90ff27c4031da6ead7ec1886023b80ce0dfe0adf61533" + ], + "version": "==1.11.1" + }, + "xmltodict": { + "hashes": [ + "sha256:50d8c638ed7ecb88d90561beedbf720c9b4e851a9fa6c47ebd64e99d166d8a21", + "sha256:8bbcb45cc982f48b2ca8fe7e7827c5d792f217ecf1792626f808bf41c3b86051" + ], + "version": "==0.12.0" + } + }, + "develop": { + "atomicwrites": { + "hashes": [ + "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", + "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" + ], + "version": "==1.3.0" + }, + "attrs": { + "hashes": [ + "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", + "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" + ], + "version": "==19.1.0" + }, + "more-itertools": { + "hashes": [ + "sha256:0125e8f60e9e031347105eb1682cef932f5e97d7b9a1a28d9bf00c22a5daef40", + "sha256:590044e3942351a1bdb1de960b739ff4ce277960f2425ad4509446dbace8d9d1" + ], + "markers": "python_version > '2.7'", + "version": "==6.0.0" + }, + "pluggy": { + "hashes": [ + "sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f", + "sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746" + ], + "version": "==0.9.0" + }, + "py": { + "hashes": [ + "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", + "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53" + ], + "version": "==1.8.0" + }, + "pytest": { + "hashes": [ + "sha256:067a1d4bf827ffdd56ad21bd46674703fce77c5957f6c1eef731f6146bfcef1c", + "sha256:9687049d53695ad45cf5fdc7bbd51f0c49f1ea3ecfc4b7f3fde7501b541f17f4" + ], + "version": "==4.3.0" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + } + } +} diff --git a/manifest_service/api.py b/manifest_service/api.py index 2228f9b..4fc1e6e 100644 --- a/manifest_service/api.py +++ b/manifest_service/api.py @@ -8,11 +8,22 @@ from .admin_endpoints import blueprint as admin_bp from .manifests import blueprint as manifests_bp +def create_app(): + app = flask.Flask(__name__) + app.register_blueprint(admin_bp, url_prefix="/admin") + app.register_blueprint(manifests_bp, url_prefix="") -app = flask.Flask(__name__) -app.register_blueprint(admin_bp, url_prefix="/admin") -app.register_blueprint(manifests_bp, url_prefix="") + # load configuration + app.config.from_object("manifest_service.dev_settings") + + # try: + # app_init(app) + # except: + # app.logger.exception("Couldn't initialize application, continuing anyway") + + return app +app = create_app() @app.route("/user_endpoint", methods=["GET"]) def do_something_connected(): @@ -49,29 +60,17 @@ def health_check(): return "Healthy", 200 -def app_init(app): - app.logger.info("Initializing app") - start = time.time() +# def app_init(app): +# app.logger.info("Initializing app") +# start = time.time() - # do the necessary here! +# # do the necessary here! - end = int(round(time.time() - start)) - app.logger.info("Initialization complete in {} sec".format(end)) +# end = int(round(time.time() - start)) +# app.logger.info("Initialization complete in {} sec".format(end)) def run_for_development(**kwargs): app.logger.setLevel(logging.INFO) - - # import os - # for key in ["http_proxy", "https_proxy"]: - # if os.environ.get(key): - # del os.environ[key] - - # load configuration - app.config.from_object("manifest_service.dev_settings") - - try: - app_init(app) - except: - app.logger.exception("Couldn't initialize application, continuing anyway") + app.run(**kwargs) diff --git a/manifest_service/dev_settings.py b/manifest_service/dev_settings.py index ae78e35..fbd0c68 100644 --- a/manifest_service/dev_settings.py +++ b/manifest_service/dev_settings.py @@ -1,3 +1,4 @@ # To run locally, fill these values out FENCE_USER_INFO_URL = "https://fence-service/user/user" +OIDC_ISSUER = "https://fence-service/user/" MANIFEST_BUCKET_NAME = "the_name_of_the_s3_bucket_where_manifests_are_stored" \ No newline at end of file diff --git a/manifest_service/errors.py b/manifest_service/errors.py index c4c07d2..6bb0847 100644 --- a/manifest_service/errors.py +++ b/manifest_service/errors.py @@ -1,13 +1,6 @@ from cdiserrors import * from authutils.errors import JWTError -class CustomException(APIError): - def __init__(self, message): - self.message = str(message) - self.code = 500 - - - class UserError(APIError): ''' User error. diff --git a/manifest_service/manifests/__init__.py b/manifest_service/manifests/__init__.py index a5308b4..a6cfcf4 100644 --- a/manifest_service/manifests/__init__.py +++ b/manifest_service/manifests/__init__.py @@ -5,6 +5,12 @@ import requests import ntpath from datetime import date, datetime +from authutils.token.validate import ( + current_token, + require_auth_header, + validate_request, + set_current_token +) blueprint = Blueprint("manifests", __name__) @@ -12,11 +18,28 @@ session = boto3.Session(region_name="us-east-1") s3 = session.resource("s3") -def get_folder_name_from_user_info(user_info): - return "user-" + str(user_info["user_id"]) +def get_folder_name_from_token(user_info): + """ + Returns the name of the user's manifest folder (their "prefix"). + It takes a "user_info" dict, which is the response that Fence returns at /user/user + The convention we'll use here is that a user's folder name will be "user-x" where x is + their ID (integer). + + According to the revproxy's helpers.js, it looks like the user_id is stored in a variable called "sub". Hm. + """ + return "user-" + str(user_info["sub"]) -def does_the_user_have_read_access_on_at_least_one_project(project_access_dict): - privileges = list(project_access_dict.values()) +def does_the_user_have_read_access_on_at_least_one_project(current_token): + """ + Returns True if the user has both read and read-storage access on at least one project, + False otherwise. + """ + privileges = [] + try: + project_access_dict = current_token.get("context").get("user").get("projects") + privileges = list(project_access_dict.values()) + except Exception: + return False if len(privileges) == 0: return False @@ -27,32 +50,12 @@ def does_the_user_have_read_access_on_at_least_one_project(project_access_dict): return False -def is_the_user_permitted_to_use_this_service(access_token): +def is_valid_manifest(manifest_json): """ - This function returns (access_token, full_user_info_response_from_fence) + Returns (True, "") if the manifest.json is a list of the form [{'k' : v}, ...], + where valid keys are object_id and subject_id. + Otherwise, returns (False, error_msg) """ - headers = { - "Accept" : "application/json", - "Content-Type": "application/json", - } - - cookies = { - "access_token" : access_token - } - - FENCE_USER_INFO_URL = flask.current_app.config.get("FENCE_USER_INFO_URL") - r = requests.get(FENCE_USER_INFO_URL, headers=headers, cookies=cookies) - - try: - json = r.json() - except: - return False, None - - project_access_dict = json["project_access"] - - return does_the_user_have_read_access_on_at_least_one_project(project_access_dict), json - -def is_valid_manifest(manifest_json): valid_keys = set(["object_id" , "subject_id"]) error_msg = "Manifest format is invalid. Please POST a list of key-value pairs, like [{'k' : v}, ...] Valid keys are: " + " ".join(valid_keys) if type(manifest_json) != list: @@ -69,12 +72,21 @@ def is_valid_manifest(manifest_json): return True, "" def generate_unique_manifest_filename(folder_name, manifest_bucket_name): + """ + Returns a filename of the form manifest--.json that is + unique among the files in the user's manifest folder. + """ timestamp = datetime.now().isoformat() users_existing_manifest_files = list_files_in_bucket(manifest_bucket_name, folder_name) filename = generate_unique_filename_with_timestamp_and_increment(timestamp, users_existing_manifest_files) return filename def generate_unique_filename_with_timestamp_and_increment(timestamp, users_existing_manifest_files): + """ + A helper function for generate_unique_manifest_filename(), which facilitates unit testing. + Adds an increment to the filename if there happens to be another timestamped file with the same name + (unlikely, but good to check). + """ filename_without_extension = "manifest-" + timestamp.replace(":", "-") extension = ".json" @@ -89,6 +101,9 @@ def generate_unique_filename_with_timestamp_and_increment(timestamp, users_exist return filename def list_files_in_bucket(bucket_name, folder): + """ + Lists the files in an s3 bucket. Returns a list of filenames. + """ rv = [] bucket = s3.Bucket(bucket_name) @@ -98,6 +113,9 @@ def list_files_in_bucket(bucket_name, folder): return rv def get_file_contents(bucket_name, folder, filename): + """ + Returns the body of a requested file as a string. + """ client = boto3.client("s3") obj = client.get_object(Bucket=bucket_name, Key=folder + "/" + filename) as_bytes = obj["Body"].read() @@ -107,19 +125,30 @@ def get_file_contents(bucket_name, folder, filename): @blueprint.route("/", methods=["GET"]) def get_manifests(): """ - Returns a list of filenames corresponding to the user's manifests + Returns a list of filenames corresponding to the user's manifests. + We find the appropriate folder ("prefix") in the bucket by asking Fence for + info about the user's access token. + --- + responses: + 200: + description: Success + 403: + description: Unauthorized """ - access_token = request.cookies.get("access_token") - if access_token is None: + + try: + set_current_token(validate_request(aud={"user"})) + except Exception: json_to_return = { "error" : "Please log in." } - return flask.jsonify(json_to_return), 403 + return flask.jsonify(json_to_return), 401 + + auth_successful = does_the_user_have_read_access_on_at_least_one_project(current_token) - auth_successful, user_info = is_the_user_permitted_to_use_this_service(access_token) if not auth_successful: json_to_return = { "error" : "You must have read access on at least one project in order to use this feature." } return flask.jsonify(json_to_return), 403 - - folder_name = get_folder_name_from_user_info(user_info) + + folder_name = get_folder_name_from_token(current_token) MANIFEST_BUCKET_NAME = flask.current_app.config.get("MANIFEST_BUCKET_NAME") json_to_return = { @@ -132,34 +161,47 @@ def get_manifests(): def get_manifest_file(file_name): """ Returns the requested manifest file from the user's folder. + --- + responses: + 200: + description: Success + 403: + description: Unauthorized + 400: + description: Bad request format """ - if not file_name.endswith("json"): - json_to_return = { "error" : "Incorrect usage. You can only use this pathway to request files of type JSON." } - return flask.jsonify(json_to_return), 403 - access_token = request.cookies.get("access_token") - if access_token is None: + try: + set_current_token(validate_request(aud={"user"})) + except Exception: json_to_return = { "error" : "Please log in." } - return flask.jsonify(json_to_return), 403 + return flask.jsonify(json_to_return), 401 + + auth_successful = does_the_user_have_read_access_on_at_least_one_project(current_token) - auth_successful, user_info = is_the_user_permitted_to_use_this_service(access_token) if not auth_successful: json_to_return = { "error" : "You must have read access on at least one project in order to use this feature." } return flask.jsonify(json_to_return), 403 - folder_name = get_folder_name_from_user_info(user_info) + if not file_name.endswith("json"): + json_to_return = { "error" : "Incorrect usage. You can only use this pathway to request files of type JSON." } + return flask.jsonify(json_to_return), 400 + + folder_name = get_folder_name_from_token(current_token) MANIFEST_BUCKET_NAME = flask.current_app.config.get("MANIFEST_BUCKET_NAME") json_to_return = { "body" : get_file_contents(MANIFEST_BUCKET_NAME, folder_name, file_name) } - print(json_to_return) - return flask.jsonify(json_to_return), 200 -def add_manifest_to_bucket(user_info, manifest_json): - folder_name = get_folder_name_from_user_info(user_info) +def add_manifest_to_bucket(current_token, manifest_json): + """ + Puts the manifest_json string into a file and uploads it to s3. + Generates and returns the name of the new file. + """ + folder_name = get_folder_name_from_token(current_token) MANIFEST_BUCKET_NAME = flask.current_app.config.get("MANIFEST_BUCKET_NAME") filename = generate_unique_manifest_filename(folder_name, MANIFEST_BUCKET_NAME) @@ -174,14 +216,24 @@ def add_manifest_to_bucket(user_info, manifest_json): def put_manifest(): """ Add manifest to s3 bucket + --- + responses: + 200: + description: Success + 403: + description: Unauthorized + 400: + description: Bad manifest format """ - - access_token = request.cookies.get("access_token") - if access_token is None: + + try: + set_current_token(validate_request(aud={"user"})) + except Exception: json_to_return = { "error" : "Please log in." } - return flask.jsonify(json_to_return), 403 + return flask.jsonify(json_to_return), 401 + + auth_successful = does_the_user_have_read_access_on_at_least_one_project(current_token) - auth_successful, user_info = is_the_user_permitted_to_use_this_service(access_token) if not auth_successful: json_to_return = { "error" : "You must have read access on at least one project in order to use this feature." } return flask.jsonify(json_to_return), 403 @@ -194,7 +246,7 @@ def put_manifest(): if not is_valid: return flask.jsonify({"error" : err}), 400 - filename = add_manifest_to_bucket(user_info, manifest_json) + filename = add_manifest_to_bucket(current_token, manifest_json) ret = { "filename": filename, diff --git a/tests/app_test.py b/tests/app_test.py index b0c540a..edfbb4d 100644 --- a/tests/app_test.py +++ b/tests/app_test.py @@ -1,9 +1,40 @@ import pytest import requests import json as json_utils + from manifest_service import manifests +from manifest_service.api import create_app + +@pytest.fixture +def app(mocker): + test_user = { + 'context': { + 'user': {'policies': ['data_upload', 'programs.test-read-storage', 'programs.test-read'], + 'google': {'proxy_group': None}, + 'is_admin': True, + 'name': 'example@uchicago.edu', + 'projects': + {'test': + ['read-storage', 'read', 'create', 'write-storage', 'upload', 'update', 'delete'] + } + } + }, + 'aud': ['data', 'user', 'fence', 'openid'], + 'sub': '18' + } + + mocker.patch("manifest_service.manifests.validate_request", return_value=test_user) + + app = create_app() + return app + def test_generate_unique_manifest_filename_basic_date_generation(): + """ + Tests that the generate_unique_filename_with_timestamp_and_increment() function + generates a unique filename containing the given timestamp, based on the files in the + user's bucket. + """ timestamp = "a-b-c" users_existing_manifest_files = [] filename = manifests.generate_unique_filename_with_timestamp_and_increment(timestamp, users_existing_manifest_files) @@ -14,7 +45,6 @@ def test_generate_unique_manifest_filename_basic_date_generation(): filename = manifests.generate_unique_filename_with_timestamp_and_increment(timestamp, users_existing_manifest_files) assert filename == "manifest-a-b-c.json" -def test_generate_unique_manifest_filename_if_some_times_are_the_same(): # Case 1: One collision timestamp = "a-b-c" users_existing_manifest_files = ["manifest-a-b-c.json"] @@ -34,24 +64,32 @@ def test_generate_unique_manifest_filename_if_some_times_are_the_same(): assert filename == "manifest-a-b-c-3.json" def test_does_the_user_have_read_access_on_at_least_one_project(): + """ + Tests that the function does_the_user_have_read_access_on_at_least_one_project() + provides the correct value for different arborist user_info inputs. + """ project_access_dict = { } rv = manifests.does_the_user_have_read_access_on_at_least_one_project(project_access_dict) assert rv is False - project_access_dict = {'test' : [ 'read-storage' , 'write-storage', 'read' ], 'DEV' : [] } + project_access_dict = {'context' : { 'user' : { 'projects' : {'test' : [ 'read-storage' , 'write-storage', 'read' ], 'DEV' : [] } } } } rv = manifests.does_the_user_have_read_access_on_at_least_one_project(project_access_dict) assert rv is True - project_access_dict = {'test' : [ 'write-storage', 'read' ] , 'abc123' : ['something', 'something-else'] } + project_access_dict = {'context' : { 'user' : { 'projects' : {'test' : [ 'write-storage', 'read' ] , 'abc123' : ['something', 'something-else'] } } } } rv = manifests.does_the_user_have_read_access_on_at_least_one_project(project_access_dict) assert rv is False # You need both read and read-storage to use this service. - project_access_dict = {'jenkins' : [ 'read' ] , 'abc123' : ['something', 'something-else'] } + project_access_dict = {'context' : { 'user' : { 'projects' : {'jenkins' : [ 'read' ] , 'abc123' : ['something', 'something-else'] } } } } rv = manifests.does_the_user_have_read_access_on_at_least_one_project(project_access_dict) assert rv is False def test_is_valid_manifest(): + """ + Tests that the function is_valid_manifest() correctly determines + if the input manifest string is valid. + """ test_manifest = [{ "foo" : 44 }] is_valid, err_message = manifests.is_valid_manifest(test_manifest) assert is_valid is False @@ -73,92 +111,94 @@ def test_is_valid_manifest(): assert is_valid is True def get_me_an_access_token(api_key, fence_hostname): + """ + Just a helper function that gets an access token for use with Fence + based on the optional api key passed into pytest. This access token is + used to facilitate the test functions that make requests to the manifest_service. + (Without an access token, all requests to the service will come back 403s). + """ data = { 'api_key' : api_key } headers = {'Content-Type': 'application/json', 'Accept':'application/json'} - r = requests.post(fence_hostname + "/user/credentials/api/access_token", json=data, headers=headers) + r = client.post(fence_hostname + "/user/credentials/api/access_token", json=data, headers=headers) json = r.json() return json['access_token'] -@pytest.mark.skip(reason="This is technically an integration test, because it talks to s3 and Fence") -def test_POST_handles_invalid_json(api_key_one, manifest_service_hostname, fence_hostname): - cookies = {'access_token' : get_me_an_access_token(api_key_one, fence_hostname) } - r = requests.post(manifest_service_hostname, data={'a':1}, cookies=cookies) +def test_POST_handles_invalid_json(client): + r = client.post("/", data={'a':1}) assert r.status_code == 400 -@pytest.mark.skip(reason="Integration test, because it talks to s3 and Fence") -def test_POST_handles_invalid_manifest_keys(api_key_one, manifest_service_hostname, fence_hostname): +def test_POST_handles_invalid_manifest_keys(client): test_manifest = [{ 'foo' : 44 , "object_id" : 88 }] headers = {'Content-Type': 'application/json', 'Accept':'application/json'} - cookies = {'access_token' : get_me_an_access_token(api_key_one, fence_hostname) } - r = requests.post(manifest_service_hostname, json=test_manifest, headers=headers, cookies=cookies) + r = client.post("/", json=test_manifest, headers=headers) assert r.status_code == 400 -@pytest.mark.skip(reason="Integration test, because it talks to s3 and Fence") -def test_POST_successful_manifest_upload(api_key_one, manifest_service_hostname, fence_hostname): +@pytest.mark.skip(reason="This is an integration test because it requires s3 credentials. I would rather keep this good test intact than mock s3.") +def test_POST_successful_manifest_upload(client): import random random_nums = [ random.randint(1,101) , random.randint(1,101) , random.randint(1,101) , random.randint(1,101) ] test_manifest = [{ "subject_id" : random_nums[0] , "object_id" : random_nums[1] }, { "subject_id" : random_nums[2] , "object_id" : random_nums[3] }] headers = {'Content-Type': 'application/json', 'Accept':'application/json'} - cookies = {'access_token' : get_me_an_access_token(api_key_one, fence_hostname) } - r = requests.post(manifest_service_hostname, json=test_manifest, headers=headers, cookies=cookies) + r = client.post("/", data=json_utils.dumps(test_manifest), headers=headers) + print(r.data) assert r.status_code == 200 - json = r.json() + json = r.json new_filename = json['filename'] assert new_filename is not None assert type(new_filename) is str assert len(new_filename) > 1 # Check that the new manifest is in the bucket - r = requests.get(manifest_service_hostname, headers=headers, cookies=cookies) + r = client.get("/", headers=headers) assert r.status_code == 200 - json = r.json() + json = r.json manifest_files = json['manifests'] assert type(manifest_files) is list assert len(manifest_files) > 0 assert new_filename in manifest_files # Read the body of the manifest and make sure the contents is what we posted - r = requests.get(manifest_service_hostname + "/file/" + new_filename, headers=headers, cookies=cookies) + r = client.get("/file/" + new_filename, headers=headers) assert r.status_code == 200 - json = r.json() + json = r.json response_manifest = json_utils.loads(json['body']) assert response_manifest == test_manifest -@pytest.mark.skip(reason="") -def test_GET_fails_if_access_token_missing_or_invalid(manifest_service_hostname, fence_hostname): +@pytest.mark.skip(reason="This test is difficult to automate and will be tested by an integration test anyway.") +def test_GET_fails_if_access_token_missing_or_invalid(manifest_service_hostname): headers = {'Content-Type': 'application/json', 'Accept':'application/json'} cookies = {} - r = requests.get(manifest_service_hostname, headers=headers, cookies=cookies) + r = client.get("/", headers=headers) assert r.status_code == 403 cookies = {'access_token' : 'abc'} - r = requests.get(manifest_service_hostname, headers=headers, cookies=cookies) + r = client.get("/", headers=headers) assert r.status_code == 403 -@pytest.mark.skip(reason="") -def test_POST_fails_if_access_token_missing_or_invalid(manifest_service_hostname, fence_hostname): +@pytest.mark.skip(reason="This test is difficult to automate and will be tested by an integration test anyway.") +def test_POST_fails_if_access_token_missing_or_invalid(client): headers = {'Content-Type': 'application/json', 'Accept':'application/json'} test_manifest = [{ "subject_id" : 44 , "object_id" : 88 }] cookies = {} - r = requests.post(manifest_service_hostname, json=test_manifest, headers=headers, cookies=cookies) + r = client.post("/", json=test_manifest, headers=headers, cookies=cookies) assert r.status_code == 403 cookies = {'access_token' : 'abc'} - r = requests.post(manifest_service_hostname, json=test_manifest, headers=headers, cookies=cookies) + r = client.post("/", json=test_manifest, headers=headers, cookies=cookies) assert r.status_code == 403 -@pytest.mark.skip(reason="Integration test, because it talks to s3 and Fence") -def test_folder_creation_with_multiple_users(api_key_one, api_key_two, manifest_service_hostname, fence_hostname): +@pytest.mark.skip(reason="This test is difficult to automate and will be tested by an integration test anyway.") +def test_folder_creation_with_multiple_users(client, api_key_one, api_key_two, fence_hostname): """ In particular, users should never see each others' manifests. Or even know that we're making folders for them. @@ -169,25 +209,25 @@ def test_folder_creation_with_multiple_users(api_key_one, api_key_two, manifest_ # User 1 POST cookies_user_1 = {'access_token' : get_me_an_access_token(api_key_one, fence_hostname) } - r = requests.post(manifest_service_hostname, json=test_manifest, headers=headers, cookies=cookies_user_1) + r = client.post("/", json=test_manifest, headers=headers, cookies=cookies_user_1) json = r.json() user_1_filename = json['filename'] # User 2 POST -- notice that we're using the other api_key (from another account) cookies_user_2 = {'access_token' : get_me_an_access_token(api_key_two, fence_hostname) } - r = requests.post(manifest_service_hostname, json=test_manifest, headers=headers, cookies=cookies_user_2) + r = client.post("/", json=test_manifest, headers=headers, cookies=cookies_user_2) json = r.json() user_2_filename = json['filename'] # User 1 GET - r = requests.get(manifest_service_hostname, headers=headers, cookies=cookies_user_1) + r = client.get("/", headers=headers, cookies=cookies_user_1) json = r.json() manifest_files = json['manifests'] assert user_1_filename in manifest_files assert user_2_filename not in manifest_files # User 2 GET - r = requests.get(manifest_service_hostname, headers=headers, cookies=cookies_user_2) + r = client.get("/", headers=headers, cookies=cookies_user_2) json = r.json() manifest_files = json['manifests'] assert user_2_filename in manifest_files diff --git a/tests/conftest.py b/tests/conftest.py index 23dcce3..78e7bb3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,11 +1,11 @@ import pytest -from manifest_service.api import app as service_app, app_init - +from manifest_service.api import app as service_app @pytest.fixture(scope="session") def app(): # load configuration # service_app.config.from_object('manifest_service.test_settings') - app_init(service_app) - return service_app \ No newline at end of file + #app_init(service_app) + return service_app +