Compare commits
11 Commits
production
...
poetry
| Author | SHA1 | Date | |
|---|---|---|---|
| 9e6d6d273c | |||
| b91cc55341 | |||
| 2d0a5c2974 | |||
| b60d89ce0a | |||
|
|
6936d8d02c | ||
|
|
e5eb9ceeb6 | ||
|
|
1d563cd8b7 | ||
|
|
40ddd4bbeb | ||
| 884ae00574 | |||
| 52664b7adb | |||
| 6e2fa6c101 |
762
Pipfile.lock
generated
762
Pipfile.lock
generated
@@ -1,762 +0,0 @@
|
|||||||
{
|
|
||||||
"_meta": {
|
|
||||||
"hash": {
|
|
||||||
"sha256": "76bfb08c216b99c72b4d6f0e7379173e2dcbc59c351b15d0695f60c2ddf46f5c"
|
|
||||||
},
|
|
||||||
"pipfile-spec": 6,
|
|
||||||
"requires": {
|
|
||||||
"python_version": "3.8"
|
|
||||||
},
|
|
||||||
"sources": [
|
|
||||||
{
|
|
||||||
"name": "pypi",
|
|
||||||
"url": "https://pypi.org/simple",
|
|
||||||
"verify_ssl": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"default": {
|
|
||||||
"alabaster": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
|
|
||||||
"sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"
|
|
||||||
],
|
|
||||||
"version": "==0.7.12"
|
|
||||||
},
|
|
||||||
"alembic": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:49277bb7242192bbb9eac58fed4fe02ec6c3a2a4b4345d2171197459266482b2"
|
|
||||||
],
|
|
||||||
"version": "==1.3.1"
|
|
||||||
},
|
|
||||||
"aniso8601": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:529dcb1f5f26ee0df6c0a1ee84b7b27197c3c50fc3a6321d66c544689237d072",
|
|
||||||
"sha256:c033f63d028b9a58e3ab0c2c7d0532ab4bfa7452bfc788fbfe3ddabd327b181a"
|
|
||||||
],
|
|
||||||
"version": "==8.0.0"
|
|
||||||
},
|
|
||||||
"apispec": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:cf8e1f3b56949710f8cf23797b7f40215e9dae8bac583789a3f2c13dc56349fa",
|
|
||||||
"sha256:fe5cf5fc89b1c4a73acd5af3a10ede02b31ec116f215ed02271cb905d3172367"
|
|
||||||
],
|
|
||||||
"version": "==3.1.0"
|
|
||||||
},
|
|
||||||
"arrow": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:4bfacea734ead51495dc47df00421ecfd4ca1f2c0fbe58b9a26eaeddedc31caf",
|
|
||||||
"sha256:67f8be7c0cf420424bc62d8d7dc40b44e4bb2f7b515f9cc2954fb36e35797656"
|
|
||||||
],
|
|
||||||
"version": "==0.14.7"
|
|
||||||
},
|
|
||||||
"attrs": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
|
|
||||||
"sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
|
|
||||||
],
|
|
||||||
"version": "==19.3.0"
|
|
||||||
},
|
|
||||||
"beaker": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:ad5d1c05027ee3be3a482ea39f8cb70339b41e5d6ace0cb861382754076d187e"
|
|
||||||
],
|
|
||||||
"version": "==1.11.0"
|
|
||||||
},
|
|
||||||
"beautifulsoup4": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:5279c36b4b2ec2cb4298d723791467e3000e5384a43ea0cdf5d45207c7e97169",
|
|
||||||
"sha256:6135db2ba678168c07950f9a16c4031822c6f4aec75a65e0a97bc5ca09789931",
|
|
||||||
"sha256:dcdef580e18a76d54002088602eba453eec38ebbcafafeaabd8cab12b6155d57"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==4.8.1"
|
|
||||||
},
|
|
||||||
"certifi": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50",
|
|
||||||
"sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef"
|
|
||||||
],
|
|
||||||
"version": "==2019.9.11"
|
|
||||||
},
|
|
||||||
"cffi": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0b49274afc941c626b605fb59b59c3485c17dc776dc3cc7cc14aca74cc19cc42",
|
|
||||||
"sha256:0e3ea92942cb1168e38c05c1d56b0527ce31f1a370f6117f1d490b8dcd6b3a04",
|
|
||||||
"sha256:135f69aecbf4517d5b3d6429207b2dff49c876be724ac0c8bf8e1ea99df3d7e5",
|
|
||||||
"sha256:19db0cdd6e516f13329cba4903368bff9bb5a9331d3410b1b448daaadc495e54",
|
|
||||||
"sha256:2781e9ad0e9d47173c0093321bb5435a9dfae0ed6a762aabafa13108f5f7b2ba",
|
|
||||||
"sha256:291f7c42e21d72144bb1c1b2e825ec60f46d0a7468f5346841860454c7aa8f57",
|
|
||||||
"sha256:2c5e309ec482556397cb21ede0350c5e82f0eb2621de04b2633588d118da4396",
|
|
||||||
"sha256:2e9c80a8c3344a92cb04661115898a9129c074f7ab82011ef4b612f645939f12",
|
|
||||||
"sha256:32a262e2b90ffcfdd97c7a5e24a6012a43c61f1f5a57789ad80af1d26c6acd97",
|
|
||||||
"sha256:3c9fff570f13480b201e9ab69453108f6d98244a7f495e91b6c654a47486ba43",
|
|
||||||
"sha256:415bdc7ca8c1c634a6d7163d43fb0ea885a07e9618a64bda407e04b04333b7db",
|
|
||||||
"sha256:42194f54c11abc8583417a7cf4eaff544ce0de8187abaf5d29029c91b1725ad3",
|
|
||||||
"sha256:4424e42199e86b21fc4db83bd76909a6fc2a2aefb352cb5414833c030f6ed71b",
|
|
||||||
"sha256:4a43c91840bda5f55249413037b7a9b79c90b1184ed504883b72c4df70778579",
|
|
||||||
"sha256:599a1e8ff057ac530c9ad1778293c665cb81a791421f46922d80a86473c13346",
|
|
||||||
"sha256:5c4fae4e9cdd18c82ba3a134be256e98dc0596af1e7285a3d2602c97dcfa5159",
|
|
||||||
"sha256:5ecfa867dea6fabe2a58f03ac9186ea64da1386af2159196da51c4904e11d652",
|
|
||||||
"sha256:62f2578358d3a92e4ab2d830cd1c2049c9c0d0e6d3c58322993cc341bdeac22e",
|
|
||||||
"sha256:6471a82d5abea994e38d2c2abc77164b4f7fbaaf80261cb98394d5793f11b12a",
|
|
||||||
"sha256:6d4f18483d040e18546108eb13b1dfa1000a089bcf8529e30346116ea6240506",
|
|
||||||
"sha256:71a608532ab3bd26223c8d841dde43f3516aa5d2bf37b50ac410bb5e99053e8f",
|
|
||||||
"sha256:74a1d8c85fb6ff0b30fbfa8ad0ac23cd601a138f7509dc617ebc65ef305bb98d",
|
|
||||||
"sha256:7b93a885bb13073afb0aa73ad82059a4c41f4b7d8eb8368980448b52d4c7dc2c",
|
|
||||||
"sha256:7d4751da932caaec419d514eaa4215eaf14b612cff66398dd51129ac22680b20",
|
|
||||||
"sha256:7f627141a26b551bdebbc4855c1157feeef18241b4b8366ed22a5c7d672ef858",
|
|
||||||
"sha256:8169cf44dd8f9071b2b9248c35fc35e8677451c52f795daa2bb4643f32a540bc",
|
|
||||||
"sha256:aa00d66c0fab27373ae44ae26a66a9e43ff2a678bf63a9c7c1a9a4d61172827a",
|
|
||||||
"sha256:ccb032fda0873254380aa2bfad2582aedc2959186cce61e3a17abc1a55ff89c3",
|
|
||||||
"sha256:d754f39e0d1603b5b24a7f8484b22d2904fa551fe865fd0d4c3332f078d20d4e",
|
|
||||||
"sha256:d75c461e20e29afc0aee7172a0950157c704ff0dd51613506bd7d82b718e7410",
|
|
||||||
"sha256:dcd65317dd15bc0451f3e01c80da2216a31916bdcffd6221ca1202d96584aa25",
|
|
||||||
"sha256:e570d3ab32e2c2861c4ebe6ffcad6a8abf9347432a37608fe1fbd157b3f0036b",
|
|
||||||
"sha256:fd43a88e045cf992ed09fa724b5315b790525f2676883a6ea64e3263bae6549d"
|
|
||||||
],
|
|
||||||
"version": "==1.13.2"
|
|
||||||
},
|
|
||||||
"chardet": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
|
||||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
|
||||||
],
|
|
||||||
"version": "==3.0.4"
|
|
||||||
},
|
|
||||||
"click": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
|
|
||||||
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
|
|
||||||
],
|
|
||||||
"version": "==7.0"
|
|
||||||
},
|
|
||||||
"coloredlogs": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:34fad2e342d5a559c31b6c889e8d14f97cb62c47d9a2ae7b5ed14ea10a79eff8",
|
|
||||||
"sha256:b869a2dda3fa88154b9dd850e27828d8755bfab5a838a1c97fbc850c6e377c36"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==10.0"
|
|
||||||
},
|
|
||||||
"coverage": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:08907593569fe59baca0bf152c43f3863201efb6113ecb38ce7e97ce339805a6",
|
|
||||||
"sha256:0be0f1ed45fc0c185cfd4ecc19a1d6532d72f86a2bac9de7e24541febad72650",
|
|
||||||
"sha256:141f08ed3c4b1847015e2cd62ec06d35e67a3ac185c26f7635f4406b90afa9c5",
|
|
||||||
"sha256:19e4df788a0581238e9390c85a7a09af39c7b539b29f25c89209e6c3e371270d",
|
|
||||||
"sha256:23cc09ed395b03424d1ae30dcc292615c1372bfba7141eb85e11e50efaa6b351",
|
|
||||||
"sha256:245388cda02af78276b479f299bbf3783ef0a6a6273037d7c60dc73b8d8d7755",
|
|
||||||
"sha256:331cb5115673a20fb131dadd22f5bcaf7677ef758741312bee4937d71a14b2ef",
|
|
||||||
"sha256:386e2e4090f0bc5df274e720105c342263423e77ee8826002dcffe0c9533dbca",
|
|
||||||
"sha256:3a794ce50daee01c74a494919d5ebdc23d58873747fa0e288318728533a3e1ca",
|
|
||||||
"sha256:60851187677b24c6085248f0a0b9b98d49cba7ecc7ec60ba6b9d2e5574ac1ee9",
|
|
||||||
"sha256:63a9a5fc43b58735f65ed63d2cf43508f462dc49857da70b8980ad78d41d52fc",
|
|
||||||
"sha256:6b62544bb68106e3f00b21c8930e83e584fdca005d4fffd29bb39fb3ffa03cb5",
|
|
||||||
"sha256:6ba744056423ef8d450cf627289166da65903885272055fb4b5e113137cfa14f",
|
|
||||||
"sha256:7494b0b0274c5072bddbfd5b4a6c6f18fbbe1ab1d22a41e99cd2d00c8f96ecfe",
|
|
||||||
"sha256:826f32b9547c8091679ff292a82aca9c7b9650f9fda3e2ca6bf2ac905b7ce888",
|
|
||||||
"sha256:93715dffbcd0678057f947f496484e906bf9509f5c1c38fc9ba3922893cda5f5",
|
|
||||||
"sha256:9a334d6c83dfeadae576b4d633a71620d40d1c379129d587faa42ee3e2a85cce",
|
|
||||||
"sha256:af7ed8a8aa6957aac47b4268631fa1df984643f07ef00acd374e456364b373f5",
|
|
||||||
"sha256:bf0a7aed7f5521c7ca67febd57db473af4762b9622254291fbcbb8cd0ba5e33e",
|
|
||||||
"sha256:bf1ef9eb901113a9805287e090452c05547578eaab1b62e4ad456fcc049a9b7e",
|
|
||||||
"sha256:c0afd27bc0e307a1ffc04ca5ec010a290e49e3afbe841c5cafc5c5a80ecd81c9",
|
|
||||||
"sha256:dd579709a87092c6dbee09d1b7cfa81831040705ffa12a1b248935274aee0437",
|
|
||||||
"sha256:df6712284b2e44a065097846488f66840445eb987eb81b3cc6e4149e7b6982e1",
|
|
||||||
"sha256:e07d9f1a23e9e93ab5c62902833bf3e4b1f65502927379148b6622686223125c",
|
|
||||||
"sha256:e2ede7c1d45e65e209d6093b762e98e8318ddeff95317d07a27a2140b80cfd24",
|
|
||||||
"sha256:e4ef9c164eb55123c62411f5936b5c2e521b12356037b6e1c2617cef45523d47",
|
|
||||||
"sha256:eca2b7343524e7ba246cab8ff00cab47a2d6d54ada3b02772e908a45675722e2",
|
|
||||||
"sha256:eee64c616adeff7db37cc37da4180a3a5b6177f5c46b187894e633f088fb5b28",
|
|
||||||
"sha256:ef824cad1f980d27f26166f86856efe11eff9912c4fed97d3804820d43fa550c",
|
|
||||||
"sha256:efc89291bd5a08855829a3c522df16d856455297cf35ae827a37edac45f466a7",
|
|
||||||
"sha256:fa964bae817babece5aa2e8c1af841bebb6d0b9add8e637548809d040443fee0",
|
|
||||||
"sha256:ff37757e068ae606659c28c3bd0d923f9d29a85de79bf25b2b34b148473b5025"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==4.5.4"
|
|
||||||
},
|
|
||||||
"cryptography": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:02079a6addc7b5140ba0825f542c0869ff4df9a69c360e339ecead5baefa843c",
|
|
||||||
"sha256:1df22371fbf2004c6f64e927668734070a8953362cd8370ddd336774d6743595",
|
|
||||||
"sha256:369d2346db5934345787451504853ad9d342d7f721ae82d098083e1f49a582ad",
|
|
||||||
"sha256:3cda1f0ed8747339bbdf71b9f38ca74c7b592f24f65cdb3ab3765e4b02871651",
|
|
||||||
"sha256:44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2",
|
|
||||||
"sha256:4b1030728872c59687badcca1e225a9103440e467c17d6d1730ab3d2d64bfeff",
|
|
||||||
"sha256:58363dbd966afb4f89b3b11dfb8ff200058fbc3b947507675c19ceb46104b48d",
|
|
||||||
"sha256:6ec280fb24d27e3d97aa731e16207d58bd8ae94ef6eab97249a2afe4ba643d42",
|
|
||||||
"sha256:7270a6c29199adc1297776937a05b59720e8a782531f1f122f2eb8467f9aab4d",
|
|
||||||
"sha256:73fd30c57fa2d0a1d7a49c561c40c2f79c7d6c374cc7750e9ac7c99176f6428e",
|
|
||||||
"sha256:7f09806ed4fbea8f51585231ba742b58cbcfbfe823ea197d8c89a5e433c7e912",
|
|
||||||
"sha256:90df0cc93e1f8d2fba8365fb59a858f51a11a394d64dbf3ef844f783844cc793",
|
|
||||||
"sha256:971221ed40f058f5662a604bd1ae6e4521d84e6cad0b7b170564cc34169c8f13",
|
|
||||||
"sha256:a518c153a2b5ed6b8cc03f7ae79d5ffad7315ad4569b2d5333a13c38d64bd8d7",
|
|
||||||
"sha256:b0de590a8b0979649ebeef8bb9f54394d3a41f66c5584fff4220901739b6b2f0",
|
|
||||||
"sha256:b43f53f29816ba1db8525f006fa6f49292e9b029554b3eb56a189a70f2a40879",
|
|
||||||
"sha256:d31402aad60ed889c7e57934a03477b572a03af7794fa8fb1780f21ea8f6551f",
|
|
||||||
"sha256:de96157ec73458a7f14e3d26f17f8128c959084931e8997b9e655a39c8fde9f9",
|
|
||||||
"sha256:df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2",
|
|
||||||
"sha256:ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf",
|
|
||||||
"sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8"
|
|
||||||
],
|
|
||||||
"version": "==2.8"
|
|
||||||
},
|
|
||||||
"decorator": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:54c38050039232e1db4ad7375cfce6748d7b41c29e95a081c8a6d2c30364a2ce",
|
|
||||||
"sha256:5d19b92a3c8f7f101c8dd86afd86b0f061a8ce4540ab8cd401fa2542756bce6d"
|
|
||||||
],
|
|
||||||
"version": "==4.4.1"
|
|
||||||
},
|
|
||||||
"dnspython": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:36c5e8e38d4369a08b6780b7f27d790a292b2b08eea01607865bf0936c558e01",
|
|
||||||
"sha256:f69c21288a962f4da86e56c4905b49d11aba7938d3d740e80d9e366ee4f1632d"
|
|
||||||
],
|
|
||||||
"version": "==1.16.0"
|
|
||||||
},
|
|
||||||
"ecdsa": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:64c613005f13efec6541bb0a33290d0d03c27abab5f15fbab20fb0ee162bdd8e",
|
|
||||||
"sha256:e108a5fe92c67639abae3260e43561af914e7fd0d27bae6d2ec1312ae7934dfe"
|
|
||||||
],
|
|
||||||
"version": "==0.14.1"
|
|
||||||
},
|
|
||||||
"eventlet": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:658b1cd80937adc1a4860de2841e0528f64e2ca672885c4e00fc0e2217bde6b1",
|
|
||||||
"sha256:6c9c625af48424c4680d89314dbe45a76cc990cf002489f9469ff214b044ffc1"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==0.25.1"
|
|
||||||
},
|
|
||||||
"flask": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52",
|
|
||||||
"sha256:45eb5a6fd193d6cf7e0cf5d8a5b31f83d5faae0293695626f539a823e93b13f6"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==1.1.1"
|
|
||||||
},
|
|
||||||
"flask-cors": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:72170423eb4612f0847318afff8c247b38bd516b7737adfc10d1c2cdbb382d16",
|
|
||||||
"sha256:f4d97201660e6bbcff2d89d082b5b6d31abee04b1b3003ee073a6fd25ad1d69a"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==3.0.8"
|
|
||||||
},
|
|
||||||
"flask-httpauth": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0149953720489407e51ec24bc2f86273597b7973d71cd51f9443bd0e2a89bd72",
|
|
||||||
"sha256:6ef8b761332e780f9ff74d5f9056c2616f52babc1998b01d9f361a1e439e61b9"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==3.3.0"
|
|
||||||
},
|
|
||||||
"flask-jwt-extended": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0aa8ee6fa7eb3be9314e39dd199ac8e19389a95371f9d54e155c7aa635e319dd"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==3.24.1"
|
|
||||||
},
|
|
||||||
"flask-login": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:c815c1ac7b3e35e2081685e389a665f2c74d7e077cb93cecabaea352da4752ec"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==0.4.1"
|
|
||||||
},
|
|
||||||
"flask-marshmallow": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:4f507f883838b397638a3a36c7d36ee146b255a49db952f5d9de3f6f4522e8a8",
|
|
||||||
"sha256:69e99e3a123393894884a032ae2d11e6bdf4519a505819b66cec7eda32057741"
|
|
||||||
],
|
|
||||||
"version": "==0.10.1"
|
|
||||||
},
|
|
||||||
"flask-migrate": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:6fb038be63d4c60727d5dfa5f581a6189af5b4e2925bc378697b4f0a40cfb4e1",
|
|
||||||
"sha256:a96ff1875a49a40bd3e8ac04fce73fdb0870b9211e6168608cbafa4eb839d502"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.5.2"
|
|
||||||
},
|
|
||||||
"flask-pyoidc": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:c5556e82eb45c6314f4dc4c357c9909830e99cd2e4237ba56f34ca9e699aae42"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==3.0.0"
|
|
||||||
},
|
|
||||||
"flask-restplus": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:a15d251923a8feb09a5d805c2f4d188555910a42c64d58f7dd281b8cac095f1b",
|
|
||||||
"sha256:a66e442d0bca08f389fc3d07b4d808fc89961285d12fb8013f7cf15516fa9f5c"
|
|
||||||
],
|
|
||||||
"version": "==0.13.0"
|
|
||||||
},
|
|
||||||
"flask-restplus-patched": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:36342775f9e0990dfc000dbe61133dfe56f9ef32c9b4c6293ba7f2c128d16efc"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==0.1.10"
|
|
||||||
},
|
|
||||||
"flask-script": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:6425963d91054cfcc185807141c7314a9c5ad46325911bd24dcb489bd0161c65"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.0.6"
|
|
||||||
},
|
|
||||||
"flask-socketio": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2172dff1e42415ba480cee02c30c2fc833671ff326f1598ee3d69aa02cf768ec",
|
|
||||||
"sha256:7ff5b2f5edde23e875a8b0abf868584e5706e11741557449bc5147df2cd78268"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==4.2.1"
|
|
||||||
},
|
|
||||||
"flask-sqlalchemy": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327",
|
|
||||||
"sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.4.1"
|
|
||||||
},
|
|
||||||
"flask-testing": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:dc076623d7d850653a018cb64f500948334c8aeb6b10a5a842bf1bcfb98122bc"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==0.7.1"
|
|
||||||
},
|
|
||||||
"future": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
|
|
||||||
],
|
|
||||||
"version": "==0.18.2"
|
|
||||||
},
|
|
||||||
"greenlet": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:000546ad01e6389e98626c1367be58efa613fa82a1be98b0c6fc24b563acc6d0",
|
|
||||||
"sha256:0d48200bc50cbf498716712129eef819b1729339e34c3ae71656964dac907c28",
|
|
||||||
"sha256:23d12eacffa9d0f290c0fe0c4e81ba6d5f3a5b7ac3c30a5eaf0126bf4deda5c8",
|
|
||||||
"sha256:37c9ba82bd82eb6a23c2e5acc03055c0e45697253b2393c9a50cef76a3985304",
|
|
||||||
"sha256:51503524dd6f152ab4ad1fbd168fc6c30b5795e8c70be4410a64940b3abb55c0",
|
|
||||||
"sha256:8041e2de00e745c0e05a502d6e6db310db7faa7c979b3a5877123548a4c0b214",
|
|
||||||
"sha256:81fcd96a275209ef117e9ec91f75c731fa18dcfd9ffaa1c0adbdaa3616a86043",
|
|
||||||
"sha256:853da4f9563d982e4121fed8c92eea1a4594a2299037b3034c3c898cb8e933d6",
|
|
||||||
"sha256:8b4572c334593d449113f9dc8d19b93b7b271bdbe90ba7509eb178923327b625",
|
|
||||||
"sha256:9416443e219356e3c31f1f918a91badf2e37acf297e2fa13d24d1cc2380f8fbc",
|
|
||||||
"sha256:9854f612e1b59ec66804931df5add3b2d5ef0067748ea29dc60f0efdcda9a638",
|
|
||||||
"sha256:99a26afdb82ea83a265137a398f570402aa1f2b5dfb4ac3300c026931817b163",
|
|
||||||
"sha256:a19bf883b3384957e4a4a13e6bd1ae3d85ae87f4beb5957e35b0be287f12f4e4",
|
|
||||||
"sha256:a9f145660588187ff835c55a7d2ddf6abfc570c2651c276d3d4be8a2766db490",
|
|
||||||
"sha256:ac57fcdcfb0b73bb3203b58a14501abb7e5ff9ea5e2edfa06bb03035f0cff248",
|
|
||||||
"sha256:bcb530089ff24f6458a81ac3fa699e8c00194208a724b644ecc68422e1111939",
|
|
||||||
"sha256:beeabe25c3b704f7d56b573f7d2ff88fc99f0138e43480cecdfcaa3b87fe4f87",
|
|
||||||
"sha256:d634a7ea1fc3380ff96f9e44d8d22f38418c1c381d5fac680b272d7d90883720",
|
|
||||||
"sha256:d97b0661e1aead761f0ded3b769044bb00ed5d33e1ec865e891a8b128bf7c656"
|
|
||||||
],
|
|
||||||
"version": "==0.4.15"
|
|
||||||
},
|
|
||||||
"html5lib": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:20b159aa3badc9d5ee8f5c647e5efd02ed2a66ab8d354930bd9ff139fc1dc0a3",
|
|
||||||
"sha256:66cb0dcfdbbc4f9c3ba1a63fdb511ffdbd4f513b2b6d81b80cd26ce6b3fb3736"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==1.0.1"
|
|
||||||
},
|
|
||||||
"humanfriendly": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:23057b10ad6f782e7bc3a20e3cb6768ab919f619bbdc0dd75691121bbde5591d",
|
|
||||||
"sha256:33ee8ceb63f1db61cce8b5c800c531e1a61023ac5488ccde2ba574a85be00a85"
|
|
||||||
],
|
|
||||||
"version": "==4.18"
|
|
||||||
},
|
|
||||||
"ics": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:12cf34aed0dafa1bf99d79ca58e99949d6721511b856386e118015fe5f5d6e3a"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==0.6"
|
|
||||||
},
|
|
||||||
"idna": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
|
|
||||||
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
|
|
||||||
],
|
|
||||||
"version": "==2.8"
|
|
||||||
},
|
|
||||||
"itsdangerous": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
|
|
||||||
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
|
|
||||||
],
|
|
||||||
"version": "==1.1.0"
|
|
||||||
},
|
|
||||||
"jinja2": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f",
|
|
||||||
"sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de"
|
|
||||||
],
|
|
||||||
"version": "==2.10.3"
|
|
||||||
},
|
|
||||||
"jsonschema": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163",
|
|
||||||
"sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"
|
|
||||||
],
|
|
||||||
"version": "==3.2.0"
|
|
||||||
},
|
|
||||||
"mako": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:a36919599a9b7dc5d86a7a8988f23a9a3a3d083070023bab23d64f7f1d1e0a4b"
|
|
||||||
],
|
|
||||||
"version": "==1.1.0"
|
|
||||||
},
|
|
||||||
"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"
|
|
||||||
},
|
|
||||||
"marshmallow": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1a358beb89c2b4d5555272065a9533591a3eb02f1b854f3c4002d88d8f2a1ddb",
|
|
||||||
"sha256:eb97c42c5928b5720812c9268865fe863d4807bc1a8b48ddd7d5c9e1779a6af0"
|
|
||||||
],
|
|
||||||
"version": "==3.2.2"
|
|
||||||
},
|
|
||||||
"monotonic": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:23953d55076df038541e648a53676fb24980f7a1be290cdda21300b3bc21dfb0",
|
|
||||||
"sha256:552a91f381532e33cbd07c6a2655a21908088962bb8fa7239ecbcc6ad1140cc7"
|
|
||||||
],
|
|
||||||
"version": "==1.5"
|
|
||||||
},
|
|
||||||
"oic": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:34da42f4f9b4a36a02b28d72a42a3197a91e7efc5356c1c41d7f1a0bdf3ad1d2"
|
|
||||||
],
|
|
||||||
"version": "==0.12"
|
|
||||||
},
|
|
||||||
"passlib": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:3d948f64138c25633613f303bcc471126eae67c04d5e3f6b7b8ce6242f8653e0",
|
|
||||||
"sha256:43526aea08fa32c6b6dbbbe9963c4c767285b78147b7437597f992812f69d280"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==1.7.1"
|
|
||||||
},
|
|
||||||
"pbr": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:2c8e420cd4ed4cec4e7999ee47409e876af575d4c35a45840d59e8b5f3155ab8",
|
|
||||||
"sha256:b32c8ccaac7b1a20c0ce00ce317642e6cf231cf038f9875e0280e28af5bf7ac9"
|
|
||||||
],
|
|
||||||
"version": "==5.4.3"
|
|
||||||
},
|
|
||||||
"pyasn1": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d",
|
|
||||||
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"
|
|
||||||
],
|
|
||||||
"version": "==0.4.8"
|
|
||||||
},
|
|
||||||
"pycparser": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"
|
|
||||||
],
|
|
||||||
"version": "==2.19"
|
|
||||||
},
|
|
||||||
"pycryptodomex": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0943b65fb41b7403a9def6214061fdd9ab9afd0bbc581e553c72eebe60bded36",
|
|
||||||
"sha256:0a1dbb5c4d975a4ea568fb7686550aa225d94023191fb0cca8747dc5b5d77857",
|
|
||||||
"sha256:0f43f1608518347fdcb9c8f443fa5cabedd33f94188b13e4196a3a7ba90d169c",
|
|
||||||
"sha256:11ce5fec5990e34e3981ed14897ba601c83957b577d77d395f1f8f878a179f98",
|
|
||||||
"sha256:17a09e38fdc91e4857cf5a7ce82f3c0b229c3977490f2146513e366923fc256b",
|
|
||||||
"sha256:22d970cee5c096b9123415e183ae03702b2cd4d3ba3f0ced25c4e1aba3967167",
|
|
||||||
"sha256:2a1793efcbae3a2264c5e0e492a2629eb10d895d6e5f17dbbd00eb8b489c6bda",
|
|
||||||
"sha256:30a8a148a0fe482cec1aaf942bbd0ade56ec197c14fe058b2a94318c57e1f991",
|
|
||||||
"sha256:32fbbaf964c5184d3f3e349085b0536dd28184b02e2b014fc900f58bbc126339",
|
|
||||||
"sha256:347d67faee36d449dc9632da411cc318df52959079062627f1243001b10dc227",
|
|
||||||
"sha256:45f4b4e5461a041518baabc52340c249b60833aa84cea6377dc8016a2b33c666",
|
|
||||||
"sha256:4717daec0035034b002d31c42e55431c970e3e38a78211f43990e1b7eaf19e28",
|
|
||||||
"sha256:51a1ac9e7dda81da444fed8be558a60ec88dfc73b2aa4b0efa310e87acb75838",
|
|
||||||
"sha256:53e9dcc8f14783f6300b70da325a50ac1b0a3dbaee323bd9dc3f71d409c197a1",
|
|
||||||
"sha256:5519a2ed776e193688b7ddb61ab709303f6eb7d1237081e298283c72acc44271",
|
|
||||||
"sha256:583450e8e80a0885c453211ed2bd69ceea634d8c904f23ff8687f677fe810e95",
|
|
||||||
"sha256:60f862bd2a07133585a4fc2ce2b1a8ec24746b07ac44307d22ef2b767cb03435",
|
|
||||||
"sha256:612091f1d3c84e723bec7cb855cf77576e646045744794c9a3f75ba80737762f",
|
|
||||||
"sha256:629a87b87c8203b8789ccefc7f2f2faecd2daaeb56bdd0b4e44cd89565f2db07",
|
|
||||||
"sha256:6e56ec4c8938fb388b6f250ddd5e21c15e8f25a76e0ad0e2abae9afee09e67b4",
|
|
||||||
"sha256:8e8092651844a11ec7fa534395f3dfe99256ce4edca06f128efc9d770d6e1dc1",
|
|
||||||
"sha256:8f5f260629876603e08f3ce95c8ccd9b6b83bf9a921c41409046796267f7adc5",
|
|
||||||
"sha256:9a6b74f38613f54c56bd759b411a352258f47489bbefd1d57c930a291498b35b",
|
|
||||||
"sha256:a5a13ebb52c4cd065fb673d8c94f39f30823428a4de19e1f3f828b63a8882d1e",
|
|
||||||
"sha256:a77ca778a476829876a3a70ae880073379160e4a465d057e3c4e1c79acdf1b8a",
|
|
||||||
"sha256:a9f7be3d19f79429c2118fd61bc2ec4fa095e93b56fb3a5f3009822402c4380f",
|
|
||||||
"sha256:dc15a467c4f9e4b43748ba2f97aea66f67812bfd581818284c47cadc81d4caec",
|
|
||||||
"sha256:e13cdeea23059f7577c230fd580d2c8178e67ebe10e360041abe86c33c316f1c",
|
|
||||||
"sha256:e45b85c8521bca6bdfaf57e4987743ade53e9f03529dd3adbc9524094c6d55c4",
|
|
||||||
"sha256:e87f17867b260f57c88487f943eb4d46c90532652bb37046e764842c3b66cbb1",
|
|
||||||
"sha256:ee40a5b156f6c1192bc3082e9d73d0479904433cdda83110546cd67f5a15a5be",
|
|
||||||
"sha256:ef63ffde3b267043579af8830fc97fc3b9b8a526a24e3ba23af9989d4e9e689a"
|
|
||||||
],
|
|
||||||
"version": "==3.9.4"
|
|
||||||
},
|
|
||||||
"pyjwkest": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:5560fd5ba08655f29ff6ad1df1e15dc05abc9d976fcbcec8d2b5167f49b70222"
|
|
||||||
],
|
|
||||||
"version": "==1.4.2"
|
|
||||||
},
|
|
||||||
"pyjwt": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e",
|
|
||||||
"sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==1.7.1"
|
|
||||||
},
|
|
||||||
"pyopenssl": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:621880965a720b8ece2f1b2f54ea2071966ab00e2970ad2ce11d596102063504",
|
|
||||||
"sha256:9a24494b2602aaf402be5c9e30a0b82d4a5c67528fe8fb475e3f3bc00dd69507"
|
|
||||||
],
|
|
||||||
"version": "==19.1.0"
|
|
||||||
},
|
|
||||||
"pyrsistent": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:eb6545dbeb1aa69ab1fb4809bfbf5a8705e44d92ef8fc7c2361682a47c46c778"
|
|
||||||
],
|
|
||||||
"version": "==0.15.5"
|
|
||||||
},
|
|
||||||
"python-dateutil": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
|
|
||||||
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
|
|
||||||
],
|
|
||||||
"version": "==2.8.1"
|
|
||||||
},
|
|
||||||
"python-editor": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d",
|
|
||||||
"sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b",
|
|
||||||
"sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8"
|
|
||||||
],
|
|
||||||
"version": "==1.0.4"
|
|
||||||
},
|
|
||||||
"python-engineio": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:4a13fb87c819b855c55a731fdf82559adb8311c04cfdfebd6b9ecd1c2afbb575",
|
|
||||||
"sha256:9c9a6035b4b5e5a225f426f846afa14cf627f7571d1ae02167cb703fefd134b7"
|
|
||||||
],
|
|
||||||
"version": "==3.10.0"
|
|
||||||
},
|
|
||||||
"python-jose": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:29701d998fe560e52f17246c3213a882a4a39da7e42c7015bcc1f7823ceaff1c",
|
|
||||||
"sha256:ed7387f0f9af2ea0ddc441d83a6eb47a5909bd0c8a72ac3250e75afec2cc1371"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==3.0.1"
|
|
||||||
},
|
|
||||||
"python-socketio": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:506b2cf7a520b40ea0b3f25e1272eff8de134dce6f471c1f6bc0de8c90fe8c57",
|
|
||||||
"sha256:d4e2c23241afa0aae2a5bcc107523b2fcc71f5020df89a093f3634eb48955967"
|
|
||||||
],
|
|
||||||
"version": "==4.3.1"
|
|
||||||
},
|
|
||||||
"pythonping": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:c754c2bd8617943988a0a1b47d7392a57efea51fb7a30c7e938e246e90ea91eb"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==1.0.7"
|
|
||||||
},
|
|
||||||
"pytz": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
|
|
||||||
"sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"
|
|
||||||
],
|
|
||||||
"version": "==2019.3"
|
|
||||||
},
|
|
||||||
"requests": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
|
|
||||||
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
|
|
||||||
],
|
|
||||||
"version": "==2.22.0"
|
|
||||||
},
|
|
||||||
"rsa": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:14ba45700ff1ec9eeb206a2ce76b32814958a98e372006c8fb76ba820211be66",
|
|
||||||
"sha256:1a836406405730121ae9823e19c6e806c62bbad73f890574fff50efa4122c487"
|
|
||||||
],
|
|
||||||
"version": "==4.0"
|
|
||||||
},
|
|
||||||
"scapy": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:e2f8d11f6a941c14a789ae8b236b27bd634681f1b29b5e893861e284d234f6b0"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==2.4.3"
|
|
||||||
},
|
|
||||||
"six": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd",
|
|
||||||
"sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"
|
|
||||||
],
|
|
||||||
"version": "==1.13.0"
|
|
||||||
},
|
|
||||||
"soupsieve": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:bdb0d917b03a1369ce964056fc195cfdff8819c40de04695a80bc813c3cfa1f5",
|
|
||||||
"sha256:e2c1c5dee4a1c36bcb790e0fabd5492d874b8ebd4617622c4f6a731701060dda"
|
|
||||||
],
|
|
||||||
"version": "==1.9.5"
|
|
||||||
},
|
|
||||||
"sqlalchemy": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:afa5541e9dea8ad0014251bc9d56171ca3d8b130c9627c6cb3681cff30be3f8a"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==1.3.11"
|
|
||||||
},
|
|
||||||
"sqlalchemy-migrate": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0bc02e292a040ade5e35a01d3ea744119e1309cdddb704fdb99bac13236614f8",
|
|
||||||
"sha256:e5d2348db19a5062132d93e3b4d9e7644af552fffbec4c78cc5358f848d2f6c1"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==0.13.0"
|
|
||||||
},
|
|
||||||
"sqlparse": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:40afe6b8d4b1117e7dff5504d7a8ce07d9a1b15aeeade8a2d10f130a834f8177",
|
|
||||||
"sha256:7c3dca29c022744e95b547e867cee89f4fce4373f3549ccd8797d8eb52cdb873"
|
|
||||||
],
|
|
||||||
"version": "==0.3.0"
|
|
||||||
},
|
|
||||||
"ssh2-python": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:054f2cab611ddca34095eb78d1ae1f6e29521678c82f0de17f696072a0b924fe",
|
|
||||||
"sha256:097d74684172f163d5025aef316987c1c7acf852d9fb75e4735720c8690adf87",
|
|
||||||
"sha256:0f4555b79e19ca6fdd84a74e85152d375a921d167ed9dc680d05f1f9f5dfc463",
|
|
||||||
"sha256:0fb3d9eca454b2874748c69e7e836ea5c6544f1b7acd87d8a876f99f618c806c",
|
|
||||||
"sha256:180c2728796f777a5856b23e5e1656a15a3e8602b4270348f892599ae5426f45",
|
|
||||||
"sha256:49aaa9d48cf52798d89f466a5e774e9dab8b76dc380b90645fd8d5c6622df177",
|
|
||||||
"sha256:4b5ba1390ff5e8faa6b2409df750e4650de3934c3b76ad707d7b54b477600226",
|
|
||||||
"sha256:57a4452dfc5c7f414de14e32f29e21f680fb8d7bdf94306e5f6bc8c2a235fa7a",
|
|
||||||
"sha256:584f753f0401217a55e70d2ae97a7e5220f4d37993a1bba275c4c5e110d87193",
|
|
||||||
"sha256:692cf27ddc5583da9b07b0f0348f0577579f8d798a0b8e4bf495b8d7f19fc854",
|
|
||||||
"sha256:6dc75bacc37de63aa9c86f9982642982c3a8103297fbbb73543a76e3db9abc6a",
|
|
||||||
"sha256:7a029275e62a1e31333ee7f2f151f661dedfac0345f1108e1a8d0b62e08ddf9b",
|
|
||||||
"sha256:7a3d761c669392e778c24a0eabcf6f1ba48ad68bb3b82a2d98235f56500bc8b0",
|
|
||||||
"sha256:8632684aae2f523e603381cf51a1da134fd5937cf0ae9650b1b9e7b4f328c133",
|
|
||||||
"sha256:8acab66dd73554d2c68abaccf96cdddfc10449ad6c7c273ac4b7a91779d414b2",
|
|
||||||
"sha256:9410b63c2361b2b65a55b5e8dc8187270de0f81e11864a427ad52b82b926bcc9",
|
|
||||||
"sha256:abe29ad01ed2ac4465e61c8de42b6850fca9c87d8792686a2de7c7d06c6eb72d",
|
|
||||||
"sha256:de151500a2a027a29bca3d7196c76008b41bb97db29378f29c4651ba1bcb00e8",
|
|
||||||
"sha256:debc56711643b82ab60a9c8b8cbf2978b65c0293aa7e53557a3c0d1fc57799cf",
|
|
||||||
"sha256:f508144d684b3c3c0f264ce67058f05ae3729ae77cc3a8f2051b442e01594849"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==0.18.0.post1"
|
|
||||||
},
|
|
||||||
"style": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:6485a4bcb84629341a5fd1587fe3ac4887daa4741f0c8a1d01b9c3c8a263afe7",
|
|
||||||
"sha256:8eb365fc15039b19b728bd4e6e85fb7daf24e7aeeec6a15a666f97484c564005"
|
|
||||||
],
|
|
||||||
"version": "==1.1.0"
|
|
||||||
},
|
|
||||||
"tatsu": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:80713413473a009f2081148d0f494884cabaf9d6866b71f2a68a92b6442f343d",
|
|
||||||
"sha256:c9211eeee9a2d4c90f69879ec0b518b1aa0d9450249cb0dd181f5f5b18be0a92"
|
|
||||||
],
|
|
||||||
"version": "==4.4.0"
|
|
||||||
},
|
|
||||||
"tempita": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:cacecf0baa674d356641f1d406b8bff1d756d739c46b869a54de515d08e6fc9c"
|
|
||||||
],
|
|
||||||
"version": "==0.5.2"
|
|
||||||
},
|
|
||||||
"update": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:a25522b4bf60e3e3c1a3ff3ca3a4f5a328ac4b8ff400fdc9614483147e313323"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"version": "==0.0.1"
|
|
||||||
},
|
|
||||||
"urllib3": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293",
|
|
||||||
"sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745"
|
|
||||||
],
|
|
||||||
"version": "==1.25.7"
|
|
||||||
},
|
|
||||||
"webargs": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:3beca296598067cec24a0b6f91c0afcc19b6e3c4d84ab026b931669628bb47b4",
|
|
||||||
"sha256:3f9dc15de183d356c9a0acc159c100ea0506c0c240c1e6f1d8b308c5fed4dbbd",
|
|
||||||
"sha256:fa4ad3ad9b38bedd26c619264fdc50d7ae014b49186736bca851e5b5228f2a1b"
|
|
||||||
],
|
|
||||||
"version": "==5.5.2"
|
|
||||||
},
|
|
||||||
"webencodings": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78",
|
|
||||||
"sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"
|
|
||||||
],
|
|
||||||
"version": "==0.5.1"
|
|
||||||
},
|
|
||||||
"werkzeug": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:7280924747b5733b246fe23972186c6b348f9ae29724135a6dfc1e53cea433e7",
|
|
||||||
"sha256:e5f4a1f98b52b18a93da705a7458e55afb26f32bff83ff5d19189f92462d65c4"
|
|
||||||
],
|
|
||||||
"version": "==0.16.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"develop": {}
|
|
||||||
}
|
|
||||||
@@ -3,19 +3,18 @@
|
|||||||
Backend base module
|
Backend base module
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
from io import StringIO
|
|
||||||
from logging.config import dictConfig
|
from logging.config import dictConfig
|
||||||
from logging.handlers import MemoryHandler
|
from logging.handlers import MemoryHandler
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import coloredlogs as coloredlogs
|
import coloredlogs
|
||||||
import jwt
|
import jwt
|
||||||
import requests
|
import requests
|
||||||
from flask import Flask, jsonify
|
from flask import Flask, jsonify
|
||||||
from flask_httpauth import HTTPTokenAuth, HTTPBasicAuth, MultiAuth
|
from flask_httpauth import HTTPTokenAuth, HTTPBasicAuth, MultiAuth
|
||||||
from flask_jwt_extended import JWTManager, decode_token
|
from flask_jwt_extended import JWTManager, decode_token
|
||||||
from flask_login import LoginManager
|
from flask_login import LoginManager
|
||||||
from flask_restplus import abort
|
from flask_restx import abort
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
from flask_cors import CORS
|
from flask_cors import CORS
|
||||||
from backend.config import Config
|
from backend.config import Config
|
||||||
@@ -109,11 +108,11 @@ class LrcException(Exception):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self.type is None:
|
if self.type is None:
|
||||||
msg = "LRC Exception: \"{}\"".format(', '.join(super().args))
|
msg = f"LRC Exception: \"{', '.join(super().args)}\""
|
||||||
else:
|
else:
|
||||||
msg = "LRC Exception: (original Exception: {}) \"{}\"".format(self.type, ', '.join(super().args))
|
msg = f"LRC Exception: (original Exception: {self.type}) \"{', '.join(super().args)}\""
|
||||||
if self.html_code is not None:
|
if self.html_code is not None:
|
||||||
msg += " (HTML Code: {})".format(self.html_code)
|
msg += f" (HTML Code: {self.html_code})"
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -135,7 +134,7 @@ jwt_auth = HTTPTokenAuth('Bearer')
|
|||||||
|
|
||||||
@jwt_extended.invalid_token_loader
|
@jwt_extended.invalid_token_loader
|
||||||
def unauthorized_jwt(token):
|
def unauthorized_jwt(token):
|
||||||
main_logger.info("Unauthorized access; invalid token provided: {}".format(token))
|
main_logger.info("Unauthorized access; invalid token provided: %s", token)
|
||||||
abort(401)
|
abort(401)
|
||||||
|
|
||||||
|
|
||||||
@@ -147,10 +146,10 @@ def verify_token(token):
|
|||||||
try:
|
try:
|
||||||
decoded = decode_token(token)
|
decoded = decode_token(token)
|
||||||
except jwt.exceptions.DecodeError as e:
|
except jwt.exceptions.DecodeError as e:
|
||||||
app.logger.warn("Could not verify token: {}".format(str(e)))
|
app.logger.warning("Could not verify token: %s", str(e))
|
||||||
return False
|
return False
|
||||||
except jwt.exceptions.ExpiredSignatureError as e:
|
except jwt.exceptions.ExpiredSignatureError as e:
|
||||||
app.logger.warn("Could not verify token: {}".format(str(e)))
|
app.logger.warning("Could not verify token: %s", str(e))
|
||||||
return False
|
return False
|
||||||
app.logger.info(decoded)
|
app.logger.info(decoded)
|
||||||
return True
|
return True
|
||||||
@@ -164,20 +163,22 @@ from backend.auth import oidc_auth, auth_bp
|
|||||||
try:
|
try:
|
||||||
oidc_auth.init_app(app)
|
oidc_auth.init_app(app)
|
||||||
except requests.exceptions.ConnectionError as err:
|
except requests.exceptions.ConnectionError as err:
|
||||||
app.logger.error("Could not connect to OIDC!!", err)
|
app.logger.exception("Could not connect to OIDC!!", exc_info=err)
|
||||||
|
|
||||||
# oidc_multi_auth = MultiAuth(oidc_auth, jwt_auth) <- can't work as OIDCAuthentication not implementing HTTPAuth
|
# oidc_multi_auth = MultiAuth(oidc_auth, jwt_auth) <- can't work as OIDCAuthentication not implementing HTTPAuth
|
||||||
|
|
||||||
from .serve_frontend import fe_bp
|
from .serve_frontend import fe_bp
|
||||||
from .api import auth_api_bp, api_v1, api_bp
|
from .api import auth_api_bp, api_v1, api_bp
|
||||||
|
|
||||||
|
CORS(app)
|
||||||
|
CORS(api_bp)
|
||||||
|
|
||||||
app.register_blueprint(auth_bp)
|
app.register_blueprint(auth_bp)
|
||||||
app.register_blueprint(auth_api_bp)
|
app.register_blueprint(auth_api_bp)
|
||||||
app.register_blueprint(api_bp)
|
app.register_blueprint(api_bp)
|
||||||
app.register_blueprint(fe_bp)
|
app.register_blueprint(fe_bp)
|
||||||
|
|
||||||
CORS(app)
|
|
||||||
CORS(api_bp)
|
|
||||||
|
|
||||||
# Fix flask-restplus by duck typing error handlers
|
# Fix flask-restplus by duck typing error handlers
|
||||||
jwt_extended._set_error_handler_callbacks(api_v1)
|
jwt_extended._set_error_handler_callbacks(api_v1)
|
||||||
|
|||||||
@@ -12,16 +12,25 @@ from backend.models import room_model, recorder_model, RecorderCommand
|
|||||||
from backend.recorder_adapters import get_defined_recorder_adapters
|
from backend.recorder_adapters import get_defined_recorder_adapters
|
||||||
from backend.tools.model_updater import update_recorder_models_database, create_default_recorders
|
from backend.tools.model_updater import update_recorder_models_database, create_default_recorders
|
||||||
|
|
||||||
|
def setup_database(app):
|
||||||
|
with app.app_context():
|
||||||
|
#db.drop_all()
|
||||||
|
db.create_all()
|
||||||
|
room_model.pre_fill_table()
|
||||||
|
update_recorder_models_database(drop=False)
|
||||||
|
create_default_recorders()
|
||||||
|
|
||||||
|
|
||||||
|
def get_app():
|
||||||
|
setup_database(app)
|
||||||
|
|
||||||
|
#app.register_blueprint(api_blueprint)
|
||||||
|
return app
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
|
app = get_app()
|
||||||
#db.drop_all()
|
|
||||||
#db.create_all()
|
|
||||||
room_model.pre_fill_table()
|
|
||||||
update_recorder_models_database(drop=False)
|
|
||||||
create_default_recorders()
|
|
||||||
|
|
||||||
print(app.config.get("SERVER_NAME", None))
|
print(app.config.get("SERVER_NAME", None))
|
||||||
server_name = app.config.get("SERVER_NAME", None)
|
server_name = app.config.get("SERVER_NAME", None)
|
||||||
if server_name is not None and "ubkaps154.ubka.uni-karlsruhe.de" in server_name:
|
if server_name is not None and "ubkaps154.ubka.uni-karlsruhe.de" in server_name:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from flask import Blueprint, abort
|
from flask import Blueprint, abort
|
||||||
from flask_restplus import Api, Namespace
|
from flask_restx import Api, Namespace
|
||||||
|
|
||||||
api_authorizations = {
|
api_authorizations = {
|
||||||
'apikey': {
|
'apikey': {
|
||||||
|
|||||||
@@ -10,21 +10,16 @@ import json
|
|||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
from datetime import datetime, timedelta
|
|
||||||
import jwt
|
import jwt
|
||||||
from flask import request, jsonify, current_app, url_for, Response, session, redirect, make_response
|
from flask import request, jsonify, current_app, url_for, redirect, make_response
|
||||||
from flask_jwt_extended import create_access_token, create_refresh_token, jwt_refresh_token_required, get_jwt_identity, \
|
from flask_jwt_extended import create_access_token, create_refresh_token, get_jwt, get_jwt_identity, jwt_required
|
||||||
get_raw_jwt, jwt_required
|
|
||||||
from functools import wraps
|
|
||||||
from random import randint
|
|
||||||
|
|
||||||
from flask_login import logout_user, login_user
|
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
from flask_restx import Resource, fields
|
||||||
from flask_restplus import Resource, fields
|
|
||||||
from werkzeug.routing import BuildError
|
from werkzeug.routing import BuildError
|
||||||
|
|
||||||
from backend import db, app, jwt_extended
|
from backend import db, app
|
||||||
from backend.api import auth_api_bp, auth_api_providers_ns, auth_api_register_ns
|
from backend.api import auth_api_bp, auth_api_providers_ns, auth_api_register_ns
|
||||||
from backend.auth import AUTH_PROVIDERS, oidc_auth
|
from backend.auth import AUTH_PROVIDERS, oidc_auth
|
||||||
from backend.models.user_model import User, Group, BlacklistToken
|
from backend.models.user_model import User, Group, BlacklistToken
|
||||||
@@ -58,19 +53,19 @@ def register():
|
|||||||
return jsonify(user.to_dict()), 201
|
return jsonify(user.to_dict()), 201
|
||||||
|
|
||||||
|
|
||||||
@auth_api_register_ns.route('/')
|
# @auth_api_register_ns.route('/')
|
||||||
@auth_api_register_ns.expect(auth_api_register_ns.model('RegisterModel', {
|
# @auth_api_register_ns.expect(auth_api_register_ns.model('RegisterModel', {
|
||||||
'nickname': fields.String(required=False, description='The user\'s nickname'),
|
# 'nickname': fields.String(required=False, description='The user\'s nickname'),
|
||||||
'first_name': fields.String(required=False, description='The user\'s first name'),
|
# 'first_name': fields.String(required=False, description='The user\'s first name'),
|
||||||
'last_name': fields.String(required=False, description='The user\'s last name'),
|
# 'last_name': fields.String(required=False, description='The user\'s last name'),
|
||||||
'lang': fields.String(required=False, description='The user\'s preferred language'),
|
# 'lang': fields.String(required=False, description='The user\'s preferred language'),
|
||||||
'timezone': fields.String(required=False, description='The user\'s preferred timezone'),
|
# 'timezone': fields.String(required=False, description='The user\'s preferred timezone'),
|
||||||
'email': fields.String(required=True, description='The user\'s e-mail address'),
|
# 'email': fields.String(required=True, description='The user\'s e-mail address'),
|
||||||
'password': fields.String(required=False, description='The group\'s name')
|
# 'password': fields.String(required=False, description='The group\'s name')
|
||||||
}))
|
# }))
|
||||||
class AuthProviders(Resource):
|
# class AuthProviders(Resource):
|
||||||
def get(self):
|
# def get(self):
|
||||||
return register()
|
# return register()
|
||||||
|
|
||||||
|
|
||||||
@auth_api_bp.route('/login', methods=('GET', 'POST',))
|
@auth_api_bp.route('/login', methods=('GET', 'POST',))
|
||||||
@@ -96,7 +91,7 @@ def login():
|
|||||||
@auth_api_bp.route('/logout', methods=['GET', 'DELETE'])
|
@auth_api_bp.route('/logout', methods=['GET', 'DELETE'])
|
||||||
@jwt_required
|
@jwt_required
|
||||||
def logout():
|
def logout():
|
||||||
jti = get_raw_jwt()['jti']
|
jti = get_jwt()['jti']
|
||||||
db.session.add(BlacklistToken(token=jti))
|
db.session.add(BlacklistToken(token=jti))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return jsonify({"msg": "Successfully logged out"}), 200
|
return jsonify({"msg": "Successfully logged out"}), 200
|
||||||
@@ -104,9 +99,9 @@ def logout():
|
|||||||
|
|
||||||
# Endpoint for revoking the current users refresh token
|
# Endpoint for revoking the current users refresh token
|
||||||
@auth_api_bp.route('/logout2', methods=['GET', 'DELETE'])
|
@auth_api_bp.route('/logout2', methods=['GET', 'DELETE'])
|
||||||
@jwt_refresh_token_required
|
@jwt_required(refresh=True)
|
||||||
def logout2():
|
def logout2():
|
||||||
jti = get_raw_jwt()['jti']
|
jti = get_jwt()['jti']
|
||||||
db.session.add(BlacklistToken(token=jti))
|
db.session.add(BlacklistToken(token=jti))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return jsonify({"msg": "Successfully logged out"}), 200
|
return jsonify({"msg": "Successfully logged out"}), 200
|
||||||
@@ -182,7 +177,7 @@ def oidc(redirect_url=None):
|
|||||||
|
|
||||||
|
|
||||||
@auth_api_bp.route('/refresh', methods=['GET'])
|
@auth_api_bp.route('/refresh', methods=['GET'])
|
||||||
@jwt_refresh_token_required
|
@jwt_required(refresh=True)
|
||||||
def refresh():
|
def refresh():
|
||||||
"""Refresh token endpoint. This will generate a new access token from
|
"""Refresh token endpoint. This will generate a new access token from
|
||||||
the refresh token, but will mark that access token as non-fresh,
|
the refresh token, but will mark that access token as non-fresh,
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ Login through API does not start a new session, but instead returns JWT.
|
|||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from flask_jwt_extended import jwt_required, get_current_user, get_jwt_claims
|
from flask_jwt_extended import jwt_required, get_current_user, get_jwt
|
||||||
from flask_restplus import fields, Resource
|
from flask_restx import fields, Resource
|
||||||
|
|
||||||
from backend import db
|
from backend import db
|
||||||
from backend.api import api_control, get_jwt_identity
|
from backend.api import api_control, get_jwt_identity
|
||||||
@@ -37,6 +37,6 @@ class ControlCommand(Resource):
|
|||||||
def post(self):
|
def post(self):
|
||||||
print(get_current_user())
|
print(get_current_user())
|
||||||
print(get_jwt_identity())
|
print(get_jwt_identity())
|
||||||
current_user = {'user': get_current_user(), 'claims': get_jwt_claims()}
|
current_user = {'user': get_current_user(), 'claims': get_jwt()}
|
||||||
args = self.control_command_parser.parse_args()
|
args = self.control_command_parser.parse_args()
|
||||||
return {'time': datetime.utcnow(), 'output': args, 'state': current_user}
|
return {'time': datetime.utcnow(), 'output': args, 'state': current_user}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
from random import *
|
from random import *
|
||||||
from flask import jsonify, Blueprint, request
|
from flask import jsonify, Blueprint, request
|
||||||
from flask_restplus import Resource, reqparse
|
from flask_restx import Resource, reqparse
|
||||||
|
|
||||||
from backend import basic_auth, multi_auth, db, jwt_auth
|
from backend import basic_auth, multi_auth, db, jwt_auth
|
||||||
from backend.api import api_v1, api_bp
|
from backend.api import api_v1, api_bp
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ For example: listing of available auth providers or registration of users.
|
|||||||
Login through API does not start a new session, but instead returns JWT.
|
Login through API does not start a new session, but instead returns JWT.
|
||||||
"""
|
"""
|
||||||
from flask_jwt_extended import jwt_required
|
from flask_jwt_extended import jwt_required
|
||||||
from flask_restplus import fields, Resource
|
from flask_restx import fields, Resource
|
||||||
|
|
||||||
from backend import db
|
from backend import db
|
||||||
from backend.api import api_group
|
from backend.api import api_group
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from flask_restplus import fields
|
from flask_restx import fields
|
||||||
from backend.api import api_user, api_recorder, api_v1
|
from backend.api import api_user, api_recorder, api_v1
|
||||||
|
|
||||||
generic_id_parser = api_v1.parser()
|
generic_id_parser = api_v1.parser()
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from datetime import datetime
|
|||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
|
|
||||||
from flask_jwt_extended import jwt_required
|
from flask_jwt_extended import jwt_required
|
||||||
from flask_restplus import fields, Resource, inputs
|
from flask_restx import fields, Resource, inputs
|
||||||
|
|
||||||
from backend import db, app
|
from backend import db, app
|
||||||
from backend.api import api_recorder
|
from backend.api import api_recorder
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ For example: listing of available auth providers or registration of users.
|
|||||||
Login through API does not start a new session, but instead returns JWT.
|
Login through API does not start a new session, but instead returns JWT.
|
||||||
"""
|
"""
|
||||||
from flask_jwt_extended import jwt_required
|
from flask_jwt_extended import jwt_required
|
||||||
from flask_restplus import fields, Resource
|
from flask_restx import fields, Resource
|
||||||
from sqlalchemy import exc
|
from sqlalchemy import exc
|
||||||
|
|
||||||
from backend import db, app
|
from backend import db, app
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ Login through API does not start a new session, but instead returns JWT.
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
|
|
||||||
from flask_jwt_extended import get_jwt_identity, jwt_required, current_user
|
from flask_jwt_extended import get_jwt_identity, jwt_required, current_user, verify_jwt_in_request
|
||||||
from flask_restplus import Resource, fields, inputs, abort
|
from flask_restx import Resource, inputs, abort
|
||||||
|
|
||||||
from backend import db, app, jwt_auth
|
from backend import db, app, jwt_auth
|
||||||
from backend.api import api_user
|
from backend.api import api_user
|
||||||
from backend.api.models import user_model, recorder_model, generic_id_parser
|
from backend.api.models import user_model, recorder_model, generic_id_parser
|
||||||
from backend.models import Recorder
|
from backend.models.recorder_model import Recorder
|
||||||
from backend.models.user_model import User, Group
|
from backend.models.user_model import User
|
||||||
|
|
||||||
|
|
||||||
user_update_parser = api_user.parser()
|
user_update_parser = api_user.parser()
|
||||||
@@ -27,10 +27,11 @@ user_update_parser.add_argument('last_name', type=str, required=False, store_mis
|
|||||||
|
|
||||||
@api_user.route('/profile')
|
@api_user.route('/profile')
|
||||||
class Profile(Resource):
|
class Profile(Resource):
|
||||||
@jwt_required
|
@jwt_required()
|
||||||
@api_user.marshal_with(user_model)
|
@api_user.marshal_with(user_model)
|
||||||
def get(self):
|
def get(self):
|
||||||
"""Get infos about logged in user."""
|
"""Get infos about logged in user."""
|
||||||
|
print("hey!")
|
||||||
current_user_id = get_jwt_identity()
|
current_user_id = get_jwt_identity()
|
||||||
app.logger.info(current_user_id)
|
app.logger.info(current_user_id)
|
||||||
return User.get_by_identifier(current_user_id)
|
return User.get_by_identifier(current_user_id)
|
||||||
@@ -102,7 +103,7 @@ class UserList(Resource):
|
|||||||
return User.get_all()
|
return User.get_all()
|
||||||
|
|
||||||
@jwt_required
|
@jwt_required
|
||||||
@api_user.doc('create_group')
|
@api_user.doc('create_user')
|
||||||
@api_user.expect(user_model)
|
@api_user.expect(user_model)
|
||||||
@api_user.marshal_with(user_model, code=201)
|
@api_user.marshal_with(user_model, code=201)
|
||||||
def post(self):
|
def post(self):
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import pkgutil
|
|||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
|
|
||||||
from flask_jwt_extended import jwt_required
|
from flask_jwt_extended import jwt_required
|
||||||
from flask_restplus import fields, Resource
|
from flask_restx import fields, Resource
|
||||||
|
|
||||||
from backend import db, app
|
from backend import db, app
|
||||||
from backend.api import api_virtual_command
|
from backend.api import api_virtual_command
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ def logout():
|
|||||||
logout_user()
|
logout_user()
|
||||||
|
|
||||||
|
|
||||||
@jwt_extended.user_claims_loader
|
@jwt_extended.additional_claims_loader
|
||||||
def add_claims_to_access_token(user):
|
def add_claims_to_access_token(user):
|
||||||
if isinstance(user, str):
|
if isinstance(user, str):
|
||||||
return {}
|
return {}
|
||||||
@@ -66,13 +66,13 @@ def user_identity_loader(user):
|
|||||||
return user.email
|
return user.email
|
||||||
|
|
||||||
|
|
||||||
@jwt_extended.user_loader_callback_loader
|
@jwt_extended.user_lookup_loader
|
||||||
def user_loader_callback(identity):
|
def user_loader_callback(identity):
|
||||||
print("### user_loader_callback_loader")
|
print("### user_loader_callback_loader")
|
||||||
return User.get_by_identifier(identity)
|
return User.get_by_identifier(identity)
|
||||||
|
|
||||||
|
|
||||||
@jwt_extended.token_in_blacklist_loader
|
@jwt_extended.token_in_blocklist_loader
|
||||||
def check_if_token_in_blacklist(decrypted_token):
|
def check_if_token_in_blacklist(decrypted_token):
|
||||||
jti = decrypted_token['jti']
|
jti = decrypted_token['jti']
|
||||||
return BlacklistToken.get_by_token(jti) is not None
|
return BlacklistToken.get_by_token(jti) is not None
|
||||||
|
|||||||
Binary file not shown.
@@ -21,14 +21,13 @@ class AccessControlEntry(db.Model):
|
|||||||
name = db.Column(db.Unicode(127), unique=False, nullable=False)
|
name = db.Column(db.Unicode(127), unique=False, nullable=False)
|
||||||
url = db.Column(db.Unicode(2047), unique=False, nullable=True, default="")
|
url = db.Column(db.Unicode(2047), unique=False, nullable=True, default="")
|
||||||
|
|
||||||
required_permission_id = db.Column(db.Integer, db.ForeignKey('permission.id'))
|
required_permission_id = db.Column(db.Integer, db.ForeignKey("permission.id"))
|
||||||
required_permission = db.relationship('Permission', back_populates='access_control_entry')
|
required_permission = db.relationship(
|
||||||
|
"Permission", back_populates="access_control_entry"
|
||||||
__table_args__ = (
|
|
||||||
CheckConstraint('length(name) > 2',
|
|
||||||
name='name_min_length'),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
__table_args__ = (CheckConstraint("length(name) > 2", name="name_min_length"),)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(AccessControlEntry, self).__init__(**kwargs)
|
super(AccessControlEntry, self).__init__(**kwargs)
|
||||||
|
|
||||||
@@ -56,15 +55,24 @@ class AccessControlEntry(db.Model):
|
|||||||
return dict(id=self.id, name=self.name)
|
return dict(id=self.id, name=self.name)
|
||||||
|
|
||||||
def toJSON(self):
|
def toJSON(self):
|
||||||
return json.dumps(self.to_dict(), default=lambda o: o.__dict__,
|
return json.dumps(
|
||||||
sort_keys=True, indent=4)
|
self.to_dict(), default=lambda o: o.__dict__, sort_keys=True, indent=4
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def pre_fill_table():
|
def pre_fill_table():
|
||||||
a_es = {"url": "", }
|
a_es = {
|
||||||
access_entries = [AccessControlEntry(name=room['name'], number=room['room_number'],
|
"url": "",
|
||||||
building_name=room['building_name'], building_number=room['building_number']) for room in
|
}
|
||||||
a_es]
|
access_entries = [
|
||||||
|
AccessControlEntry(
|
||||||
|
name=room["name"],
|
||||||
|
number=room["room_number"],
|
||||||
|
building_name=room["building_name"],
|
||||||
|
building_number=room["building_number"],
|
||||||
|
)
|
||||||
|
for room in a_es
|
||||||
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db.session.bulk_save_objects(access_entries)
|
db.session.bulk_save_objects(access_entries)
|
||||||
|
|||||||
@@ -7,9 +7,18 @@ class ExampleDataItem(db.Model):
|
|||||||
"""
|
"""
|
||||||
just an example class...
|
just an example class...
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
||||||
mac = db.Column(db.String(32), nullable=False, unique=True, index=True)
|
mac = db.Column(db.String(32), nullable=False, unique=True, index=True)
|
||||||
uuid = db.Column(db.String(36), nullable=False, unique=True, index=True, default=str(uuid.uuid4()))
|
uuid = db.Column(
|
||||||
|
db.String(36),
|
||||||
|
nullable=False,
|
||||||
|
unique=True,
|
||||||
|
index=True,
|
||||||
|
default=str(uuid.uuid4()),
|
||||||
|
)
|
||||||
some_string_value = db.Column(db.String, nullable=True, index=True)
|
some_string_value = db.Column(db.String, nullable=True, index=True)
|
||||||
name = db.Column(db.String(128), default="<not set>", nullable=False, index=True, unique=False)
|
name = db.Column(
|
||||||
|
db.String(128), default="<not set>", nullable=False, index=True, unique=False
|
||||||
|
)
|
||||||
description = db.Column(db.String(4096), nullable=True, unique=False)
|
description = db.Column(db.String(4096), nullable=True, unique=False)
|
||||||
|
|||||||
Binary file not shown.
@@ -10,10 +10,11 @@ class Post(db.Model):
|
|||||||
"""
|
"""
|
||||||
A post example class
|
A post example class
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
body = db.Column(db.String(140))
|
body = db.Column(db.String(140))
|
||||||
timestamp = db.Column(db.DateTime)
|
timestamp = db.Column(db.DateTime)
|
||||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Post %r>' % self.body
|
return "<Post %r>" % self.body
|
||||||
|
|||||||
@@ -18,26 +18,32 @@ from backend import db, app, login_manager, LrcException
|
|||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from backend.models.virtual_command_model import virtual_command_recorder_command_table, virtual_command_recorder_table
|
from backend.models.virtual_command_model import (
|
||||||
|
virtual_command_recorder_command_table,
|
||||||
|
virtual_command_recorder_table,
|
||||||
|
)
|
||||||
|
|
||||||
metadata = MetaData()
|
metadata = MetaData()
|
||||||
|
|
||||||
|
|
||||||
class RecorderModel(db.Model):
|
class RecorderModel(db.Model):
|
||||||
__table_args__ = {'extend_existing': True}
|
__table_args__ = {"extend_existing": True}
|
||||||
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
|
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
|
||||||
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow())
|
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow())
|
||||||
last_time_modified = db.Column(db.DateTime, nullable=True, default=None)
|
last_time_modified = db.Column(db.DateTime, nullable=True, default=None)
|
||||||
record_adapter_id = db.Column(db.Unicode(63), unique=True, nullable=False)
|
record_adapter_id = db.Column(db.Unicode(63), unique=True, nullable=False)
|
||||||
model_name = db.Column(db.Unicode(63), unique=True, nullable=False)
|
model_name = db.Column(db.Unicode(63), unique=True, nullable=False)
|
||||||
notes = db.Column(db.Unicode(255), unique=False, nullable=True, default=None)
|
notes = db.Column(db.Unicode(255), unique=False, nullable=True, default=None)
|
||||||
recorder_commands = db.relationship('RecorderCommand', back_populates='recorder_model')
|
recorder_commands = db.relationship(
|
||||||
recorders = db.relationship('Recorder', back_populates='recorder_model')
|
"RecorderCommand", back_populates="recorder_model"
|
||||||
checksum = db.Column(db.String(63), unique=True,
|
)
|
||||||
nullable=False) # checksum of the recorder commands! (see: model_updater.py)
|
recorders = db.relationship("Recorder", back_populates="recorder_model")
|
||||||
|
checksum = db.Column(
|
||||||
|
db.String(63), unique=True, nullable=False
|
||||||
|
) # checksum of the recorder commands! (see: model_updater.py)
|
||||||
last_checksum_change = db.Column(db.DateTime, nullable=True, default=None)
|
last_checksum_change = db.Column(db.DateTime, nullable=True, default=None)
|
||||||
_requires_user = db.Column(db.Integer, default=False, name='requires_user')
|
_requires_user = db.Column(db.Integer, default=False, name="requires_user")
|
||||||
_requires_password = db.Column(db.Integer, default=True, name='requires_password')
|
_requires_password = db.Column(db.Integer, default=True, name="requires_password")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all():
|
def get_all():
|
||||||
@@ -49,11 +55,15 @@ class RecorderModel(db.Model):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_by_adapter_id(name):
|
def get_by_adapter_id(name):
|
||||||
return RecorderModel.query.filter(RecorderModel.record_adapter_id == name).first()
|
return RecorderModel.query.filter(
|
||||||
|
RecorderModel.record_adapter_id == name
|
||||||
|
).first()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_where_adapter_id_contains(adapter_id):
|
def get_where_adapter_id_contains(adapter_id):
|
||||||
return RecorderModel.query.filter(RecorderModel.record_adapter_id.contains(adapter_id)).first()
|
return RecorderModel.query.filter(
|
||||||
|
RecorderModel.record_adapter_id.contains(adapter_id)
|
||||||
|
).first()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_by_checksum(md5_sum):
|
def get_by_checksum(md5_sum):
|
||||||
@@ -80,10 +90,12 @@ class RecorderModel(db.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Recorder(db.Model):
|
class Recorder(db.Model):
|
||||||
__table_args__ = {'extend_existing': True}
|
__table_args__ = {"extend_existing": True}
|
||||||
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
|
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
|
||||||
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow())
|
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow())
|
||||||
last_time_modified = db.Column(db.DateTime, nullable=False, default=datetime.utcnow())
|
last_time_modified = db.Column(
|
||||||
|
db.DateTime, nullable=False, default=datetime.utcnow()
|
||||||
|
)
|
||||||
name = db.Column(db.Unicode(63), unique=True, nullable=False)
|
name = db.Column(db.Unicode(63), unique=True, nullable=False)
|
||||||
model_name = db.Column(db.Unicode(63), unique=False, nullable=False)
|
model_name = db.Column(db.Unicode(63), unique=False, nullable=False)
|
||||||
serial_number = db.Column(db.Unicode(63), unique=True, nullable=True)
|
serial_number = db.Column(db.Unicode(63), unique=True, nullable=True)
|
||||||
@@ -99,16 +111,21 @@ class Recorder(db.Model):
|
|||||||
ssh_port = db.Column(db.Integer, unique=False, nullable=False, default=22)
|
ssh_port = db.Column(db.Integer, unique=False, nullable=False, default=22)
|
||||||
username = db.Column(db.String, nullable=True, default=None)
|
username = db.Column(db.String, nullable=True, default=None)
|
||||||
password = db.Column(db.String, nullable=True, default=None)
|
password = db.Column(db.String, nullable=True, default=None)
|
||||||
_configured_options_json_string = db.Column(db.UnicodeText, default='')
|
_configured_options_json_string = db.Column(db.UnicodeText, default="")
|
||||||
_additional_notes_json_string = db.Column(db.UnicodeText, default='')
|
_additional_notes_json_string = db.Column(db.UnicodeText, default="")
|
||||||
additional_camera_connected = db.Column(db.Boolean, default=False)
|
additional_camera_connected = db.Column(db.Boolean, default=False)
|
||||||
firmware_version = db.Column(db.String, nullable=True, default=None)
|
firmware_version = db.Column(db.String, nullable=True, default=None)
|
||||||
room_id = db.Column(db.Integer, db.ForeignKey('room.id'))
|
room_id = db.Column(db.Integer, db.ForeignKey("room.id"))
|
||||||
room = db.relationship('Room', uselist=False, back_populates='recorder') # one-to-one relation (uselist=False)
|
room = db.relationship(
|
||||||
recorder_model_id = db.Column(db.Integer, db.ForeignKey('recorder_model.id'))
|
"Room", uselist=False, back_populates="recorder"
|
||||||
recorder_model = db.relationship('RecorderModel', back_populates='recorders')
|
) # one-to-one relation (uselist=False)
|
||||||
virtual_commands = db.relationship('VirtualCommand', secondary=virtual_command_recorder_table,
|
recorder_model_id = db.Column(db.Integer, db.ForeignKey("recorder_model.id"))
|
||||||
back_populates='recorders')
|
recorder_model = db.relationship("RecorderModel", back_populates="recorders")
|
||||||
|
virtual_commands = db.relationship(
|
||||||
|
"VirtualCommand",
|
||||||
|
secondary=virtual_command_recorder_table,
|
||||||
|
back_populates="recorders",
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(Recorder, self).__init__(**kwargs)
|
super(Recorder, self).__init__(**kwargs)
|
||||||
@@ -123,16 +140,16 @@ class Recorder(db.Model):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_by_mac(mac: str):
|
def get_by_mac(mac: str):
|
||||||
if mac is None or mac == '':
|
if mac is None or mac == "":
|
||||||
return None
|
return None
|
||||||
mac = mac.replace('-', ':').lower()
|
mac = mac.replace("-", ":").lower()
|
||||||
return Recorder.query.filter(Recorder._mac == mac).first()
|
return Recorder.query.filter(Recorder._mac == mac).first()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all():
|
def get_all():
|
||||||
return Recorder.query.all()
|
return Recorder.query.all()
|
||||||
|
|
||||||
@validates('name')
|
@validates("name")
|
||||||
def validate_name(self, key, value):
|
def validate_name(self, key, value):
|
||||||
assert len(value) > 2
|
assert len(value) > 2
|
||||||
return value
|
return value
|
||||||
@@ -146,7 +163,9 @@ class Recorder(db.Model):
|
|||||||
self._configured_options_json_string = json.dumps(value)
|
self._configured_options_json_string = json.dumps(value)
|
||||||
|
|
||||||
def add_configured_option(self, value: str):
|
def add_configured_option(self, value: str):
|
||||||
self._configured_options_json_string = json.dumps(self.configured_options.append(value))
|
self._configured_options_json_string = json.dumps(
|
||||||
|
self.configured_options.append(value)
|
||||||
|
)
|
||||||
|
|
||||||
@hybrid_property
|
@hybrid_property
|
||||||
def additional_notes(self) -> list:
|
def additional_notes(self) -> list:
|
||||||
@@ -157,7 +176,9 @@ class Recorder(db.Model):
|
|||||||
self._additional_notes_json_string = json.dumps(value)
|
self._additional_notes_json_string = json.dumps(value)
|
||||||
|
|
||||||
def add_additional_notes(self, value: str):
|
def add_additional_notes(self, value: str):
|
||||||
self._additional_notes_json_string = json.dumps(self._additional_notes_json_string.append(value))
|
self._additional_notes_json_string = json.dumps(
|
||||||
|
self._additional_notes_json_string.append(value)
|
||||||
|
)
|
||||||
|
|
||||||
@hybrid_property
|
@hybrid_property
|
||||||
def mac(self) -> str:
|
def mac(self) -> str:
|
||||||
@@ -165,10 +186,10 @@ class Recorder(db.Model):
|
|||||||
|
|
||||||
@mac.setter
|
@mac.setter
|
||||||
def mac(self, value: str):
|
def mac(self, value: str):
|
||||||
if value is None or value == '':
|
if value is None or value == "":
|
||||||
return
|
return
|
||||||
if re.match("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", value.lower()):
|
if re.match("[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", value.lower()):
|
||||||
self._mac = value.replace('-', ':').lower()
|
self._mac = value.replace("-", ":").lower()
|
||||||
else:
|
else:
|
||||||
raise LrcException("'{}' is not a valid MAC Address!".format(value))
|
raise LrcException("'{}' is not a valid MAC Address!".format(value))
|
||||||
|
|
||||||
@@ -205,25 +226,35 @@ class Recorder(db.Model):
|
|||||||
return dict(id=self.id, name=self.name)
|
return dict(id=self.id, name=self.name)
|
||||||
|
|
||||||
def toJSON(self):
|
def toJSON(self):
|
||||||
return json.dumps(self.to_dict(), default=lambda o: o.__dict__,
|
return json.dumps(
|
||||||
sort_keys=True, indent=4)
|
self.to_dict(), default=lambda o: o.__dict__, sort_keys=True, indent=4
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RecorderCommand(db.Model):
|
class RecorderCommand(db.Model):
|
||||||
__table_args__ = {'extend_existing': True}
|
__table_args__ = {"extend_existing": True}
|
||||||
"""Table containing permissions associated with groups."""
|
"""Table containing permissions associated with groups."""
|
||||||
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
|
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
|
||||||
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow())
|
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow())
|
||||||
last_time_modified = db.Column(db.DateTime, nullable=True, default=datetime.utcnow())
|
last_time_modified = db.Column(
|
||||||
|
db.DateTime, nullable=True, default=datetime.utcnow()
|
||||||
|
)
|
||||||
name = db.Column(db.Unicode(63), unique=True, nullable=False)
|
name = db.Column(db.Unicode(63), unique=True, nullable=False)
|
||||||
alternative_name = db.Column(db.Unicode(63), unique=True, nullable=True, default=None)
|
alternative_name = db.Column(
|
||||||
|
db.Unicode(63), unique=True, nullable=True, default=None
|
||||||
|
)
|
||||||
disabled = db.Column(db.Boolean, default=False)
|
disabled = db.Column(db.Boolean, default=False)
|
||||||
description = db.Column(db.Unicode(511), nullable=True, default=None)
|
description = db.Column(db.Unicode(511), nullable=True, default=None)
|
||||||
parameters_string = db.Column(db.String(2047), nullable=True)
|
parameters_string = db.Column(db.String(2047), nullable=True)
|
||||||
recorder_model = db.relationship('RecorderModel', back_populates='recorder_commands')
|
recorder_model = db.relationship(
|
||||||
recorder_model_id = db.Column(db.Integer, db.ForeignKey('recorder_model.id'))
|
"RecorderModel", back_populates="recorder_commands"
|
||||||
virtual_commands = db.relationship('VirtualCommand', secondary=virtual_command_recorder_command_table,
|
)
|
||||||
back_populates='recorder_commands')
|
recorder_model_id = db.Column(db.Integer, db.ForeignKey("recorder_model.id"))
|
||||||
|
virtual_commands = db.relationship(
|
||||||
|
"VirtualCommand",
|
||||||
|
secondary=virtual_command_recorder_command_table,
|
||||||
|
back_populates="recorder_commands",
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all():
|
def get_all():
|
||||||
|
|||||||
@@ -8,12 +8,13 @@ import logging
|
|||||||
from sqlalchemy import MetaData, CheckConstraint
|
from sqlalchemy import MetaData, CheckConstraint
|
||||||
from sqlalchemy.exc import IntegrityError
|
from sqlalchemy.exc import IntegrityError
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime
|
||||||
|
|
||||||
from backend import db, app, login_manager
|
from backend import db
|
||||||
|
from backend.models.recorder_model import Recorder
|
||||||
from backend.tools.scrape_rooms import scrape_rooms
|
from backend.tools.scrape_rooms import scrape_rooms
|
||||||
|
|
||||||
logger = logging.getLogger("lrc."+__name__)
|
logger = logging.getLogger("lrc." + __name__)
|
||||||
|
|
||||||
metadata = MetaData()
|
metadata = MetaData()
|
||||||
|
|
||||||
@@ -22,21 +23,22 @@ class Room(db.Model):
|
|||||||
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
|
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
|
||||||
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow())
|
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow())
|
||||||
name = db.Column(db.Unicode(127), unique=False, nullable=False)
|
name = db.Column(db.Unicode(127), unique=False, nullable=False)
|
||||||
alternate_name = db.Column(db.Unicode(127), unique=False, nullable=True, default=None)
|
alternate_name = db.Column(
|
||||||
|
db.Unicode(127), unique=False, nullable=True, default=None
|
||||||
|
)
|
||||||
comment = db.Column(db.Unicode(2047), unique=False, nullable=True, default="")
|
comment = db.Column(db.Unicode(2047), unique=False, nullable=True, default="")
|
||||||
number = db.Column(db.Unicode(63), unique=False, nullable=True)
|
number = db.Column(db.Unicode(63), unique=False, nullable=True)
|
||||||
building_name = db.Column(db.Unicode(63), unique=False, nullable=True)
|
building_name = db.Column(db.Unicode(63), unique=False, nullable=True)
|
||||||
building_number = db.Column(db.Unicode(63), unique=False, nullable=True)
|
building_number = db.Column(db.Unicode(63), unique=False, nullable=True)
|
||||||
|
|
||||||
recorder = db.relationship('Recorder', uselist=False, back_populates='room') # one-to-one relation (uselist=False)
|
recorder = db.relationship(
|
||||||
|
Recorder, uselist=False, back_populates="room"
|
||||||
|
) # one-to-one relation (uselist=False)
|
||||||
|
|
||||||
__table_args__ = (
|
__table_args__ = (CheckConstraint("length(name) > 2", name="name_min_length"),)
|
||||||
CheckConstraint('length(name) > 2',
|
|
||||||
name='name_min_length'),
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(Room, self).__init__(**kwargs)
|
db.Model.__init__(self, **kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_by_name(name):
|
def get_by_name(name):
|
||||||
@@ -71,17 +73,24 @@ class Room(db.Model):
|
|||||||
return dict(id=self.id, name=self.name)
|
return dict(id=self.id, name=self.name)
|
||||||
|
|
||||||
def toJSON(self):
|
def toJSON(self):
|
||||||
return json.dumps(self.to_dict(), default=lambda o: o.__dict__,
|
return json.dumps(
|
||||||
sort_keys=True, indent=4)
|
self.to_dict(), default=lambda o: o.__dict__, sort_keys=True, indent=4
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def pre_fill_table():
|
def pre_fill_table():
|
||||||
rooms = scrape_rooms()
|
rooms = scrape_rooms()
|
||||||
logger.debug("tada")
|
logger.debug("tada")
|
||||||
logger.debug("got {} rooms".format(len(rooms)))
|
logger.debug("got {} rooms".format(len(rooms)))
|
||||||
db_rooms = [Room(name=room['name'], number=room['room_number'],
|
db_rooms = [
|
||||||
building_name=room['building_name'], building_number=room['building_number']) for room in
|
Room(
|
||||||
rooms]
|
name=room["name"],
|
||||||
|
number=room["room_number"],
|
||||||
|
building_name=room["building_name"],
|
||||||
|
building_number=room["building_number"],
|
||||||
|
)
|
||||||
|
for room in rooms
|
||||||
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db.session.bulk_save_objects(db_rooms)
|
db.session.bulk_save_objects(db_rooms)
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ Example user model and related models
|
|||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from sqlalchemy.orm import relation
|
|
||||||
from sqlalchemy import MetaData
|
from sqlalchemy import MetaData
|
||||||
|
|
||||||
from backend import db, app, login_manager
|
from backend import db, app, login_manager
|
||||||
|
from backend.models.recorder_model import Recorder
|
||||||
from backend.config import Config
|
from backend.config import Config
|
||||||
from backend.models.post_model import Post
|
from backend.models.post_model import Post
|
||||||
from backend.models.example_model import ExampleDataItem
|
from backend.models.example_model import ExampleDataItem
|
||||||
@@ -21,61 +21,76 @@ from hashlib import md5
|
|||||||
|
|
||||||
metadata = MetaData()
|
metadata = MetaData()
|
||||||
|
|
||||||
followers = db.Table('followers',
|
followers = db.Table(
|
||||||
db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),
|
"followers",
|
||||||
db.Column('followed_id', db.Integer, db.ForeignKey('user.id'))
|
db.Column("follower_id", db.Integer, db.ForeignKey("user.id")),
|
||||||
)
|
db.Column("followed_id", db.Integer, db.ForeignKey("user.id")),
|
||||||
|
)
|
||||||
|
|
||||||
acquaintances = db.Table('acquaintances',
|
acquaintances = db.Table(
|
||||||
db.Column('me_id', db.Integer, db.ForeignKey('user.id')),
|
"acquaintances",
|
||||||
db.Column('acquaintance_id', db.Integer, db.ForeignKey('user.id'))
|
db.Column("me_id", db.Integer, db.ForeignKey("user.id")),
|
||||||
)
|
db.Column("acquaintance_id", db.Integer, db.ForeignKey("user.id")),
|
||||||
|
)
|
||||||
|
|
||||||
user_favorite_recorders_table = db.Table('user_favorite_recorders',
|
user_favorite_recorders_table = db.Table(
|
||||||
db.Column('user_id', db.Integer,
|
"user_favorite_recorders",
|
||||||
db.ForeignKey('user.id',
|
db.Column(
|
||||||
onupdate="CASCADE",
|
"user_id",
|
||||||
ondelete="CASCADE"),
|
db.Integer,
|
||||||
primary_key=True),
|
db.ForeignKey("user.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||||
db.Column('recorder_id', db.Integer,
|
primary_key=True,
|
||||||
db.ForeignKey('recorder.id',
|
),
|
||||||
onupdate="CASCADE",
|
db.Column(
|
||||||
ondelete="CASCADE"),
|
"recorder_id",
|
||||||
primary_key=True))
|
db.Integer,
|
||||||
|
db.ForeignKey("recorder.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# This is the association table for the many-to-many relationship between
|
# This is the association table for the many-to-many relationship between
|
||||||
# groups and members - this is, the memberships.
|
# groups and members - this is, the memberships.
|
||||||
user_group_table = db.Table('user_group',
|
user_group_table = db.Table(
|
||||||
db.Column('user_id', db.Integer,
|
"user_group",
|
||||||
db.ForeignKey('user.id',
|
db.Column(
|
||||||
onupdate="CASCADE",
|
"user_id",
|
||||||
ondelete="CASCADE"),
|
db.Integer,
|
||||||
primary_key=True),
|
db.ForeignKey("user.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||||
db.Column('group_id', db.Integer,
|
primary_key=True,
|
||||||
db.ForeignKey('group.id',
|
),
|
||||||
onupdate="CASCADE",
|
db.Column(
|
||||||
ondelete="CASCADE"),
|
"group_id",
|
||||||
primary_key=True))
|
db.Integer,
|
||||||
|
db.ForeignKey("group.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# This is the association table for the many-to-many relationship between
|
# This is the association table for the many-to-many relationship between
|
||||||
# groups and permissions.
|
# groups and permissions.
|
||||||
group_permission_table = db.Table('group_permission',
|
group_permission_table = db.Table(
|
||||||
db.Column('group_id', db.Integer,
|
"group_permission",
|
||||||
db.ForeignKey('group.id',
|
db.Column(
|
||||||
onupdate="CASCADE",
|
"group_id",
|
||||||
ondelete="CASCADE"),
|
db.Integer,
|
||||||
primary_key=True),
|
db.ForeignKey("group.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||||
db.Column('permission_id', db.Integer,
|
primary_key=True,
|
||||||
db.ForeignKey('permission.id',
|
),
|
||||||
onupdate="CASCADE",
|
db.Column(
|
||||||
ondelete="CASCADE"),
|
"permission_id",
|
||||||
primary_key=True))
|
db.Integer,
|
||||||
|
db.ForeignKey("permission.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class User(UserMixin, db.Model):
|
class User(UserMixin, db.Model):
|
||||||
"""
|
"""
|
||||||
Example user model representation.
|
Example user model representation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
social_id = db.Column(db.Unicode(63), nullable=True, unique=True)
|
social_id = db.Column(db.Unicode(63), nullable=True, unique=True)
|
||||||
nickname = db.Column(db.Unicode(63), index=True, unique=True)
|
nickname = db.Column(db.Unicode(63), index=True, unique=True)
|
||||||
@@ -84,32 +99,40 @@ class User(UserMixin, db.Model):
|
|||||||
email = db.Column(db.String(120), nullable=False, index=True, unique=True)
|
email = db.Column(db.String(120), nullable=False, index=True, unique=True)
|
||||||
lang = db.Column(db.Unicode(32), index=False, unique=False)
|
lang = db.Column(db.Unicode(32), index=False, unique=False)
|
||||||
timezone = db.Column(db.Unicode(63), index=False, unique=False)
|
timezone = db.Column(db.Unicode(63), index=False, unique=False)
|
||||||
posts = db.relationship('Post', backref='author', lazy='dynamic')
|
posts = db.relationship(Post, backref="author", lazy="dynamic")
|
||||||
example_data_item = db.relationship('ExampleDataItem', backref='owner')
|
example_data_item = db.relationship(ExampleDataItem, backref="owner")
|
||||||
example_data_item_id = db.Column(db.ForeignKey(ExampleDataItem.id))
|
example_data_item_id = db.Column(db.ForeignKey(ExampleDataItem.id))
|
||||||
about_me = db.Column(db.Unicode(255))
|
about_me = db.Column(db.Unicode(255))
|
||||||
role = db.Column(db.Unicode(63))
|
role = db.Column(db.Unicode(63))
|
||||||
groups = db.relationship('Group', secondary=user_group_table, back_populates='users')
|
groups = db.relationship(
|
||||||
|
"Group", secondary=user_group_table, back_populates="users"
|
||||||
|
)
|
||||||
password = db.Column(db.String(255), nullable=True)
|
password = db.Column(db.String(255), nullable=True)
|
||||||
registered_on = db.Column(db.DateTime, nullable=False, default=datetime.utcnow())
|
registered_on = db.Column(db.DateTime, nullable=False, default=datetime.utcnow())
|
||||||
external_user = db.Column(db.Boolean, default=False)
|
external_user = db.Column(db.Boolean, default=False)
|
||||||
last_seen = db.Column(db.DateTime, default=datetime.utcnow())
|
last_seen = db.Column(db.DateTime, default=datetime.utcnow())
|
||||||
last_time_modified = db.Column(db.DateTime, default=datetime.utcnow())
|
last_time_modified = db.Column(db.DateTime, default=datetime.utcnow())
|
||||||
jwt_exp_delta_seconds = db.Column(db.Integer, nullable=True)
|
jwt_exp_delta_seconds = db.Column(db.Integer, nullable=True)
|
||||||
acquainted = db.relationship('User',
|
acquainted = db.relationship(
|
||||||
secondary=acquaintances,
|
"User",
|
||||||
primaryjoin=(acquaintances.c.me_id == id),
|
secondary=acquaintances,
|
||||||
secondaryjoin=(acquaintances.c.acquaintance_id == id),
|
primaryjoin=(acquaintances.c.me_id == id),
|
||||||
backref=db.backref('acquaintances', lazy='dynamic'),
|
secondaryjoin=(acquaintances.c.acquaintance_id == id),
|
||||||
lazy='dynamic')
|
backref=db.backref("acquaintances", lazy="dynamic"),
|
||||||
followed = db.relationship('User',
|
lazy="dynamic",
|
||||||
secondary=followers,
|
)
|
||||||
primaryjoin=(followers.c.follower_id == id),
|
followed = db.relationship(
|
||||||
secondaryjoin=(followers.c.followed_id == id),
|
"User",
|
||||||
backref=db.backref('followers', lazy='dynamic'),
|
secondary=followers,
|
||||||
lazy='dynamic')
|
primaryjoin=(followers.c.follower_id == id),
|
||||||
|
secondaryjoin=(followers.c.followed_id == id),
|
||||||
|
backref=db.backref("followers", lazy="dynamic"),
|
||||||
|
lazy="dynamic",
|
||||||
|
)
|
||||||
|
|
||||||
favorite_recorders = db.relationship('Recorder', secondary=user_favorite_recorders_table)
|
favorite_recorders = db.relationship(
|
||||||
|
Recorder, secondary=user_favorite_recorders_table
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(User, self).__init__(**kwargs)
|
super(User, self).__init__(**kwargs)
|
||||||
@@ -128,9 +151,13 @@ class User(UserMixin, db.Model):
|
|||||||
:param identifier:
|
:param identifier:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return User.query.filter(or_(User.nickname == identifier,
|
return User.query.filter(
|
||||||
User.email == identifier,
|
or_(
|
||||||
User.id == identifier)).first()
|
User.nickname == identifier,
|
||||||
|
User.email == identifier,
|
||||||
|
User.id == identifier,
|
||||||
|
)
|
||||||
|
).first()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
@@ -174,12 +201,12 @@ class User(UserMixin, db.Model):
|
|||||||
:param nickname:
|
:param nickname:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return re.sub('[^a-zA-Z0-9_.]', '', nickname)
|
return re.sub("[^a-zA-Z0-9_.]", "", nickname)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def authenticate(cls, **kwargs):
|
def authenticate(cls, **kwargs):
|
||||||
email = kwargs.get('email')
|
email = kwargs.get("email")
|
||||||
password = kwargs.get('password')
|
password = kwargs.get("password")
|
||||||
|
|
||||||
if not email or not password:
|
if not email or not password:
|
||||||
return None
|
return None
|
||||||
@@ -244,16 +271,16 @@ class User(UserMixin, db.Model):
|
|||||||
:return: integer|string
|
:return: integer|string
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
payload = jwt.decode(auth_token, app.config.get('SECRET_KEY'))
|
payload = jwt.decode(auth_token, app.config.get("SECRET_KEY"))
|
||||||
is_blacklisted_token = BlacklistToken.check_blacklist(auth_token)
|
is_blacklisted_token = BlacklistToken.check_blacklist(auth_token)
|
||||||
if is_blacklisted_token:
|
if is_blacklisted_token:
|
||||||
return 'Token blacklisted. Please log in again.'
|
return "Token blacklisted. Please log in again."
|
||||||
else:
|
else:
|
||||||
return payload['sub']
|
return payload["sub"]
|
||||||
except jwt.ExpiredSignatureError:
|
except jwt.ExpiredSignatureError:
|
||||||
return 'Signature expired. Please log in again.'
|
return "Signature expired. Please log in again."
|
||||||
except jwt.InvalidTokenError:
|
except jwt.InvalidTokenError:
|
||||||
return 'Invalid token. Please log in again.'
|
return "Invalid token. Please log in again."
|
||||||
|
|
||||||
def encode_auth_token(self):
|
def encode_auth_token(self):
|
||||||
"""
|
"""
|
||||||
@@ -262,15 +289,11 @@ class User(UserMixin, db.Model):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
payload = {
|
payload = {
|
||||||
'exp': datetime.utcnow() + timedelta(days=0, hours=3, seconds=5),
|
"exp": datetime.utcnow() + timedelta(days=0, hours=3, seconds=5),
|
||||||
'iat': datetime.utcnow(),
|
"iat": datetime.utcnow(),
|
||||||
'sub': self.id
|
"sub": self.id,
|
||||||
}
|
}
|
||||||
return jwt.encode(
|
return jwt.encode(payload, app.config.get("SECRET_KEY"), algorithm="HS256")
|
||||||
payload,
|
|
||||||
app.config.get('SECRET_KEY'),
|
|
||||||
algorithm='HS256'
|
|
||||||
)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return e
|
return e
|
||||||
|
|
||||||
@@ -309,7 +332,10 @@ class User(UserMixin, db.Model):
|
|||||||
:param size:
|
:param size:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return 'https://s.gravatar.com/avatar/%s?d=mm&s=%d' % (md5(self.email.encode('utf-8')).hexdigest(), size)
|
return "https://s.gravatar.com/avatar/%s?d=mm&s=%d" % (
|
||||||
|
md5(self.email.encode("utf-8")).hexdigest(),
|
||||||
|
size,
|
||||||
|
)
|
||||||
|
|
||||||
def acquaint(self, user):
|
def acquaint(self, user):
|
||||||
"""
|
"""
|
||||||
@@ -337,24 +363,35 @@ class User(UserMixin, db.Model):
|
|||||||
:param user:
|
:param user:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return self.acquainted.filter(acquaintances.c.acquaintance_id == user.id).count() > 0
|
return (
|
||||||
|
self.acquainted.filter(acquaintances.c.acquaintance_id == user.id).count()
|
||||||
|
> 0
|
||||||
|
)
|
||||||
|
|
||||||
def get_acquaintances(self):
|
def get_acquaintances(self):
|
||||||
"""
|
"""
|
||||||
Returns the list of acquaintances.
|
Returns the list of acquaintances.
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return User.query.join(acquaintances, (acquaintances.c.acquaintance_id == User.id)).filter(
|
return (
|
||||||
acquaintances.c.me_id == self.id).order_by(User.nickname.desc())
|
User.query.join(acquaintances, (acquaintances.c.acquaintance_id == User.id))
|
||||||
|
.filter(acquaintances.c.me_id == self.id)
|
||||||
|
.order_by(User.nickname.desc())
|
||||||
|
)
|
||||||
|
|
||||||
def shared_example_data_items(self):
|
def shared_example_data_items(self):
|
||||||
"""
|
"""
|
||||||
Returns a list of the shared data items.
|
Returns a list of the shared data items.
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return ExampleDataItem.query.join(acquaintances,
|
return (
|
||||||
(acquaintances.c.acquaintance_id == ExampleDataItem.user_id)).filter(
|
ExampleDataItem.query.join(
|
||||||
acquaintances.c.me_id == self.id).order_by(ExampleDataItem.timestamp.desc())
|
acquaintances,
|
||||||
|
(acquaintances.c.acquaintance_id == ExampleDataItem.user_id),
|
||||||
|
)
|
||||||
|
.filter(acquaintances.c.me_id == self.id)
|
||||||
|
.order_by(ExampleDataItem.timestamp.desc())
|
||||||
|
)
|
||||||
|
|
||||||
def follow(self, user):
|
def follow(self, user):
|
||||||
"""
|
"""
|
||||||
@@ -389,26 +426,33 @@ class User(UserMixin, db.Model):
|
|||||||
Returns list of followed posts.
|
Returns list of followed posts.
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return Post.query.join(followers, (followers.c.followed_id == Post.user_id)).filter(
|
return (
|
||||||
followers.c.follower_id == self.id).order_by(Post.timestamp.desc())
|
Post.query.join(followers, (followers.c.followed_id == Post.user_id))
|
||||||
|
.filter(followers.c.follower_id == self.id)
|
||||||
|
.order_by(Post.timestamp.desc())
|
||||||
|
)
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
# return self.__dict__
|
# return self.__dict__
|
||||||
return dict(id=self.id, email=self.email, groups=[g.to_dict() for g in self.groups])
|
return dict(
|
||||||
|
id=self.id, email=self.email, groups=[g.to_dict() for g in self.groups]
|
||||||
|
)
|
||||||
|
|
||||||
def toJSON(self):
|
def toJSON(self):
|
||||||
return json.dumps(self.to_dict(), default=lambda o: o.__dict__,
|
return json.dumps(
|
||||||
sort_keys=True, indent=4)
|
self.to_dict(), default=lambda o: o.__dict__, sort_keys=True, indent=4
|
||||||
|
)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<User %r>' % self.email
|
return "<User %r>" % self.email
|
||||||
|
|
||||||
|
|
||||||
class BlacklistToken(db.Model):
|
class BlacklistToken(db.Model):
|
||||||
"""
|
"""
|
||||||
Token Model for storing JWT tokens
|
Token Model for storing JWT tokens
|
||||||
"""
|
"""
|
||||||
__tablename__ = 'blacklist_tokens'
|
|
||||||
|
__tablename__ = "blacklist_tokens"
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
||||||
token = db.Column(db.String(500), unique=True, nullable=False)
|
token = db.Column(db.String(500), unique=True, nullable=False)
|
||||||
@@ -419,7 +463,7 @@ class BlacklistToken(db.Model):
|
|||||||
self.blacklisted_on = datetime.now()
|
self.blacklisted_on = datetime.now()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<id: token: {}'.format(self.token)
|
return "<id: token: {}".format(self.token)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_by_token(jwt_id):
|
def get_by_token(jwt_id):
|
||||||
@@ -443,10 +487,19 @@ class Group(db.Model):
|
|||||||
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
|
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
|
||||||
name = db.Column(db.Unicode(63), unique=True, nullable=False)
|
name = db.Column(db.Unicode(63), unique=True, nullable=False)
|
||||||
description = db.Column(db.Unicode(255), unique=False, nullable=True, default="")
|
description = db.Column(db.Unicode(255), unique=False, nullable=True, default="")
|
||||||
users = db.relationship('User', secondary=user_group_table, back_populates='groups')
|
users = db.relationship("User", secondary=user_group_table, back_populates="groups")
|
||||||
permissions = db.relationship('Permission', secondary=group_permission_table, back_populates='groups')
|
permissions = db.relationship(
|
||||||
|
"Permission", secondary=group_permission_table, back_populates="groups"
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
# get permissions from kwargs and make Permission objects out of them
|
||||||
|
permissions = kwargs.pop("permissions", [])
|
||||||
|
if permissions is not None:
|
||||||
|
for p in permissions:
|
||||||
|
permission = Permission(name=p)
|
||||||
|
if permission is not None:
|
||||||
|
self.permissions.append(permission)
|
||||||
super(Group, self).__init__(**kwargs)
|
super(Group, self).__init__(**kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -473,36 +526,40 @@ class Group(db.Model):
|
|||||||
return dict(id=self.id, name=self.name)
|
return dict(id=self.id, name=self.name)
|
||||||
|
|
||||||
def toJSON(self):
|
def toJSON(self):
|
||||||
return json.dumps(self.to_dict(), default=lambda o: o.__dict__,
|
return json.dumps(
|
||||||
sort_keys=True, indent=4)
|
self.to_dict(), default=lambda o: o.__dict__, sort_keys=True, indent=4
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Permission(db.Model):
|
class Permission(db.Model):
|
||||||
"""Table containing permissions associated with groups."""
|
"""Table containing permissions associated with groups."""
|
||||||
|
|
||||||
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
|
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
|
||||||
name = db.Column(db.Unicode(63), unique=True, nullable=False)
|
name = db.Column(db.Unicode(63), unique=True, nullable=False)
|
||||||
description = db.Column(db.Unicode(511))
|
description = db.Column(db.Unicode(511))
|
||||||
groups = db.relationship(Group, secondary=group_permission_table,
|
groups = db.relationship(
|
||||||
back_populates='permissions')
|
Group, secondary=group_permission_table, back_populates="permissions"
|
||||||
access_control_entry = db.relationship('AccessControlEntry', back_populates='required_permission')
|
)
|
||||||
|
access_control_entry = db.relationship(
|
||||||
|
"AccessControlEntry", back_populates="required_permission"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@event.listens_for(User.__table__, 'after_create')
|
@event.listens_for(User.__table__, "after_create")
|
||||||
def insert_initial_users(*args, **kwargs):
|
def insert_initial_users(*args, **kwargs):
|
||||||
for u in app.config.get("USERS", []):
|
for u in app.config.get("USERS", []):
|
||||||
db.session.add(User(**u))
|
db.session.add(User(**u))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
@event.listens_for(Group.__table__, 'after_create')
|
@event.listens_for(Permission.__table__, "after_create")
|
||||||
def insert_initial_groups(*args, **kwargs):
|
|
||||||
for g in app.config.get("GROUPS", []):
|
|
||||||
db.session.add(Group(**g))
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
|
|
||||||
@event.listens_for(Permission.__table__, 'after_create')
|
|
||||||
def insert_initial_permissions(*args, **kwargs):
|
def insert_initial_permissions(*args, **kwargs):
|
||||||
for p in app.config.get("PERMISSIONS", []):
|
for p in app.config.get("PERMISSIONS", []):
|
||||||
db.session.add(Permission(name=p))
|
db.session.add(Permission(name=p))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@event.listens_for(Group.__table__, "after_create")
|
||||||
|
def insert_initial_groups(*args, **kwargs):
|
||||||
|
for g in app.config.get("GROUPS", []):
|
||||||
|
db.session.add(Group(**g))
|
||||||
|
db.session.commit()
|
||||||
|
|||||||
@@ -8,31 +8,39 @@ from backend import db
|
|||||||
|
|
||||||
# This is the association table for the many-to-many relationship between
|
# This is the association table for the many-to-many relationship between
|
||||||
# virtual commands and recorder commands.
|
# virtual commands and recorder commands.
|
||||||
virtual_command_recorder_command_table = db.Table('virtual_command_recorder_command',
|
virtual_command_recorder_command_table = db.Table(
|
||||||
db.Column('virtual_command_id', db.Integer,
|
"virtual_command_recorder_command",
|
||||||
db.ForeignKey('virtual_command.id',
|
db.Column(
|
||||||
onupdate="CASCADE",
|
"virtual_command_id",
|
||||||
ondelete="CASCADE"),
|
db.Integer,
|
||||||
primary_key=True),
|
db.ForeignKey("virtual_command.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||||
db.Column('recorder_command_id', db.Integer,
|
primary_key=True,
|
||||||
db.ForeignKey('recorder_command.id',
|
),
|
||||||
onupdate="CASCADE",
|
db.Column(
|
||||||
ondelete="CASCADE"),
|
"recorder_command_id",
|
||||||
primary_key=True))
|
db.Integer,
|
||||||
|
db.ForeignKey("recorder_command.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# This is the association table for the many-to-many relationship between
|
# This is the association table for the many-to-many relationship between
|
||||||
# virtual commands and recorder commands.
|
# virtual commands and recorder commands.
|
||||||
virtual_command_recorder_table = db.Table('virtual_command_recorder',
|
virtual_command_recorder_table = db.Table(
|
||||||
db.Column('virtual_command_id', db.Integer,
|
"virtual_command_recorder",
|
||||||
db.ForeignKey('virtual_command.id',
|
db.Column(
|
||||||
onupdate="CASCADE",
|
"virtual_command_id",
|
||||||
ondelete="CASCADE"),
|
db.Integer,
|
||||||
primary_key=True),
|
db.ForeignKey("virtual_command.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||||
db.Column('recorder_id', db.Integer,
|
primary_key=True,
|
||||||
db.ForeignKey('recorder.id',
|
),
|
||||||
onupdate="CASCADE",
|
db.Column(
|
||||||
ondelete="CASCADE"),
|
"recorder_id",
|
||||||
primary_key=True))
|
db.Integer,
|
||||||
|
db.ForeignKey("recorder.id", onupdate="CASCADE", ondelete="CASCADE"),
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class VirtualCommand(db.Model):
|
class VirtualCommand(db.Model):
|
||||||
@@ -41,16 +49,25 @@ class VirtualCommand(db.Model):
|
|||||||
name = db.Column(db.Unicode(63), unique=True, nullable=False)
|
name = db.Column(db.Unicode(63), unique=True, nullable=False)
|
||||||
description = db.Column(db.Unicode(255), unique=False, nullable=True, default="")
|
description = db.Column(db.Unicode(255), unique=False, nullable=True, default="")
|
||||||
|
|
||||||
recorders = db.relationship('Recorder', secondary=virtual_command_recorder_table,
|
recorders = db.relationship(
|
||||||
back_populates='virtual_commands')
|
"Recorder",
|
||||||
|
secondary=virtual_command_recorder_table,
|
||||||
|
back_populates="virtual_commands",
|
||||||
|
)
|
||||||
|
|
||||||
recorder_commands = db.relationship('RecorderCommand', secondary=virtual_command_recorder_command_table,
|
recorder_commands = db.relationship(
|
||||||
back_populates='virtual_commands')
|
"RecorderCommand",
|
||||||
|
secondary=virtual_command_recorder_command_table,
|
||||||
|
back_populates="virtual_commands",
|
||||||
|
)
|
||||||
|
|
||||||
# parent_virtual_command = db.relationship('VirtualCommand', back_populates='child_virtual_commands')
|
# parent_virtual_command = db.relationship('VirtualCommand', back_populates='child_virtual_commands')
|
||||||
parent_virtual_command_id = db.Column(db.Integer, db.ForeignKey('virtual_command.id'))
|
parent_virtual_command_id = db.Column(
|
||||||
child_virtual_commands = db.relationship('VirtualCommand',
|
db.Integer, db.ForeignKey("virtual_command.id")
|
||||||
backref=backref('parent_virtual_command', remote_side=[id]))
|
)
|
||||||
|
child_virtual_commands = db.relationship(
|
||||||
|
"VirtualCommand", backref=backref("parent_virtual_command", remote_side=[id])
|
||||||
|
)
|
||||||
|
|
||||||
command_order_string = db.Column(db.String)
|
command_order_string = db.Column(db.String)
|
||||||
|
|
||||||
@@ -91,5 +108,6 @@ class VirtualCommand(db.Model):
|
|||||||
return dict(id=self.id, name=self.name, description=self.description)
|
return dict(id=self.id, name=self.name, description=self.description)
|
||||||
|
|
||||||
def toJSON(self):
|
def toJSON(self):
|
||||||
return json.dumps(self.to_dict(), default=lambda o: o.__dict__,
|
return json.dumps(
|
||||||
sort_keys=True, indent=4)
|
self.to_dict(), default=lambda o: o.__dict__, sort_keys=True, indent=4
|
||||||
|
)
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class TelnetAdapter(ABC):
|
|||||||
|
|
||||||
def _run_cmd(self, cmd, timeout=1, auto_connect=True):
|
def _run_cmd(self, cmd, timeout=1, auto_connect=True):
|
||||||
if self.tn is None and not auto_connect:
|
if self.tn is None and not auto_connect:
|
||||||
raise Exception("Not connected!")
|
raise LrcException("Not connected!")
|
||||||
elif self.tn is None:
|
elif self.tn is None:
|
||||||
self._login()
|
self._login()
|
||||||
self.tn.write(cmd)
|
self.tn.write(cmd)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -16,8 +16,8 @@ if not os.path.exists(fe_path) or not os.path.exists(os.path.join(fe_path, "inde
|
|||||||
app.logger.critical(
|
app.logger.critical(
|
||||||
"Frontend path and/or index.html does not exist! Please build frontend before continuing! "
|
"Frontend path and/or index.html does not exist! Please build frontend before continuing! "
|
||||||
"You might want to go to ../frontend and continue from there.")
|
"You might want to go to ../frontend and continue from there.")
|
||||||
print("FATAL: Frontend path wrong or index.html missing -> EXITING!")
|
print("ERROR: Frontend path wrong or index.html missing -> NO WEB-UI AVAILABLE!")
|
||||||
exit()
|
#exit()
|
||||||
fe_bp = Blueprint('frontend', __name__, url_prefix='/', template_folder=os.path.join(fe_path, ""))
|
fe_bp = Blueprint('frontend', __name__, url_prefix='/', template_folder=os.path.join(fe_path, ""))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,12 +19,12 @@ def print_tn(tn_response):
|
|||||||
print(str(tn_response).rstrip())
|
print(str(tn_response).rstrip())
|
||||||
|
|
||||||
|
|
||||||
def run_cmd(tn, cmd, timeout=1):
|
def run_cmd(telnet_con, cmd, _timeout=1):
|
||||||
tn.write(cmd)
|
telnet_con.write(cmd)
|
||||||
out = tn.read_until_non_empty_line()
|
out = telnet_con.read_until_non_empty_line()
|
||||||
res = out
|
res = out
|
||||||
while out is not None and out != "":
|
while out is not None and out != "":
|
||||||
out = tn.read_until_non_empty_line()
|
out = telnet_con.read_until_non_empty_line()
|
||||||
print(out)
|
print(out)
|
||||||
res += out
|
res += out
|
||||||
return res
|
return res
|
||||||
|
|||||||
@@ -1,13 +1,30 @@
|
|||||||
|
# pylint: disable=missing-module-docstring,missing-function-docstring,missing-class-docstring
|
||||||
from backend import LrcException
|
from backend import LrcException
|
||||||
|
|
||||||
|
|
||||||
def exception_decorator(*exceptions):
|
def exception_decorator(*exceptions):
|
||||||
|
"""
|
||||||
|
A decorator that catches specified exceptions and raises them as LrcExceptions.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
*exceptions: A variable-length argument list of exceptions to catch.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A decorator function that can be applied to other functions.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
@exception_decorator(ValueError, TypeError)
|
||||||
|
def my_function():
|
||||||
|
# code here
|
||||||
|
"""
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
def new_func(*args, **kwargs):
|
def new_func(*args, **kwargs):
|
||||||
try:
|
try:
|
||||||
ret = func(*args, **kwargs)
|
ret = func(*args, **kwargs)
|
||||||
return ret
|
return ret
|
||||||
except exceptions as e:
|
except exceptions as e:
|
||||||
raise LrcException(e)
|
raise LrcException(e) from e
|
||||||
|
|
||||||
return new_func
|
return new_func
|
||||||
return decorator
|
|
||||||
|
return decorator
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ import requests
|
|||||||
from requests.auth import HTTPBasicAuth
|
from requests.auth import HTTPBasicAuth
|
||||||
from multiprocessing.pool import ThreadPool
|
from multiprocessing.pool import ThreadPool
|
||||||
from multiprocessing.context import TimeoutError
|
from multiprocessing.context import TimeoutError
|
||||||
|
from tatsu.exceptions import ParseException
|
||||||
|
|
||||||
from ics import Calendar
|
from ics import Calendar
|
||||||
|
from ics.grammar.parse import ParseError
|
||||||
|
|
||||||
from backend import LrcException
|
from backend import LrcException
|
||||||
from backend.config import Config
|
from backend.config import Config
|
||||||
@@ -34,7 +36,7 @@ rec_err_state_log_stream_handler.setLevel(logging.WARNING)
|
|||||||
logger.addHandler(rec_err_state_log_stream_handler)
|
logger.addHandler(rec_err_state_log_stream_handler)
|
||||||
#logger.addHandler(mem_handler)
|
#logger.addHandler(mem_handler)
|
||||||
|
|
||||||
base_url = "https://opencast.bibliothek.kit.edu"
|
base_url = "https://oc-bib-admin.bibliothek.kit.edu"
|
||||||
|
|
||||||
session = requests.session()
|
session = requests.session()
|
||||||
session.auth = HTTPBasicAuth(Config.OPENCAST_USER, Config.OPENCAST_PW)
|
session.auth = HTTPBasicAuth(Config.OPENCAST_USER, Config.OPENCAST_PW)
|
||||||
@@ -66,7 +68,13 @@ def get_calender(rec_id):
|
|||||||
url = get_service_url('org.opencastproject.scheduler') + "/calendars"
|
url = get_service_url('org.opencastproject.scheduler') + "/calendars"
|
||||||
res = session.get(url, params=params)
|
res = session.get(url, params=params)
|
||||||
if res.ok:
|
if res.ok:
|
||||||
return Calendar(res.text)
|
|
||||||
|
try:
|
||||||
|
return Calendar(res.text)
|
||||||
|
except (ValueError, ParseException, IndexError, ParseError) as ex:
|
||||||
|
logger.debug(res.text)
|
||||||
|
logger.error("Could not parse calendar for agent {}! ({})".format(rec_id, ex))
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_capture_agents():
|
def get_capture_agents():
|
||||||
@@ -108,11 +116,17 @@ def get_recorder_adapter(recorder_info: dict) -> RecorderAdapter:
|
|||||||
def check_capture_agent_state(a: dict):
|
def check_capture_agent_state(a: dict):
|
||||||
logger.debug("Checking Agent {}".format(a['name']))
|
logger.debug("Checking Agent {}".format(a['name']))
|
||||||
c = get_calender(a['name'])
|
c = get_calender(a['name'])
|
||||||
|
if c is None:
|
||||||
|
logger.error("Could not get calendar for agent {}!".format(a['name']))
|
||||||
|
return
|
||||||
is_recording_in_calendar = len(list(c.timeline.now())) >= 1
|
is_recording_in_calendar = len(list(c.timeline.now())) >= 1
|
||||||
if is_recording_in_calendar:
|
if is_recording_in_calendar:
|
||||||
logger.info("{} has entry in Calender and should therefore be recording... checking now!".format(a['name']))
|
logger.info("{} has entry in Calender and should therefore be recording... checking now!".format(a['name']))
|
||||||
if a['state'] == "capturing":
|
if a['state'] == "capturing":
|
||||||
recorder_info = get_recorder_by_name(a['name'])
|
recorder_info = get_recorder_by_name(a['name'])
|
||||||
|
if recorder_info is None:
|
||||||
|
logger.error("FATAL: Could not find recorder info for {}!".format(a['name']))
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
rec = get_recorder_adapter(recorder_info)
|
rec = get_recorder_adapter(recorder_info)
|
||||||
if rec.is_recording():
|
if rec.is_recording():
|
||||||
@@ -131,6 +145,9 @@ def check_capture_agent_state(a: dict):
|
|||||||
logger.error("FATAL: {} is not in capturing state...but should be!!".format(a['name']))
|
logger.error("FATAL: {} is not in capturing state...but should be!!".format(a['name']))
|
||||||
else:
|
else:
|
||||||
recorder_info = get_recorder_by_name(a['name'])
|
recorder_info = get_recorder_by_name(a['name'])
|
||||||
|
if recorder_info is None:
|
||||||
|
logger.error("FATAL: Could not find recorder info for {}!".format(a['name']))
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
rec = get_recorder_adapter(recorder_info)
|
rec = get_recorder_adapter(recorder_info)
|
||||||
if rec.is_recording():
|
if rec.is_recording():
|
||||||
@@ -147,7 +164,11 @@ def check_capture_agent_state(a: dict):
|
|||||||
|
|
||||||
|
|
||||||
def ping_capture_agent(a: dict):
|
def ping_capture_agent(a: dict):
|
||||||
recorder_ip = get_recorder_by_name(a['name'])['ip']
|
logger.debug(get_recorder_by_name(a['name']))
|
||||||
|
recorder = get_recorder_by_name(a['name'])
|
||||||
|
if recorder is None:
|
||||||
|
return
|
||||||
|
recorder_ip = recorder['ip']
|
||||||
try:
|
try:
|
||||||
response = subprocess.check_call(
|
response = subprocess.check_call(
|
||||||
['ping', '-W', '10', '-c', '2', recorder_ip],
|
['ping', '-W', '10', '-c', '2', recorder_ip],
|
||||||
|
|||||||
1978
poetry.lock
generated
Normal file
1978
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,15 @@
|
|||||||
[[source]]
|
[tool.poetry]
|
||||||
url = "https://pypi.org/simple"
|
name = "lrc-backend"
|
||||||
verify_ssl = true
|
version = "0.1.0"
|
||||||
name = "pypi"
|
description = ""
|
||||||
|
authors = ["Tobias Kurze <it@t-kurze.de>"]
|
||||||
|
readme = "README.md"
|
||||||
|
|
||||||
[packages]
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.11"
|
||||||
flask = "*"
|
flask = "*"
|
||||||
flask-httpauth = "*"
|
flask-httpauth = "*"
|
||||||
flask-restplus-patched = "*"
|
|
||||||
flask-sqlalchemy = "*"
|
flask-sqlalchemy = "*"
|
||||||
flask-login = "*"
|
|
||||||
pyjwt = "*"
|
pyjwt = "*"
|
||||||
passlib = "*"
|
passlib = "*"
|
||||||
sqlalchemy = "*"
|
sqlalchemy = "*"
|
||||||
@@ -31,8 +32,10 @@ ics = "*"
|
|||||||
coloredlogs = "*"
|
coloredlogs = "*"
|
||||||
pythonping = "*"
|
pythonping = "*"
|
||||||
scapy = "*"
|
scapy = "*"
|
||||||
|
flask-login = "^0.6.2"
|
||||||
|
werkzeug = "2.*"
|
||||||
|
flask-restx = "^1.1.0"
|
||||||
|
|
||||||
[dev-packages]
|
[build-system]
|
||||||
|
requires = ["poetry-core"]
|
||||||
[requires]
|
build-backend = "poetry.core.masonry.api"
|
||||||
python_version = "3.8"
|
|
||||||
Reference in New Issue
Block a user