diff --git a/Pipfile b/Pipfile index 2e65c23..210ffbc 100644 --- a/Pipfile +++ b/Pipfile @@ -11,6 +11,8 @@ flask-api = "*" pyjwt = "*" flask-jwt-extended = "*" flask-cors = "*" +flask-restx = "*" +sqlalchemy = "*" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index eec23d8..bb9aef4 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "669b22bb2a66c8f60df267735d00d669d090508392aa6a13af139f18ecffba67" + "sha256": "d32cc66979cc6c5146b7607b4d44ecf5aca0c98e863c8f3c4ce33ef9e5246458" }, "pipfile-spec": 6, "requires": { @@ -16,12 +16,29 @@ ] }, "default": { + "aniso8601": { + "hashes": [ + "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f", + "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973" + ], + "markers": "python_version >= '3.5'", + "version": "==9.0.1" + }, + "attrs": { + "hashes": [ + "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4", + "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==21.4.0" + }, "certifi": { "hashes": [ - "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872", - "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569" + "sha256:9c5705e395cd70084351dd8ad5c41e65655e08ce46f2ec9cf6c2c08390f71eb7", + "sha256:f1d53542ee8cbedbe2118b5686372fb33c297fcd6379b050cca0ef13a597382a" ], - "version": "==2021.10.8" + "markers": "python_version >= '3.6'", + "version": "==2022.5.18.1" }, "charset-normalizer": { "hashes": [ @@ -71,6 +88,75 @@ "index": "pypi", "version": "==4.4.0" }, + "flask-restx": { + "hashes": [ + "sha256:63c69a61999a34f1774eaccc6fc8c7f504b1aad7d56a8ec672264e52d9ac05f4", + "sha256:96157547acaa8892adcefd8c60abf9040212ac2a8634937a82946e07b46147fd" + ], + "index": "pypi", + "version": "==0.5.1" + }, + "greenlet": { + "hashes": [ + "sha256:0051c6f1f27cb756ffc0ffbac7d2cd48cb0362ac1736871399a739b2885134d3", + "sha256:00e44c8afdbe5467e4f7b5851be223be68adb4272f44696ee71fe46b7036a711", + "sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd", + "sha256:049fe7579230e44daef03a259faa24511d10ebfa44f69411d99e6a184fe68073", + "sha256:14d4f3cd4e8b524ae9b8aa567858beed70c392fdec26dbdb0a8a418392e71708", + "sha256:166eac03e48784a6a6e0e5f041cfebb1ab400b394db188c48b3a84737f505b67", + "sha256:17ff94e7a83aa8671a25bf5b59326ec26da379ace2ebc4411d690d80a7fbcf23", + "sha256:1e12bdc622676ce47ae9abbf455c189e442afdde8818d9da983085df6312e7a1", + "sha256:21915eb821a6b3d9d8eefdaf57d6c345b970ad722f856cd71739493ce003ad08", + "sha256:288c6a76705dc54fba69fbcb59904ae4ad768b4c768839b8ca5fdadec6dd8cfd", + "sha256:2bde6792f313f4e918caabc46532aa64aa27a0db05d75b20edfc5c6f46479de2", + "sha256:32ca72bbc673adbcfecb935bb3fb1b74e663d10a4b241aaa2f5a75fe1d1f90aa", + "sha256:356b3576ad078c89a6107caa9c50cc14e98e3a6c4874a37c3e0273e4baf33de8", + "sha256:40b951f601af999a8bf2ce8c71e8aaa4e8c6f78ff8afae7b808aae2dc50d4c40", + "sha256:572e1787d1460da79590bf44304abbc0a2da944ea64ec549188fa84d89bba7ab", + "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6", + "sha256:64e6175c2e53195278d7388c454e0b30997573f3f4bd63697f88d855f7a6a1fc", + "sha256:7227b47e73dedaa513cdebb98469705ef0d66eb5a1250144468e9c3097d6b59b", + "sha256:7418b6bfc7fe3331541b84bb2141c9baf1ec7132a7ecd9f375912eca810e714e", + "sha256:7cbd7574ce8e138bda9df4efc6bf2ab8572c9aff640d8ecfece1b006b68da963", + "sha256:7ff61ff178250f9bb3cd89752df0f1dd0e27316a8bd1465351652b1b4a4cdfd3", + "sha256:833e1551925ed51e6b44c800e71e77dacd7e49181fdc9ac9a0bf3714d515785d", + "sha256:8639cadfda96737427330a094476d4c7a56ac03de7265622fcf4cfe57c8ae18d", + "sha256:8c5d5b35f789a030ebb95bff352f1d27a93d81069f2adb3182d99882e095cefe", + "sha256:8c790abda465726cfb8bb08bd4ca9a5d0a7bd77c7ac1ca1b839ad823b948ea28", + "sha256:8d2f1fb53a421b410751887eb4ff21386d119ef9cde3797bf5e7ed49fb51a3b3", + "sha256:903bbd302a2378f984aef528f76d4c9b1748f318fe1294961c072bdc7f2ffa3e", + "sha256:93f81b134a165cc17123626ab8da2e30c0455441d4ab5576eed73a64c025b25c", + "sha256:95e69877983ea39b7303570fa6760f81a3eec23d0e3ab2021b7144b94d06202d", + "sha256:9633b3034d3d901f0a46b7939f8c4d64427dfba6bbc5a36b1a67364cf148a1b0", + "sha256:97e5306482182170ade15c4b0d8386ded995a07d7cc2ca8f27958d34d6736497", + "sha256:9f3cba480d3deb69f6ee2c1825060177a22c7826431458c697df88e6aeb3caee", + "sha256:aa5b467f15e78b82257319aebc78dd2915e4c1436c3c0d1ad6f53e47ba6e2713", + "sha256:abb7a75ed8b968f3061327c433a0fbd17b729947b400747c334a9c29a9af6c58", + "sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a", + "sha256:b11548073a2213d950c3f671aa88e6f83cda6e2fb97a8b6317b1b5b33d850e06", + "sha256:b1692f7d6bc45e3200844be0dba153612103db241691088626a33ff1f24a0d88", + "sha256:b336501a05e13b616ef81ce329c0e09ac5ed8c732d9ba7e3e983fcc1a9e86965", + "sha256:b8c008de9d0daba7b6666aa5bbfdc23dcd78cafc33997c9b7741ff6353bafb7f", + "sha256:b92e29e58bef6d9cfd340c72b04d74c4b4e9f70c9fa7c78b674d1fec18896dc4", + "sha256:be5f425ff1f5f4b3c1e33ad64ab994eed12fc284a6ea71c5243fd564502ecbe5", + "sha256:dd0b1e9e891f69e7675ba5c92e28b90eaa045f6ab134ffe70b52e948aa175b3c", + "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a", + "sha256:e6a36bb9474218c7a5b27ae476035497a6990e21d04c279884eb10d9b290f1b1", + "sha256:e859fcb4cbe93504ea18008d1df98dee4f7766db66c435e4882ab35cf70cac43", + "sha256:eb6ea6da4c787111adf40f697b4e58732ee0942b5d3bd8f435277643329ba627", + "sha256:ec8c433b3ab0419100bd45b47c9c8551248a5aee30ca5e9d399a0b57ac04651b", + "sha256:eff9d20417ff9dcb0d25e2defc2574d10b491bf2e693b4e491914738b7908168", + "sha256:f0214eb2a23b85528310dad848ad2ac58e735612929c8072f6093f3585fd342d", + "sha256:f276df9830dba7a333544bd41070e8175762a7ac20350786b322b714b0e654f5", + "sha256:f3acda1924472472ddd60c29e5b9db0cec629fbe3c5c5accb74d6d6d14773478", + "sha256:f70a9e237bb792c7cc7e44c531fd48f5897961701cdaa06cf22fc14965c496cf", + "sha256:f9d29ca8a77117315101425ec7ec2a47a22ccf59f5593378fc4077ac5b754fce", + "sha256:fa877ca7f6b48054f847b61d6fa7bed5cebb663ebc55e018fda12db09dcc664c", + "sha256:fdcec0b8399108577ec290f55551d926d9a1fa6cad45882093a7a07ac5ec147b" + ], + "markers": "python_version >= '3' and platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))", + "version": "==1.1.2" + }, "idna": { "hashes": [ "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", @@ -103,6 +189,14 @@ "markers": "python_version >= '3.7'", "version": "==3.1.2" }, + "jsonschema": { + "hashes": [ + "sha256:71b5e39324422543546572954ce71c67728922c104902cb7ce252e522235b33f", + "sha256:7c6d882619340c3347a1bf7315e147e6d3dae439033ae6383d6acb908c101dfc" + ], + "markers": "python_version >= '3.7'", + "version": "==4.5.1" + }, "mariadb": { "hashes": [ "sha256:166973d6cd7da5d4fe84fc9d63f8d219a660ed2c82ebf6acadc9b3dd811f51bc", @@ -166,11 +260,45 @@ }, "pyjwt": { "hashes": [ - "sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41", - "sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f" + "sha256:72d1d253f32dbd4f5c88eaf1fdc62f3a19f676ccbadb9dbc5d07e951b2b26daf", + "sha256:d42908208c699b3b973cbeb01a969ba6a96c821eefb1c5bfe4c390c01d67abba" ], "index": "pypi", - "version": "==2.3.0" + "version": "==2.4.0" + }, + "pyrsistent": { + "hashes": [ + "sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c", + "sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc", + "sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e", + "sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26", + "sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec", + "sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286", + "sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045", + "sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec", + "sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8", + "sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c", + "sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca", + "sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22", + "sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a", + "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96", + "sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc", + "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1", + "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07", + "sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6", + "sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b", + "sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5", + "sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6" + ], + "markers": "python_version >= '3.7'", + "version": "==0.18.1" + }, + "pytz": { + "hashes": [ + "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7", + "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c" + ], + "version": "==2022.1" }, "requests": { "hashes": [ @@ -188,6 +316,48 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.16.0" }, + "sqlalchemy": { + "hashes": [ + "sha256:09c606d8238feae2f360b8742ffbe67741937eb0a05b57f536948d198a3def96", + "sha256:166a3887ec355f7d2f12738f7fa25dc8ac541867147a255f790f2f41f614cb44", + "sha256:16abf35af37a3d5af92725fc9ec507dd9e9183d261c2069b6606d60981ed1c6e", + "sha256:2e885548da361aa3f8a9433db4cfb335b2107e533bf314359ae3952821d84b3e", + "sha256:2ec89bf98cc6a0f5d1e28e3ad28e9be6f3b4bdbd521a4053c7ae8d5e1289a8a1", + "sha256:2ecac4db8c1aa4a269f5829df7e706639a24b780d2ac46b3e485cbbd27ec0028", + "sha256:316c7e5304dda3e3ad711569ac5d02698bbc71299b168ac56a7076b86259f7ea", + "sha256:5041474dcab7973baa91ec1f3112049a9dd4652898d6a95a6a895ff5c58beb6b", + "sha256:53d2d9ee93970c969bc4e3c78b1277d7129554642f6ffea039c282c7dc4577bc", + "sha256:5864a83bd345871ad9699ce466388f836db7572003d67d9392a71998092210e3", + "sha256:5c90ef955d429966d84326d772eb34333178737ebb669845f1d529eb00c75e72", + "sha256:5d50cb71c1dbed70646d521a0975fb0f92b7c3f84c61fa59e07be23a1aaeecfc", + "sha256:64678ac321d64a45901ef2e24725ec5e783f1f4a588305e196431447e7ace243", + "sha256:64d796e9af522162f7f2bf7a3c5531a0a550764c426782797bbeed809d0646c5", + "sha256:6cb4c4f57a20710cea277edf720d249d514e587f796b75785ad2c25e1c0fed26", + "sha256:6e1fe00ee85c768807f2a139b83469c1e52a9ffd58a6eb51aa7aeb524325ab18", + "sha256:6e859fa96605027bd50d8e966db1c4e1b03e7b3267abbc4b89ae658c99393c58", + "sha256:7a052bd9f53004f8993c624c452dfad8ec600f572dd0ed0445fbe64b22f5570e", + "sha256:81e53bd383c2c33de9d578bfcc243f559bd3801a0e57f2bcc9a943c790662e0c", + "sha256:83cf3077712be9f65c9aaa0b5bc47bc1a44789fd45053e2e3ecd59ff17c63fe9", + "sha256:8b20c4178ead9bc398be479428568ff31b6c296eb22e75776273781a6551973f", + "sha256:8d07fe2de0325d06e7e73281e9a9b5e259fbd7cbfbe398a0433cbb0082ad8fa7", + "sha256:a0ae3aa2e86a4613f2d4c49eb7da23da536e6ce80b2bfd60bbb2f55fc02b0b32", + "sha256:af2587ae11400157753115612d6c6ad255143efba791406ad8a0cbcccf2edcb3", + "sha256:b3db741beaa983d4cbf9087558620e7787106319f7e63a066990a70657dd6b35", + "sha256:be094460930087e50fd08297db9d7aadaed8408ad896baf758e9190c335632da", + "sha256:cb441ca461bf97d00877b607f132772644b623518b39ced54da433215adce691", + "sha256:ce20f5da141f8af26c123ebaa1b7771835ca6c161225ce728962a79054f528c3", + "sha256:d57ac32f8dc731fddeb6f5d1358b4ca5456e72594e664769f0a9163f13df2a31", + "sha256:dce3468bf1fc12374a1a732c9efd146ce034f91bb0482b602a9311cb6166a920", + "sha256:e12532c4d3f614678623da5d852f038ace1f01869b89f003ed6fe8c793f0c6a3", + "sha256:e74ce103b81c375c3853b436297952ef8d7863d801dcffb6728d01544e5191b5", + "sha256:f0394a3acfb8925db178f7728adb38c027ed7e303665b225906bfa8099dc1ce8", + "sha256:f522214f6749bc073262529c056f7dfd660f3b5ec4180c5354d985eb7219801e", + "sha256:fbf8c09fe9728168f8cc1b40c239eab10baf9c422c18be7f53213d70434dea43", + "sha256:fca8322e04b2dde722fcb0558682740eebd3bd239bea7a0d0febbc190e99dc15" + ], + "index": "pypi", + "version": "==1.4.36" + }, "urllib3": { "hashes": [ "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14", diff --git a/log/U10Manager.log b/log/U10Manager.log new file mode 100644 index 0000000..f4e3f92 --- /dev/null +++ b/log/U10Manager.log @@ -0,0 +1,77 @@ +2022-05-19 16:41:16,281 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 16:41:16,285 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 16:42:50,490 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 16:42:50,502 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 16:46:54,185 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 16:46:54,195 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 16:46:54,345 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 16:47:33,809 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 16:47:33,812 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 16:47:33,957 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:42:07,368 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:42:07,395 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:42:07,489 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:42:39,145 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:42:39,152 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:42:39,251 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:44:48,956 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:44:48,959 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:44:48,981 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:44:57,140 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:47:37,823 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:47:37,834 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:48:03,411 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:48:03,415 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:48:03,443 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:48:03,448 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:49:04,618 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:49:04,619 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:49:04,659 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:49:04,660 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:50:56,568 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:50:56,574 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:50:56,592 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:50:56,595 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:50:59,020 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:50:59,026 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:51:23,530 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:51:23,532 - U10Manager [views.login:109] - ERROR - authorization failed ! +2022-05-19 19:51:23,532 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:51:33,784 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:51:33,787 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:51:33,918 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:51:33,922 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:51:36,908 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:51:36,909 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:51:36,929 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:51:36,932 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:51:47,744 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:51:47,747 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:51:47,913 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:51:47,916 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:51:50,345 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:51:50,347 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:51:50,369 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:51:50,372 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:51:52,796 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:51:52,798 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:52:13,041 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:52:13,100 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:52:22,270 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 19:52:22,274 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 19:52:22,375 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 20:11:24,554 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 20:11:24,606 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-19 20:11:31,885 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-19 20:11:31,886 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-20 09:01:52,314 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-20 09:01:52,322 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-20 09:01:52,485 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-20 09:01:52,496 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-20 09:04:13,524 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-20 09:04:13,541 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-20 09:04:13,743 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-20 09:04:13,745 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données +2022-05-20 09:21:42,458 - U10Manager [db.connect:95] - INFO - Connexion à la base de données +2022-05-20 09:21:42,465 - U10Manager [views.post:89] - ERROR - Login and Password required ! +2022-05-20 09:21:42,465 - U10Manager [db.disconnect:112] - INFO - Déconnexion de la base de données diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/run.py b/run.py new file mode 100644 index 0000000..3fdbb43 --- /dev/null +++ b/run.py @@ -0,0 +1,9 @@ +# -*- encoding: utf-8 -*- + +# @author : vincent.benoit@benserv.fr +# @brief : U10Manager Flask RESTful API + +from src import app +print("Launch Flask RESTful API Backend ...") +application = app.create_app() +application.run(host="0.0.0.0") diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..cf76179 --- /dev/null +++ b/setup.py @@ -0,0 +1,12 @@ +from setuptools import find_packages, setup + +setup( + name='U10Manager', + version='1.0.0', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + install_requires=[ + 'flask', + ], +) diff --git a/src/__init__.py b/src/__init__.py index e69de29..e1c48ff 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -0,0 +1,4 @@ +# -*- encoding: utf-8 -*- + +# @author : vincent.benoit@benserv.fr +# @brief : U10Manager Flask RESTful API diff --git a/src/app.py b/src/app.py new file mode 100644 index 0000000..e9f0972 --- /dev/null +++ b/src/app.py @@ -0,0 +1,104 @@ +# -*- encoding: utf-8 -*- + +# @author : vincent.benoit@benserv.fr +# @brief : U10Manager Flask RESTful API + +######################################################### +# Importation de modules externes # + +import sys, re, os +from pprint import pprint +import logging as log +from logging.config import dictConfig + +from flask import Flask +from flask.logging import default_handler +from flask_cors import CORS, cross_origin +from flask_jwt_extended import JWTManager + +import jwt + +from src.config import DefaultConfig +from src.db import db +from src.users import users +from src.auth import auth + +######################################################### +# Corps principal du programme # + +def create_app(config=None, app_name=None): + ''' create and configure the app ''' + print("Create and configure the Flask app ...") + if app_name is None: + app_name = DefaultConfig.PROJECT + + # create the app + # tells the app that configuration files are relative to the instance folder. + app = Flask(app_name, instance_path=os.getcwd(), instance_relative_config=True) + + configure_app(app, config) + configure_log(app) + configure_database(app) + configure_blueprints(app) + + return app + +def configure_app(app, config=None): + ''' configure the app with conf file and/or object ''' + # load default config + app.config.from_object(DefaultConfig) + + if config: + # load specific config + app.config.from_object(config) + + # setup the Flask-JWT-Extended extension + jwt = JWTManager(app) + # setup the Flask-CORS extension for handling Cross Origin Resource Sharing + CORS(app, resources={r"/api/*": { + "origins": ["http://localhost:4200","http://localhost:5000"], + "supports_credentials": True + }}) + +def configure_log(app): + ''' configure log handler ''' + if not os.path.exists(app.config['LOG_FOLDER']): + try: + os.makedirs(app.config['LOG_FOLDER']) + except OSError: + pass + + # On vire tous les handlers + for h in app.logger.handlers: + app.logger.removeHandler(h) + + # Set info level on logger, which might be overwritten by handers. + # Suppress DEBUG messages. + app.logger.setLevel(log.DEBUG) + + formatter = log.Formatter('%(asctime)s - %(name)s [%(module)s.%(funcName)s:%(lineno)d] - %(levelname)s - %(message)s') + info_log = os.path.join(app.config['LOG_FOLDER'], DefaultConfig.PROJECT + '.log') + info_file_handler = log.handlers.RotatingFileHandler(info_log, maxBytes=100000, backupCount=10) + info_file_handler.setLevel(log.INFO) + info_file_handler.setFormatter(formatter) + app.logger.addHandler(info_file_handler) + + fl = log.StreamHandler() + fl.setLevel(log.DEBUG) + fl.setFormatter(formatter) + app.logger.addHandler(fl) + +def configure_database(app): + ''' configure database parameters ''' + # Set database parameters ... + db.host = app.config['SQL_HOST_URI'] + db.port = app.config['SQL_PORT'] + db.user = app.config['SQL_USERNAME'] + db.password = app.config['SQL_PASSWORD'] + db.database = app.config['SQL_DATABASE'] + +def configure_blueprints(app): + ''' configure blueprints ''' + for bp in [users, auth]: + app.register_blueprint(bp) + diff --git a/src/auth/__init__.py b/src/auth/__init__.py new file mode 100644 index 0000000..ad24b2b --- /dev/null +++ b/src/auth/__init__.py @@ -0,0 +1,6 @@ +# -*- encoding: utf-8 -*- + +# @author : vincent.benoit@benserv.fr +# @brief : Users views and models + +from .views import auth diff --git a/src/auth/views.py b/src/auth/views.py new file mode 100644 index 0000000..27c3746 --- /dev/null +++ b/src/auth/views.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +# -*- encoding: utf-8 -*- + +# @author : vincent.benoit@benserv.fr +# @brief : Auth routes + +######################################################### +# Importation de modules externes # + +import sys, re, os +import logging as log + +from flask import Flask, Blueprint, request, abort, jsonify, current_app +from flask_api import status +from flask_jwt_extended import create_access_token +from flask_jwt_extended import get_jwt +from flask_jwt_extended import set_access_cookies +from flask_jwt_extended import unset_jwt_cookies +from flask_jwt_extended import get_jwt_identity +from flask_jwt_extended import jwt_required +from flask_restx import Api, Resource, reqparse + +import json + +from werkzeug.security import check_password_hash +from werkzeug.exceptions import HTTPException + +from src.db import db, dbmanage + +######################################################### +# Class et Methods # + +auth = Blueprint('auth', __name__, url_prefix='/api/utilisateurs') +api = Api(auth, + version="1.0", + title="U10Manager Flask RESTful API", + description="Welcome to the Swagger UI documentation site!", + doc="/ui") + +@auth.errorhandler(HTTPException) +def handle_exception(e): + ''' return JSON instead of HTML for HTTP errors ''' + response = e.get_response() + # replace the body with JSON + response.data = json.dumps({ + 'code': e.code, + 'name': e.name, + 'description': e.description, + }) + response.content_type = "application/json" + return response + +@auth.after_request +def refresh_expiring_tokens(response): + ''' Using an 'after_request' callback, we refresh any token that is within + 30 minutes of expiring.''' + try: + exp_timestamp = get_jwt()['exp'] + now = datetime.datetime.now(datetime.timezone.utc) + target_timestamp = datetime.datetime.timestamp(now + datetime.timedelta(minutes=30)) + ### DEBUG ### + current_app.logger.debug("exp: {} - target: {}".format(exp_timestamp, target_timestamp)) + ### END DEBUG ### + if target_timestamp > exp_timestamp: + current_app.logger.warning("On doit recréer un token ....") + access_token = create_access_token(identity=get_jwt_identity()) + set_access_cookies(response, access_token) + return response + except (RuntimeError, KeyError): + return response + +@auth.route('/login', methods=['POST']) +def login(): + ### DEBUG ### + current_app.logger.debug("Request : {}".format(request)) + current_app.logger.debug("Auth {}".format(request.authorization)) + ### END DEBUG ### + auth = request.authorization + user = None + if not auth or not auth.username or not auth.password: + current_app.logger.error("Login and Password required !") + db.disconnect() + abort(401, description='Login and Password required') + + # On vérifie que l'utilisateur existe en base de données + sql_statement = "SELECT * FROM utilisateur WHERE Identifiant = \"{}\"".format(auth.username) + # execution de la requete sql + etat, ret = db.execute(sql_statement, None, False) + if not etat: + db.disconnect() + abort(500) + else: + if not ret: + db.disconnect() + abort(404) + else: + user = ret[0] + + ### DEBUG ### + current_app.logger.debug("user bdd: {}".format(user)) + ### END DEBUG ### + if user and user['Actif'] and check_password_hash(user['Password'], auth.password): + try: + # create a new access token + token = create_access_token(identity=user) + content = jsonify({'message': 'login successful !'}) + # set token as cookie + set_access_cookies(content, token) + except Exception as e: + current_app.logger.erreur('Erreur : {}'.format(e)) + db.disconnect() + abort(500) + return content + current_app.logger.error("authorization failed !") + db.disconnect() + abort(401) + +@auth.route('/logout', methods=['POST']) +def logout(): + """ Handles HTTP requests to URL: /api/utilisateurs/logout """ + content = jsonify({'message': 'logout successful !'}) + unset_jwt_cookies(content) + return content diff --git a/src/config.py b/src/config.py new file mode 100644 index 0000000..b10758a --- /dev/null +++ b/src/config.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# +# @author: vincent.benoit@benserv.fr +# @brief: Flask Config classes + +import os +import datetime + +class BaseConfig(object): + PROJECT = "U10Manager" + + # Get app root path, also can use flask.root_path. + PROJECT_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + + DEBUG = False + TESTING = False + + ADMINS = ['vincent.benoit@benserv.fr'] + + LOG_FOLDER = os.path.join(os.getcwd(), 'log') + +class DefaultConfig(BaseConfig): + DEBUG = True + TESTING = True + FLASK_ENV = 'development' + + SECRET_KEY = "thisissecret" + + # Setup the Flask-JWT-Extended extension + JWT_SECRET_KEY = "cdscjdsklcfqezffhrevneqggfuhmnvqnmh" + JWT_COOKIE_SECURE = False + JWT_TOKEN_LOCATION = ["cookies"] + JWT_ACCESS_TOKEN_EXPIRES = datetime.timedelta(hours=1) + # Controls if Cross Site Request Forgery (CSRF) protection is enabled when using cookies + # This should always be True in production + JWT_COOKIE_CSRF_PROTECT = True + JWT_CSRF_IN_COOKIES = True + + UPLOAD_FOLDER = os.path.join(os.getcwd(),'static/img') + ALLOWED_EXTENSIONS = {'png', 'jpg', 'gif', 'jpeg'} + MAX_CONTENT_LENGTH = 1 * 1024 * 1024 # 1 megabytes + + SQL_HOST_URI = '127.0.0.1' + SQL_PORT = 3306 + SQL_USERNAME = 'vincent' + SQL_PASSWORD = 'malkavian' + SQL_DATABASE = 'test1' diff --git a/src/db.py b/src/db.py new file mode 100644 index 0000000..e4b8c4b --- /dev/null +++ b/src/db.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 +# -*- encoding: utf-8 -*- + +# @author : vincent.benoit@benserv.fr +# @brief : wrapper de la base de données mariadb + +######################################################### +# Importation de modules externes # + +import sys, re, os +import logging as log + +import mariadb + +from flask_api import status +from flask import current_app + +from functools import wraps +from src.config import DefaultConfig + +######################################################### +# Class et Methods # + +class BDDsql: + _conf = { + 'host': '', + 'port': 0, + 'user': '', + 'password': '', + 'database': '' + } + + def __init__(self, host="0.0.0.0", port=3306, user="user1", password="pass1", database="db1"): + ''' Constructor ''' + self._conf['host']=host + self._conf['port']=port + self._conf['user']=user + self._conf['password']=password + self._conf['database']=database + self.connected = False + self.logger = log.getLogger(DefaultConfig.PROJECT) + + @property + def host(self): + ''' getter host parameter ''' + return self._conf['host'] + + @host.setter + def host(self, host=""): + ''' setter host parameter ''' + self._conf['host'] = host + + @property + def port(self): + ''' getter port parameter ''' + return self._conf['port'] + + @port.setter + def port(self, port=0): + ''' setter port parameter ''' + self._conf['port'] = port + + @property + def user(self): + ''' getter user parameter ''' + return self._conf['user'] + + @user.setter + def user(self, user=""): + ''' setter user parameter''' + self._conf['user'] = user + + @property + def password(self): + ''' getter password parameter ''' + return self._conf['password'] + + @password.setter + def password(self, password=""): + ''' setter password parameter ''' + self._conf['password'] = password + + @property + def database(self): + ''' getter database parameter ''' + return self._conf['database'] + + @database.setter + def database(self, database=""): + ''' setter database parameter ''' + self._conf['database'] = database + + def connect(self): + ''' connect to database ''' + self.logger.info("Connexion à la base de données") + self.conn = None + self.cursor = None + try: + # connection for MariaDB + self.conn = mariadb.connect(**self._conf) + # create a connection cursor + self.cursor = self.conn.cursor() + self.connected = True + except mariadb.Error as e: + self.logger.error("Error connecting to MariaDB Platform: {}".format(e)) + content = {'error': str(e)} + return False, content + return True, {} + + def disconnect(self): + ''' disconnect from database ''' + self.logger.info("Déconnexion de la base de données") + self.conn.close() + self.connected = False + return + + def execute(self, sql_statement="", data=None, commit=False): + ''' execute SQL statement and retreive datas if necessary ''' + json_data=[] + try: + # execute a SQL statement + ### DEBUG ### + self.logger.debug("statement: {}".format(sql_statement)) + ### END DEBUG ### + if not data: + self.cursor.execute(sql_statement) + else: + self.cursor.execute(sql_statement, data) + if commit: + self.conn.commit() + except mariadb.Error as e: + self.logger.error("Error: {}".format(e)) + content = {'error': str(e)} + return False, content + + if not commit: + # serialize results into JSON + row_headers=[x[0] for x in self.cursor.description] + rv = self.cursor.fetchall() + ### DEBUG ### + self.logger.debug("description: {} - datas: {}".format(self.cursor.description, rv)) + ### END DEBUG ### + for result in rv: + json_data.append(dict(zip(row_headers,result))) + return True, json_data + +######################################################### +# Decorators # + +def dbmanage(func): + ''' decorateur de la fonction db.connect & db.disconnect ''' + @wraps(func) + def wrapper(*args, **kwargs): + # connexion à la base de données + state, ret = db.connect() + if not state: + content = {'Erreur' : ret['error']} + return content, status.HTTP_503_SERVICE_UNAVAILABLE + else: + # Appel de la fonction + ret = func(*args, **kwargs) + # deconnexion de la base de données + db.disconnect() + return ret + return wrapper + +# Instantiate database +db = BDDsql() diff --git a/src/decorators.py b/src/decorators.py new file mode 100644 index 0000000..e1c48ff --- /dev/null +++ b/src/decorators.py @@ -0,0 +1,4 @@ +# -*- encoding: utf-8 -*- + +# @author : vincent.benoit@benserv.fr +# @brief : U10Manager Flask RESTful API diff --git a/src/schema.sql b/src/schema.sql new file mode 100644 index 0000000..e69de29 diff --git a/src/users/__init__.py b/src/users/__init__.py new file mode 100644 index 0000000..ed37c4a --- /dev/null +++ b/src/users/__init__.py @@ -0,0 +1,6 @@ +# -*- encoding: utf-8 -*- + +# @author : vincent.benoit@benserv.fr +# @brief : Users views and models + +from .views import users diff --git a/src/main.py b/src/users/views.py similarity index 59% rename from src/main.py rename to src/users/views.py index f6a8fd0..50db78b 100644 --- a/src/main.py +++ b/src/users/views.py @@ -1,20 +1,17 @@ -#!/usr/bin/env python3 # -*- encoding: utf-8 -*- # @author : vincent.benoit@benserv.fr -# @brief : Test du token JWT avec Flask +# @brief : Users routes ######################################################### # Importation de modules externes # import sys, re, os import logging as log -from logging.config import dictConfig -from pprint import pprint +import json +import datetime -from flask import Flask, request, abort, jsonify, render_template, make_response, send_file -from flask_cors import CORS, cross_origin -from flask.logging import default_handler +from flask import Flask, Blueprint, request, abort, jsonify, send_file, current_app from flask_api import status from flask_jwt_extended import create_access_token from flask_jwt_extended import get_jwt @@ -22,142 +19,19 @@ from flask_jwt_extended import set_access_cookies from flask_jwt_extended import unset_jwt_cookies from flask_jwt_extended import get_jwt_identity from flask_jwt_extended import jwt_required -from flask_jwt_extended import JWTManager -import json -import mariadb -import uuid -import datetime - -from werkzeug.security import generate_password_hash, check_password_hash +from werkzeug.security import generate_password_hash from werkzeug.exceptions import HTTPException, RequestEntityTooLarge from werkzeug.utils import secure_filename -import jwt - -from functools import wraps +from src.db import db, dbmanage ######################################################### # Class et Methods # -class BDD: - conf = { - 'host': '127.0.0.1', - 'port': 3306, - 'user': 'vincent', - 'password': 'malkavian', - 'database': 'test1' - } +users = Blueprint('users', __name__, url_prefix='/api/utilisateurs') - def __init__(self, host="0.0.0.0", port=3306, user="user1", password="pass1", database="db1"): - ''' Constructor ''' - self.conf['host']=host - self.conf['port']=port - self.conf['user']=user - self.conf['password']=password - self.conf['database']=database - self.connected = False - self.logger = log.getLogger("U10Manager") - - def connect(self): - ''' connect to database ''' - self.logger.info("Connexion à la base de données") - self.conn = None - self.cursor = None - try: - # connection for MariaDB - self.conn = mariadb.connect(**self.conf) - # create a connection cursor - self.cursor = self.conn.cursor() - self.connected = True - except mariadb.Error as e: - self.logger.error("Error connecting to MariaDB Platform: {}".format(e)) - content = {'error': str(e)} - return False, content - return True, {} - - def disconnect(self): - ''' disconnect from database ''' - self.logger.info("Déconnexion de la base de données") - self.conn.close() - self.connected = False - return - - def execute(self, sql_statement="", data=None, commit=False): - ''' execute SQL statement and retreive datas if necessary ''' - json_data=[] - try: - # execute a SQL statement - ### DEBUG ### - self.logger.debug("statement: {}".format(sql_statement)) - ### END DEBUG ### - if not data: - self.cursor.execute(sql_statement) - else: - self.cursor.execute(sql_statement, data) - if commit: - self.conn.commit() - except mariadb.Error as e: - self.logger.error("Error: {}".format(e)) - content = {'error': str(e)} - return False, content - - if not commit: - # serialize results into JSON - row_headers=[x[0] for x in self.cursor.description] - rv = self.cursor.fetchall() - self.logger.debug("description: {} - datas: {}".format(self.cursor.description, rv)) - for result in rv: - json_data.append(dict(zip(row_headers,result))) - self.logger.debug("json_data: {}".format(pprint(json_data))) - return True, json_data - -######################################################### -# Corps principal du programme # - -logger = log.getLogger("U10Manager") -app = Flask(__name__) -CORS(app, resources={r"/api/*": { - "origins": "http://localhost:4200", - "supports_credentials": True - }}) -db = BDD(host='127.0.0.1', port=3306, user='vincent', password='malkavian', database='test1') - -app.config["SECRET_KEY"] = "thisissecret" -# Setup the Flask-JWT-Extended extension -app.config["JWT_SECRET_KEY"] = "cdscjdsklcfqezffhrevneqggfuhmnvqnmh" -app.config["JWT_COOKIE_SECURE"] = False -app.config["JWT_TOKEN_LOCATION"] = ["cookies"] -app.config["JWT_ACCESS_TOKEN_EXPIRES"] = datetime.timedelta(hours=1) -# Controls if Cross Site Request Forgery (CSRF) protection is enabled when using cookies -# This should always be True in production -app.config["JWT_COOKIE_CSRF_PROTECT"] = True - -UPLOAD_FOLDER = 'static/img' -ALLOWED_EXTENSIONS = {'png', 'jpg', 'gif', 'jpeg'} -app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER -app.config['MAX_CONTENT_LENGTH'] = 1 * 1000 * 1000 # 1 megabytes - -jwt = JWTManager(app) - -def dbmanage(func): - ''' decorateur de la fonction db.connect & db.disconnect ''' - @wraps(func) - def wrapper(*args, **kwargs): - # connexion à la base de données - state, ret = db.connect() - if not state: - content = {'Erreur' : ret['error']} - return content, status.HTTP_503_SERVICE_UNAVAILABLE - else: - # Appel de la fonction - ret = func(*args, **kwargs) - # deconnexion de la base de données - db.disconnect() - return ret - return wrapper - -@app.errorhandler(HTTPException) +@users.errorhandler(HTTPException) def handle_exception(e): ''' return JSON instead of HTML for HTTP errors ''' response = e.get_response() @@ -170,7 +44,7 @@ def handle_exception(e): response.content_type = "application/json" return response -@app.after_request +@users.after_request def refresh_expiring_tokens(response): ''' Using an 'after_request' callback, we refresh any token that is within 30 minutes of expiring.''' @@ -179,17 +53,18 @@ def refresh_expiring_tokens(response): now = datetime.datetime.now(datetime.timezone.utc) target_timestamp = datetime.datetime.timestamp(now + datetime.timedelta(minutes=30)) ### DEBUG ### - logger.debug("exp: {} - target: {}".format(exp_timestamp, target_timestamp)) + current_app.logger.debug("exp: {} - target: {}".format(exp_timestamp, target_timestamp)) ### END DEBUG ### if target_timestamp > exp_timestamp: - logger.warning("On doit recréer un token ....") + current_app.logger.warning("On doit recréer un token ....") access_token = create_access_token(identity=get_jwt_identity()) set_access_cookies(response, access_token) return response except (RuntimeError, KeyError): return response -@app.route('/api/utilisateurs', methods=['GET']) + +@users.route('', methods=['GET']) @jwt_required() @dbmanage def get_all_users(): @@ -197,7 +72,7 @@ def get_all_users(): # Access the identity of the current user with get_jwt_identity current_user = get_jwt_identity() ### DEBUG ### - logger.debug("current_user: {}".format(current_user)) + current_app.logger.debug("current_user: {}".format(current_user)) ### DEBUG END ### # Test si l'utilisateur courant est actif ou pas if not current_user["Actif"]: @@ -216,7 +91,7 @@ def get_all_users(): content = ret return jsonify(content) -@app.route('/api/utilisateurs/', methods=['GET']) +@users.route('/', methods=['GET']) @jwt_required() @dbmanage def get_one_user(userId): @@ -224,7 +99,7 @@ def get_one_user(userId): # Access the identity of the current user with get_jwt_identity current_user = get_jwt_identity() ### DEBUG ### - logger.debug("Actif ? {} - Role ? {} - userId : {}/{}".format(current_user["Actif"], current_user["Role"], current_user["userId"], userId)) + current_app.logger.debug("Actif ? {} - Role ? {} - userId : {}/{}".format(current_user["Actif"], current_user["Role"], current_user["userId"], userId)) ### DEBUG END ### # Test si l'utilisateur courant est actif ou pas # Si l'utilisateur courant n'est pas administrateur, il ne peut voir que son profil @@ -244,7 +119,7 @@ def get_one_user(userId): content = ret[0] return jsonify(content) -@app.route('/api/utilisateurs', methods=['POST']) +@users.route('', methods=['POST']) @jwt_required() @dbmanage def add_user(): @@ -270,7 +145,7 @@ def add_user(): db.disconnect() abort(401, description="Identifiant déjà utilisé!") ### DEBUG ### - logger.debug("Datas: {}".format(data_json)) + current_app.logger.debug("Datas: {}".format(data_json)) ### END DEBUG ### # Hash du mot de passe hashed_password = generate_password_hash(data_json['Password'], method='sha256') @@ -282,12 +157,12 @@ def add_user(): if not etat: content = {'Erreur': ret['error']} ### DEBUG ### - logger.debug("content: {}".format(content)) + current_app.logger.debug("content: {}".format(content)) ### END DEBUG ### return content, status.HTTP_500_INTERNAL_SERVER_ERROR return jsonify({'message' : 'Nouvel utilisateur créé!'}) -@app.route('/api/utilisateurs/', methods=['PUT']) +@users.route('/', methods=['PUT']) @jwt_required() @dbmanage def modif_user(userId): @@ -302,7 +177,7 @@ def modif_user(userId): # test des attributs (JSON) de la requete if not request.data.decode("utf-8"): - logger.error("Data not found") + current_app.logger.error("Data not found") db.disconnect() abort(400, description='Data not found') @@ -310,9 +185,6 @@ def modif_user(userId): dataDict = request.get_json() # Hash du mot de passe dataDict['Password'] = generate_password_hash(dataDict['Password'], method='sha256') - ### DEBUG ### - logger.debug("Datas: {}".format(pprint(dataDict))) - ### END DEBUG ### sql_statement = "UPDATE utilisateur SET Nom=%s, Prenom=%s, Photo=%s, Identifiant=%s, Password=%s, Role=%s, Actif=%d WHERE userId = {}".format(userId) data = (dataDict['Nom'], dataDict['Prenom'], dataDict['Photo'], dataDict['Identifiant'], dataDict['Password'], dataDict['Role'], dataDict['Actif']) @@ -323,7 +195,7 @@ def modif_user(userId): abort(500) return jsonify({'message' : 'utilisateur {} modifié!'.format(userId)}) -@app.route('/api/utilisateurs/', methods=['DELETE']) +@users.route('/', methods=['DELETE']) @jwt_required() @dbmanage def del_user(userId): @@ -337,7 +209,7 @@ def del_user(userId): # test des attributs (JSON) de la requete if request.data.decode("utf-8"): - logger.error("Data found : {}".format(request.data.decode("utf-8"))) + current_app.logger.error("Data found : {}".format(request.data.decode("utf-8"))) db.disconnect() abort(400) # On vérifie que l'utilisateur existe en base de données @@ -355,7 +227,7 @@ def del_user(userId): # On supprime l'utilisateur si celui-ci a été trouvé sql_statement = "DELETE FROM utilisateur WHERE userId = {}".format(userId) ### DEBUG ### - logger.debug("statement: {}".format(sql_statement)) + current_app.logger.debug("statement: {}".format(sql_statement)) ### END DEBUG ### # Execution de la requete SQL etat, ret = db.execute(sql_statement, None, True) @@ -365,7 +237,7 @@ def del_user(userId): content = {'message' : 'utilisateur supprimé!'} return jsonify(content) -@app.route('/api/utilisateurs//reset_password', methods=['GET']) +@users.route('//reset_password', methods=['GET']) @jwt_required() @dbmanage def reset_passwd_user(userId): @@ -389,7 +261,38 @@ def reset_passwd_user(userId): content = {'message' : 'reset du mot de passe!'} return jsonify(content) -@app.route('/api/utilisateurs//photo', methods=['GET']) +@users.route('//activate', methods=['PUT']) +@jwt_required() +@dbmanage +def deactivate_user(userId): + ''' Désactivation d'un utilisateur représenté par son Id ''' + # Access the identity of the current user with get_jwt_identity + current_user = get_jwt_identity() + # Test si l'utilisateur courant est Admin ou pas + if not current_user["Actif"] or current_user["Role"] != 'Administrateur': + db.disconnect() + abort(403, description='Utilisateur non autorisé') + + # test des attributs (JSON) de la requete + if not request.data.decode("utf-8"): + current_app.logger.error("Data not found") + db.disconnect() + abort(400, description='Data not found') + + # recuperation des attributs (JSON) de la requete + dataDict = request.get_json() + # Création de la requete SQL + sql_statement = "UPDATE utilisateur SET Actif={} WHERE userId = {}".format(dataDict['Actif'], userId) + + # Execution de la requete SQL + etat, ret = db.execute(sql_statement, None, True) + if not etat: + db.disconnect() + abort(500) + content = {'message' : 'desactivation de l\'utilisateur réussi!'} + return jsonify(content) + +@users.route('//photo', methods=['GET']) @jwt_required() @dbmanage def get_photo(userId): @@ -417,10 +320,10 @@ def get_photo(userId): abort(404) user = ret[0] if user['Photo']: - return send_file(os.path.join('../', user['Photo']), mimetype='image/'+os.path.splitext(user['Photo'])[1].split('.')[1]) + return send_file(os.path.join(user['Photo']), mimetype='image/'+os.path.splitext(user['Photo'])[1].split('.')[1]) return abort(404, description='Picture not found!') -@app.route('/api/utilisateurs/current', methods=['GET']) +@users.route('/current', methods=['GET']) @jwt_required() def current_user(): ''' retourne l'utilisateur courant connecté ''' @@ -428,63 +331,10 @@ def current_user(): current_user = get_jwt_identity() return jsonify(current_user) -@app.route('/api/utilisateurs/login', methods=['POST']) -@dbmanage -def login(): - ### DEBUG ### - logger.debug("Request : {}".format(request)) - logger.debug("Auth {}".format(request.authorization)) - ### END DEBUG ### - auth = request.authorization - user = None - if not auth or not auth.username or not auth.password: - logger.error("Login and Password required !") - db.disconnect() - abort(401, description='Login and Password required') - - # On vérifie que l'utilisateur existe en base de données - sql_statement = "SELECT * FROM utilisateur WHERE Identifiant = \"{}\"".format(auth.username) - # execution de la requete sql - etat, ret = db.execute(sql_statement, None, False) - if not etat: - db.disconnect() - abort(500) - else: - if not ret: - db.disconnect() - abort(404) - else: - user = ret[0] - - ### DEBUG ### - logger.debug("user bdd: {}".format(user)) - ### END DEBUG ### - if user and check_password_hash(user['Password'], auth.password): - try: - # create a new access token - token = create_access_token(identity=user) - content = jsonify({'message': 'login successful !'}) - # set token as cookie - set_access_cookies(content, token) - except Exception as e: - logger.erreur('Erreur : {}'.format(e)) - db.disconnect() - return abort(500) - return content - logger.error("authorization failed !") - db.disconnect() - return abort(401) - -@app.route('/api/utilisateurs/logout', methods=['POST']) -def logout(): - content = jsonify({'message': 'logout successful !'}) - unset_jwt_cookies(content) - return content - def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS -@app.route('/api/utilisateurs//uploadImage', methods=['POST']) +@users.route('//uploadImage', methods=['POST']) @jwt_required() @dbmanage def uploadImage(userId): @@ -494,7 +344,7 @@ def uploadImage(userId): # Si l'utilisateur courant n'est pas administrateur, il ne peut voir que son profil if not current_user["Actif"] or current_user["Role"] != "Administrateur" and current_user['userId'] != userId: db.disconnect() - logger.error("Utilisateur non autorisé") + current_app.logger.error("Utilisateur non autorisé") abort(403, description='Utilisateur non autorisé') # On vérifie que l'utilisateur existe en base de données @@ -510,23 +360,27 @@ def uploadImage(userId): abort(404) user = ret[0] + current_app.logger.debug("Req Headers: {}".format(request.headers)) + current_app.logger.debug("Req Files: {}".format(request.files)) + current_app.logger.debug("Req data: {}".format(request.data)) + # check if the post request has the file part if 'photo' not in request.files: - logger.error('No file part in the request') + current_app.logger.error('No file part in the request') abort(400) try: photo = request.files['photo'] except RequestEntityTooLarge as e: - logger.error("Fichier trop gros ...") + current_app.logger.error("Fichier trop gros ...") abort(413) # If the user does not select a file, the browser submits an empty file without a filename if photo.filename == '': - logger.error('No selected file') + current_app.logger.error('No selected file') abort(401) if photo and allowed_file(photo.filename): filename = secure_filename(photo.filename) ### DEBUG ### - logger.debug("filename: {}".format(os.path.splitext(filename))) + current_app.logger.debug("filename: {}".format(os.path.splitext(filename))) ### END DEBUG ### filepath = os.path.join(app.config['UPLOAD_FOLDER'], user['Identifiant'] + os.path.splitext(filename)[1]) photo.save(filepath) @@ -540,18 +394,3 @@ def uploadImage(userId): abort(500) content = jsonify({'message': 'photo saved successfuly!'}) return content - -def main(): - logger.setLevel(log.DEBUG) - fl = log.StreamHandler() - fl.setLevel(log.DEBUG) - formatter = log.Formatter('%(asctime)s - %(name)s [%(module)s.%(funcName)s:%(lineno)d] - %(levelname)s - %(message)s') - fl.setFormatter(formatter) - logger.addHandler(fl) - app.logger.handlers.clear() - app.logger.addHandler(fl) - - app.run(host="0.0.0.0", debug=True) - -if __name__ == '__main__': - main()