This commit is contained in:
Untone 2021-07-14 01:25:20 +03:00
commit 4f75f199c0
20 changed files with 837 additions and 690 deletions

2
.gitignore vendored
View File

@ -131,4 +131,4 @@ dmypy.json
temp.* temp.*
*.sqlite3 *.sqlite3
dump.rdb dump.rdb

View File

@ -1,12 +1,12 @@
FROM python:3.9 FROM python:3.9
RUN pip3 install pipenv RUN pip3 install pipenv
WORKDIR /usr/src/app WORKDIR /usr/src/app
COPY Pipfile ./ COPY Pipfile ./
COPY Pipfile.lock ./ COPY Pipfile.lock ./
RUN set -ex && pipenv install --deploy --system RUN set -ex && pipenv install --deploy --system
COPY . . COPY . .

38
Pipfile
View File

@ -1,19 +1,19 @@
[[source]] [[source]]
url = "https://pypi.org/simple" url = "https://pypi.org/simple"
verify_ssl = true verify_ssl = true
name = "pypi" name = "pypi"
[packages] [packages]
aioredis = "*" aioredis = "*"
ariadne = "*" ariadne = "*"
starlette = "*" starlette = "*"
uvicorn = "*" uvicorn = "*"
pydantic = "*" pydantic = "*"
passlib = "*" passlib = "*"
PyJWT = "*" PyJWT = "*"
SQLAlchemy = "*" SQLAlchemy = "*"
[dev-packages] [dev-packages]
[requires] [requires]
python_version = "3.9" python_version = "3.9"

566
Pipfile.lock generated
View File

@ -1,283 +1,283 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "94d9a9de7e56cb5c5781a85d19fd688e64f5cbab46e8d12d194e58ba8d246612" "sha256": "94d9a9de7e56cb5c5781a85d19fd688e64f5cbab46e8d12d194e58ba8d246612"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
"python_version": "3.9" "python_version": "3.9"
}, },
"sources": [ "sources": [
{ {
"name": "pypi", "name": "pypi",
"url": "https://pypi.org/simple", "url": "https://pypi.org/simple",
"verify_ssl": true "verify_ssl": true
} }
] ]
}, },
"default": { "default": {
"aioredis": { "aioredis": {
"hashes": [ "hashes": [
"sha256:15f8af30b044c771aee6787e5ec24694c048184c7b9e54c3b60c750a4b93273a", "sha256:15f8af30b044c771aee6787e5ec24694c048184c7b9e54c3b60c750a4b93273a",
"sha256:b61808d7e97b7cd5a92ed574937a079c9387fdadd22bfbfa7ad2fd319ecc26e3" "sha256:b61808d7e97b7cd5a92ed574937a079c9387fdadd22bfbfa7ad2fd319ecc26e3"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.3.1" "version": "==1.3.1"
}, },
"ariadne": { "ariadne": {
"hashes": [ "hashes": [
"sha256:56bc3609a0512920f06e9312f8ea6db3c8e4a7cd77f31fbed388f5dba6d589c0", "sha256:56bc3609a0512920f06e9312f8ea6db3c8e4a7cd77f31fbed388f5dba6d589c0",
"sha256:e00abd7eb5869b59a638f1e3a7743445bf387236048cf1b0eb9d7c506dcd37c5" "sha256:e00abd7eb5869b59a638f1e3a7743445bf387236048cf1b0eb9d7c506dcd37c5"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.13.0" "version": "==0.13.0"
}, },
"asgiref": { "asgiref": {
"hashes": [ "hashes": [
"sha256:05914d0fa65a21711e732adc6572edad6c8da5f1435c3f0c060689ced5e85195", "sha256:05914d0fa65a21711e732adc6572edad6c8da5f1435c3f0c060689ced5e85195",
"sha256:d36fa91dd90e3aa3c81a6bd426ccc8fb20bd3d22b0cf14a12800289e9c3e2563" "sha256:d36fa91dd90e3aa3c81a6bd426ccc8fb20bd3d22b0cf14a12800289e9c3e2563"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==3.4.0" "version": "==3.4.0"
}, },
"async-timeout": { "async-timeout": {
"hashes": [ "hashes": [
"sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f",
"sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3"
], ],
"markers": "python_full_version >= '3.5.3'", "markers": "python_full_version >= '3.5.3'",
"version": "==3.0.1" "version": "==3.0.1"
}, },
"click": { "click": {
"hashes": [ "hashes": [
"sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a", "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a",
"sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6" "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==8.0.1" "version": "==8.0.1"
}, },
"graphql-core": { "graphql-core": {
"hashes": [ "hashes": [
"sha256:91d96ef0e86665777bb7115d3bbb6b0326f43dc7dbcdd60da5486a27a50cfb11", "sha256:91d96ef0e86665777bb7115d3bbb6b0326f43dc7dbcdd60da5486a27a50cfb11",
"sha256:a755635d1d364a17e8d270347000722351aaa03f1ab7d280878aae82fc68b1f3" "sha256:a755635d1d364a17e8d270347000722351aaa03f1ab7d280878aae82fc68b1f3"
], ],
"markers": "python_version >= '3.6' and python_version < '4'", "markers": "python_version >= '3.6' and python_version < '4'",
"version": "==3.1.5" "version": "==3.1.5"
}, },
"greenlet": { "greenlet": {
"hashes": [ "hashes": [
"sha256:03f28a5ea20201e70ab70518d151116ce939b412961c33827519ce620957d44c", "sha256:03f28a5ea20201e70ab70518d151116ce939b412961c33827519ce620957d44c",
"sha256:06d7ac89e6094a0a8f8dc46aa61898e9e1aec79b0f8b47b2400dd51a44dbc832", "sha256:06d7ac89e6094a0a8f8dc46aa61898e9e1aec79b0f8b47b2400dd51a44dbc832",
"sha256:06ecb43b04480e6bafc45cb1b4b67c785e183ce12c079473359e04a709333b08", "sha256:06ecb43b04480e6bafc45cb1b4b67c785e183ce12c079473359e04a709333b08",
"sha256:096cb0217d1505826ba3d723e8981096f2622cde1eb91af9ed89a17c10aa1f3e", "sha256:096cb0217d1505826ba3d723e8981096f2622cde1eb91af9ed89a17c10aa1f3e",
"sha256:0c557c809eeee215b87e8a7cbfb2d783fb5598a78342c29ade561440abae7d22", "sha256:0c557c809eeee215b87e8a7cbfb2d783fb5598a78342c29ade561440abae7d22",
"sha256:0de64d419b1cb1bfd4ea544bedea4b535ef3ae1e150b0f2609da14bbf48a4a5f", "sha256:0de64d419b1cb1bfd4ea544bedea4b535ef3ae1e150b0f2609da14bbf48a4a5f",
"sha256:14927b15c953f8f2d2a8dffa224aa78d7759ef95284d4c39e1745cf36e8cdd2c", "sha256:14927b15c953f8f2d2a8dffa224aa78d7759ef95284d4c39e1745cf36e8cdd2c",
"sha256:16183fa53bc1a037c38d75fdc59d6208181fa28024a12a7f64bb0884434c91ea", "sha256:16183fa53bc1a037c38d75fdc59d6208181fa28024a12a7f64bb0884434c91ea",
"sha256:206295d270f702bc27dbdbd7651e8ebe42d319139e0d90217b2074309a200da8", "sha256:206295d270f702bc27dbdbd7651e8ebe42d319139e0d90217b2074309a200da8",
"sha256:22002259e5b7828b05600a762579fa2f8b33373ad95a0ee57b4d6109d0e589ad", "sha256:22002259e5b7828b05600a762579fa2f8b33373ad95a0ee57b4d6109d0e589ad",
"sha256:2325123ff3a8ecc10ca76f062445efef13b6cf5a23389e2df3c02a4a527b89bc", "sha256:2325123ff3a8ecc10ca76f062445efef13b6cf5a23389e2df3c02a4a527b89bc",
"sha256:258f9612aba0d06785143ee1cbf2d7361801c95489c0bd10c69d163ec5254a16", "sha256:258f9612aba0d06785143ee1cbf2d7361801c95489c0bd10c69d163ec5254a16",
"sha256:3096286a6072553b5dbd5efbefc22297e9d06a05ac14ba017233fedaed7584a8", "sha256:3096286a6072553b5dbd5efbefc22297e9d06a05ac14ba017233fedaed7584a8",
"sha256:3d13da093d44dee7535b91049e44dd2b5540c2a0e15df168404d3dd2626e0ec5", "sha256:3d13da093d44dee7535b91049e44dd2b5540c2a0e15df168404d3dd2626e0ec5",
"sha256:408071b64e52192869129a205e5b463abda36eff0cebb19d6e63369440e4dc99", "sha256:408071b64e52192869129a205e5b463abda36eff0cebb19d6e63369440e4dc99",
"sha256:598bcfd841e0b1d88e32e6a5ea48348a2c726461b05ff057c1b8692be9443c6e", "sha256:598bcfd841e0b1d88e32e6a5ea48348a2c726461b05ff057c1b8692be9443c6e",
"sha256:5d928e2e3c3906e0a29b43dc26d9b3d6e36921eee276786c4e7ad9ff5665c78a", "sha256:5d928e2e3c3906e0a29b43dc26d9b3d6e36921eee276786c4e7ad9ff5665c78a",
"sha256:5f75e7f237428755d00e7460239a2482fa7e3970db56c8935bd60da3f0733e56", "sha256:5f75e7f237428755d00e7460239a2482fa7e3970db56c8935bd60da3f0733e56",
"sha256:60848099b76467ef09b62b0f4512e7e6f0a2c977357a036de602b653667f5f4c", "sha256:60848099b76467ef09b62b0f4512e7e6f0a2c977357a036de602b653667f5f4c",
"sha256:6b1d08f2e7f2048d77343279c4d4faa7aef168b3e36039cba1917fffb781a8ed", "sha256:6b1d08f2e7f2048d77343279c4d4faa7aef168b3e36039cba1917fffb781a8ed",
"sha256:70bd1bb271e9429e2793902dfd194b653221904a07cbf207c3139e2672d17959", "sha256:70bd1bb271e9429e2793902dfd194b653221904a07cbf207c3139e2672d17959",
"sha256:76ed710b4e953fc31c663b079d317c18f40235ba2e3d55f70ff80794f7b57922", "sha256:76ed710b4e953fc31c663b079d317c18f40235ba2e3d55f70ff80794f7b57922",
"sha256:7920e3eccd26b7f4c661b746002f5ec5f0928076bd738d38d894bb359ce51927", "sha256:7920e3eccd26b7f4c661b746002f5ec5f0928076bd738d38d894bb359ce51927",
"sha256:7db68f15486d412b8e2cfcd584bf3b3a000911d25779d081cbbae76d71bd1a7e", "sha256:7db68f15486d412b8e2cfcd584bf3b3a000911d25779d081cbbae76d71bd1a7e",
"sha256:8833e27949ea32d27f7e96930fa29404dd4f2feb13cce483daf52e8842ec246a", "sha256:8833e27949ea32d27f7e96930fa29404dd4f2feb13cce483daf52e8842ec246a",
"sha256:944fbdd540712d5377a8795c840a97ff71e7f3221d3fddc98769a15a87b36131", "sha256:944fbdd540712d5377a8795c840a97ff71e7f3221d3fddc98769a15a87b36131",
"sha256:9a6b035aa2c5fcf3dbbf0e3a8a5bc75286fc2d4e6f9cfa738788b433ec894919", "sha256:9a6b035aa2c5fcf3dbbf0e3a8a5bc75286fc2d4e6f9cfa738788b433ec894919",
"sha256:9bdcff4b9051fb1aa4bba4fceff6a5f770c6be436408efd99b76fc827f2a9319", "sha256:9bdcff4b9051fb1aa4bba4fceff6a5f770c6be436408efd99b76fc827f2a9319",
"sha256:a9017ff5fc2522e45562882ff481128631bf35da444775bc2776ac5c61d8bcae", "sha256:a9017ff5fc2522e45562882ff481128631bf35da444775bc2776ac5c61d8bcae",
"sha256:aa4230234d02e6f32f189fd40b59d5a968fe77e80f59c9c933384fe8ba535535", "sha256:aa4230234d02e6f32f189fd40b59d5a968fe77e80f59c9c933384fe8ba535535",
"sha256:ad80bb338cf9f8129c049837a42a43451fc7c8b57ad56f8e6d32e7697b115505", "sha256:ad80bb338cf9f8129c049837a42a43451fc7c8b57ad56f8e6d32e7697b115505",
"sha256:adb94a28225005890d4cf73648b5131e885c7b4b17bc762779f061844aabcc11", "sha256:adb94a28225005890d4cf73648b5131e885c7b4b17bc762779f061844aabcc11",
"sha256:b3090631fecdf7e983d183d0fad7ea72cfb12fa9212461a9b708ff7907ffff47", "sha256:b3090631fecdf7e983d183d0fad7ea72cfb12fa9212461a9b708ff7907ffff47",
"sha256:b33b51ab057f8a20b497ffafdb1e79256db0c03ef4f5e3d52e7497200e11f821", "sha256:b33b51ab057f8a20b497ffafdb1e79256db0c03ef4f5e3d52e7497200e11f821",
"sha256:b97c9a144bbeec7039cca44df117efcbeed7209543f5695201cacf05ba3b5857", "sha256:b97c9a144bbeec7039cca44df117efcbeed7209543f5695201cacf05ba3b5857",
"sha256:be13a18cec649ebaab835dff269e914679ef329204704869f2f167b2c163a9da", "sha256:be13a18cec649ebaab835dff269e914679ef329204704869f2f167b2c163a9da",
"sha256:be9768e56f92d1d7cd94185bab5856f3c5589a50d221c166cc2ad5eb134bd1dc", "sha256:be9768e56f92d1d7cd94185bab5856f3c5589a50d221c166cc2ad5eb134bd1dc",
"sha256:c1580087ab493c6b43e66f2bdd165d9e3c1e86ef83f6c2c44a29f2869d2c5bd5", "sha256:c1580087ab493c6b43e66f2bdd165d9e3c1e86ef83f6c2c44a29f2869d2c5bd5",
"sha256:c35872b2916ab5a240d52a94314c963476c989814ba9b519bc842e5b61b464bb", "sha256:c35872b2916ab5a240d52a94314c963476c989814ba9b519bc842e5b61b464bb",
"sha256:c70c7dd733a4c56838d1f1781e769081a25fade879510c5b5f0df76956abfa05", "sha256:c70c7dd733a4c56838d1f1781e769081a25fade879510c5b5f0df76956abfa05",
"sha256:c767458511a59f6f597bfb0032a1c82a52c29ae228c2c0a6865cfeaeaac4c5f5", "sha256:c767458511a59f6f597bfb0032a1c82a52c29ae228c2c0a6865cfeaeaac4c5f5",
"sha256:c87df8ae3f01ffb4483c796fe1b15232ce2b219f0b18126948616224d3f658ee", "sha256:c87df8ae3f01ffb4483c796fe1b15232ce2b219f0b18126948616224d3f658ee",
"sha256:ca1c4a569232c063615f9e70ff9a1e2fee8c66a6fb5caf0f5e8b21a396deec3e", "sha256:ca1c4a569232c063615f9e70ff9a1e2fee8c66a6fb5caf0f5e8b21a396deec3e",
"sha256:cc407b68e0a874e7ece60f6639df46309376882152345508be94da608cc0b831", "sha256:cc407b68e0a874e7ece60f6639df46309376882152345508be94da608cc0b831",
"sha256:da862b8f7de577bc421323714f63276acb2f759ab8c5e33335509f0b89e06b8f", "sha256:da862b8f7de577bc421323714f63276acb2f759ab8c5e33335509f0b89e06b8f",
"sha256:dfe7eac0d253915116ed0cd160a15a88981a1d194c1ef151e862a5c7d2f853d3", "sha256:dfe7eac0d253915116ed0cd160a15a88981a1d194c1ef151e862a5c7d2f853d3",
"sha256:ed1377feed808c9c1139bdb6a61bcbf030c236dd288d6fca71ac26906ab03ba6", "sha256:ed1377feed808c9c1139bdb6a61bcbf030c236dd288d6fca71ac26906ab03ba6",
"sha256:f42ad188466d946f1b3afc0a9e1a266ac8926461ee0786c06baac6bd71f8a6f3", "sha256:f42ad188466d946f1b3afc0a9e1a266ac8926461ee0786c06baac6bd71f8a6f3",
"sha256:f92731609d6625e1cc26ff5757db4d32b6b810d2a3363b0ff94ff573e5901f6f" "sha256:f92731609d6625e1cc26ff5757db4d32b6b810d2a3363b0ff94ff573e5901f6f"
], ],
"markers": "python_version >= '3'", "markers": "python_version >= '3'",
"version": "==1.1.0" "version": "==1.1.0"
}, },
"h11": { "h11": {
"hashes": [ "hashes": [
"sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6", "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6",
"sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042" "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==0.12.0" "version": "==0.12.0"
}, },
"hiredis": { "hiredis": {
"hashes": [ "hashes": [
"sha256:04026461eae67fdefa1949b7332e488224eac9e8f2b5c58c98b54d29af22093e", "sha256:04026461eae67fdefa1949b7332e488224eac9e8f2b5c58c98b54d29af22093e",
"sha256:04927a4c651a0e9ec11c68e4427d917e44ff101f761cd3b5bc76f86aaa431d27", "sha256:04927a4c651a0e9ec11c68e4427d917e44ff101f761cd3b5bc76f86aaa431d27",
"sha256:07bbf9bdcb82239f319b1f09e8ef4bdfaec50ed7d7ea51a56438f39193271163", "sha256:07bbf9bdcb82239f319b1f09e8ef4bdfaec50ed7d7ea51a56438f39193271163",
"sha256:09004096e953d7ebd508cded79f6b21e05dff5d7361771f59269425108e703bc", "sha256:09004096e953d7ebd508cded79f6b21e05dff5d7361771f59269425108e703bc",
"sha256:0adea425b764a08270820531ec2218d0508f8ae15a448568109ffcae050fee26", "sha256:0adea425b764a08270820531ec2218d0508f8ae15a448568109ffcae050fee26",
"sha256:0b39ec237459922c6544d071cdcf92cbb5bc6685a30e7c6d985d8a3e3a75326e", "sha256:0b39ec237459922c6544d071cdcf92cbb5bc6685a30e7c6d985d8a3e3a75326e",
"sha256:0d5109337e1db373a892fdcf78eb145ffb6bbd66bb51989ec36117b9f7f9b579", "sha256:0d5109337e1db373a892fdcf78eb145ffb6bbd66bb51989ec36117b9f7f9b579",
"sha256:0f41827028901814c709e744060843c77e78a3aca1e0d6875d2562372fcb405a", "sha256:0f41827028901814c709e744060843c77e78a3aca1e0d6875d2562372fcb405a",
"sha256:11d119507bb54e81f375e638225a2c057dda748f2b1deef05c2b1a5d42686048", "sha256:11d119507bb54e81f375e638225a2c057dda748f2b1deef05c2b1a5d42686048",
"sha256:1233e303645f468e399ec906b6b48ab7cd8391aae2d08daadbb5cad6ace4bd87", "sha256:1233e303645f468e399ec906b6b48ab7cd8391aae2d08daadbb5cad6ace4bd87",
"sha256:139705ce59d94eef2ceae9fd2ad58710b02aee91e7fa0ccb485665ca0ecbec63", "sha256:139705ce59d94eef2ceae9fd2ad58710b02aee91e7fa0ccb485665ca0ecbec63",
"sha256:1f03d4dadd595f7a69a75709bc81902673fa31964c75f93af74feac2f134cc54", "sha256:1f03d4dadd595f7a69a75709bc81902673fa31964c75f93af74feac2f134cc54",
"sha256:240ce6dc19835971f38caf94b5738092cb1e641f8150a9ef9251b7825506cb05", "sha256:240ce6dc19835971f38caf94b5738092cb1e641f8150a9ef9251b7825506cb05",
"sha256:294a6697dfa41a8cba4c365dd3715abc54d29a86a40ec6405d677ca853307cfb", "sha256:294a6697dfa41a8cba4c365dd3715abc54d29a86a40ec6405d677ca853307cfb",
"sha256:3d55e36715ff06cdc0ab62f9591607c4324297b6b6ce5b58cb9928b3defe30ea", "sha256:3d55e36715ff06cdc0ab62f9591607c4324297b6b6ce5b58cb9928b3defe30ea",
"sha256:3dddf681284fe16d047d3ad37415b2e9ccdc6c8986c8062dbe51ab9a358b50a5", "sha256:3dddf681284fe16d047d3ad37415b2e9ccdc6c8986c8062dbe51ab9a358b50a5",
"sha256:3f5f7e3a4ab824e3de1e1700f05ad76ee465f5f11f5db61c4b297ec29e692b2e", "sha256:3f5f7e3a4ab824e3de1e1700f05ad76ee465f5f11f5db61c4b297ec29e692b2e",
"sha256:508999bec4422e646b05c95c598b64bdbef1edf0d2b715450a078ba21b385bcc", "sha256:508999bec4422e646b05c95c598b64bdbef1edf0d2b715450a078ba21b385bcc",
"sha256:5d2a48c80cf5a338d58aae3c16872f4d452345e18350143b3bf7216d33ba7b99", "sha256:5d2a48c80cf5a338d58aae3c16872f4d452345e18350143b3bf7216d33ba7b99",
"sha256:5dc7a94bb11096bc4bffd41a3c4f2b958257085c01522aa81140c68b8bf1630a", "sha256:5dc7a94bb11096bc4bffd41a3c4f2b958257085c01522aa81140c68b8bf1630a",
"sha256:65d653df249a2f95673976e4e9dd7ce10de61cfc6e64fa7eeaa6891a9559c581", "sha256:65d653df249a2f95673976e4e9dd7ce10de61cfc6e64fa7eeaa6891a9559c581",
"sha256:7492af15f71f75ee93d2a618ca53fea8be85e7b625e323315169977fae752426", "sha256:7492af15f71f75ee93d2a618ca53fea8be85e7b625e323315169977fae752426",
"sha256:7f0055f1809b911ab347a25d786deff5e10e9cf083c3c3fd2dd04e8612e8d9db", "sha256:7f0055f1809b911ab347a25d786deff5e10e9cf083c3c3fd2dd04e8612e8d9db",
"sha256:807b3096205c7cec861c8803a6738e33ed86c9aae76cac0e19454245a6bbbc0a", "sha256:807b3096205c7cec861c8803a6738e33ed86c9aae76cac0e19454245a6bbbc0a",
"sha256:81d6d8e39695f2c37954d1011c0480ef7cf444d4e3ae24bc5e89ee5de360139a", "sha256:81d6d8e39695f2c37954d1011c0480ef7cf444d4e3ae24bc5e89ee5de360139a",
"sha256:87c7c10d186f1743a8fd6a971ab6525d60abd5d5d200f31e073cd5e94d7e7a9d", "sha256:87c7c10d186f1743a8fd6a971ab6525d60abd5d5d200f31e073cd5e94d7e7a9d",
"sha256:8b42c0dc927b8d7c0eb59f97e6e34408e53bc489f9f90e66e568f329bff3e443", "sha256:8b42c0dc927b8d7c0eb59f97e6e34408e53bc489f9f90e66e568f329bff3e443",
"sha256:a00514362df15af041cc06e97aebabf2895e0a7c42c83c21894be12b84402d79", "sha256:a00514362df15af041cc06e97aebabf2895e0a7c42c83c21894be12b84402d79",
"sha256:a39efc3ade8c1fb27c097fd112baf09d7fd70b8cb10ef1de4da6efbe066d381d", "sha256:a39efc3ade8c1fb27c097fd112baf09d7fd70b8cb10ef1de4da6efbe066d381d",
"sha256:a4ee8000454ad4486fb9f28b0cab7fa1cd796fc36d639882d0b34109b5b3aec9", "sha256:a4ee8000454ad4486fb9f28b0cab7fa1cd796fc36d639882d0b34109b5b3aec9",
"sha256:a7928283143a401e72a4fad43ecc85b35c27ae699cf5d54d39e1e72d97460e1d", "sha256:a7928283143a401e72a4fad43ecc85b35c27ae699cf5d54d39e1e72d97460e1d",
"sha256:adf4dd19d8875ac147bf926c727215a0faf21490b22c053db464e0bf0deb0485", "sha256:adf4dd19d8875ac147bf926c727215a0faf21490b22c053db464e0bf0deb0485",
"sha256:ae8427a5e9062ba66fc2c62fb19a72276cf12c780e8db2b0956ea909c48acff5", "sha256:ae8427a5e9062ba66fc2c62fb19a72276cf12c780e8db2b0956ea909c48acff5",
"sha256:b4c8b0bc5841e578d5fb32a16e0c305359b987b850a06964bd5a62739d688048", "sha256:b4c8b0bc5841e578d5fb32a16e0c305359b987b850a06964bd5a62739d688048",
"sha256:b84f29971f0ad4adaee391c6364e6f780d5aae7e9226d41964b26b49376071d0", "sha256:b84f29971f0ad4adaee391c6364e6f780d5aae7e9226d41964b26b49376071d0",
"sha256:c39c46d9e44447181cd502a35aad2bb178dbf1b1f86cf4db639d7b9614f837c6", "sha256:c39c46d9e44447181cd502a35aad2bb178dbf1b1f86cf4db639d7b9614f837c6",
"sha256:cb2126603091902767d96bcb74093bd8b14982f41809f85c9b96e519c7e1dc41", "sha256:cb2126603091902767d96bcb74093bd8b14982f41809f85c9b96e519c7e1dc41",
"sha256:dcef843f8de4e2ff5e35e96ec2a4abbdf403bd0f732ead127bd27e51f38ac298", "sha256:dcef843f8de4e2ff5e35e96ec2a4abbdf403bd0f732ead127bd27e51f38ac298",
"sha256:e3447d9e074abf0e3cd85aef8131e01ab93f9f0e86654db7ac8a3f73c63706ce", "sha256:e3447d9e074abf0e3cd85aef8131e01ab93f9f0e86654db7ac8a3f73c63706ce",
"sha256:f52010e0a44e3d8530437e7da38d11fb822acfb0d5b12e9cd5ba655509937ca0", "sha256:f52010e0a44e3d8530437e7da38d11fb822acfb0d5b12e9cd5ba655509937ca0",
"sha256:f8196f739092a78e4f6b1b2172679ed3343c39c61a3e9d722ce6fcf1dac2824a" "sha256:f8196f739092a78e4f6b1b2172679ed3343c39c61a3e9d722ce6fcf1dac2824a"
], ],
"markers": "python_version >= '3.6'", "markers": "python_version >= '3.6'",
"version": "==2.0.0" "version": "==2.0.0"
}, },
"passlib": { "passlib": {
"hashes": [ "hashes": [
"sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1", "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1",
"sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04" "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.7.4" "version": "==1.7.4"
}, },
"pydantic": { "pydantic": {
"hashes": [ "hashes": [
"sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd", "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd",
"sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739", "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739",
"sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f", "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f",
"sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840", "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840",
"sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23", "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23",
"sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287", "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287",
"sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62", "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62",
"sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b", "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b",
"sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb", "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb",
"sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820", "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820",
"sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3", "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3",
"sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b", "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b",
"sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e", "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e",
"sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3", "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3",
"sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316", "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316",
"sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b", "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b",
"sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4", "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4",
"sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20", "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20",
"sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e", "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e",
"sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505", "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505",
"sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1", "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1",
"sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833" "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.8.2" "version": "==1.8.2"
}, },
"pyjwt": { "pyjwt": {
"hashes": [ "hashes": [
"sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1", "sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1",
"sha256:fba44e7898bbca160a2b2b501f492824fc8382485d3a6f11ba5d0c1937ce6130" "sha256:fba44e7898bbca160a2b2b501f492824fc8382485d3a6f11ba5d0c1937ce6130"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.1.0" "version": "==2.1.0"
}, },
"sqlalchemy": { "sqlalchemy": {
"hashes": [ "hashes": [
"sha256:0f6d467b67a7e5048f1408e8ea60d6caa70be5b386d0eebbf1185ab49cb8c7e4", "sha256:0f6d467b67a7e5048f1408e8ea60d6caa70be5b386d0eebbf1185ab49cb8c7e4",
"sha256:238d78b3110b7f7cffdb70bf9cda686e0d876a849bc78ba4d471aa7b1461f306", "sha256:238d78b3110b7f7cffdb70bf9cda686e0d876a849bc78ba4d471aa7b1461f306",
"sha256:25c0e0f3a7e8c19350086b3c0fe93c4def045cec053d749ef15da710c4d54c81", "sha256:25c0e0f3a7e8c19350086b3c0fe93c4def045cec053d749ef15da710c4d54c81",
"sha256:2f60a2e599cf5cf5e5327ce60f2918b897e42ad9f405d10dd01e37869c0ce6fc", "sha256:2f60a2e599cf5cf5e5327ce60f2918b897e42ad9f405d10dd01e37869c0ce6fc",
"sha256:38ee3a266afef2978e82824650457f70c5d74ec0cadec1b10fe5ed6f038eb5d0", "sha256:38ee3a266afef2978e82824650457f70c5d74ec0cadec1b10fe5ed6f038eb5d0",
"sha256:46361690f1e1c5385994a4caeb6e8126063ff593a5c635700bbc1245de793c1e", "sha256:46361690f1e1c5385994a4caeb6e8126063ff593a5c635700bbc1245de793c1e",
"sha256:46b99eab618cdc1c871ea707b7c52edc23cfea6c750740cd242ba62b5c84de7f", "sha256:46b99eab618cdc1c871ea707b7c52edc23cfea6c750740cd242ba62b5c84de7f",
"sha256:4a67371752fd86d1d03a3b82d4e75404608f6f4d579b9676124079a22a40c79f", "sha256:4a67371752fd86d1d03a3b82d4e75404608f6f4d579b9676124079a22a40c79f",
"sha256:525dd3c2205b11a2bc6d770bf1ec63bde0253fd754b4c19c399d27ddc9dad0d3", "sha256:525dd3c2205b11a2bc6d770bf1ec63bde0253fd754b4c19c399d27ddc9dad0d3",
"sha256:6c8406c3d8c1c7d15da454de15d77f7bb48d14ede5db994f74226c348cf1050e", "sha256:6c8406c3d8c1c7d15da454de15d77f7bb48d14ede5db994f74226c348cf1050e",
"sha256:6da83225a23eaf7b3f48f3d5f53c91b2cf00fbfa48b24a7a758160112dd3e123", "sha256:6da83225a23eaf7b3f48f3d5f53c91b2cf00fbfa48b24a7a758160112dd3e123",
"sha256:7150e5b543b466f45f668b352f7abda27998cc8035f051d1b7e9524ca9eb2f5f", "sha256:7150e5b543b466f45f668b352f7abda27998cc8035f051d1b7e9524ca9eb2f5f",
"sha256:76fbc24311a3d039d6cd147d396719f606d96d1413f3816c028a48e29367f646", "sha256:76fbc24311a3d039d6cd147d396719f606d96d1413f3816c028a48e29367f646",
"sha256:854a7b15750e617e16f8d65dbc004f065a7963544b253b923f16109557648777", "sha256:854a7b15750e617e16f8d65dbc004f065a7963544b253b923f16109557648777",
"sha256:86c079732328f1add097b0b8079cd532b5d28e207fac93e9d6ea5f487506deef", "sha256:86c079732328f1add097b0b8079cd532b5d28e207fac93e9d6ea5f487506deef",
"sha256:8d860c62e3f51623ccd528d8fac44580501df557d4b467cc5581587fcf057719", "sha256:8d860c62e3f51623ccd528d8fac44580501df557d4b467cc5581587fcf057719",
"sha256:9675d5bc7e4f96a7bb2b54d14e9b269a5fb6e5d36ecc7d01f0f65bb9af3185f9", "sha256:9675d5bc7e4f96a7bb2b54d14e9b269a5fb6e5d36ecc7d01f0f65bb9af3185f9",
"sha256:9841762d114018c49483c089fa2d47f7e612e57666323f615913d7d7f46e9606", "sha256:9841762d114018c49483c089fa2d47f7e612e57666323f615913d7d7f46e9606",
"sha256:9eb25bcf9161e2fcbe9eebe8e829719b2334e849183f0e496bf4b83722bcccfa", "sha256:9eb25bcf9161e2fcbe9eebe8e829719b2334e849183f0e496bf4b83722bcccfa",
"sha256:aad3234a41340e9cf6184e621694e2a7233ba3f8aef9b1e6de8cba431b45ebd2", "sha256:aad3234a41340e9cf6184e621694e2a7233ba3f8aef9b1e6de8cba431b45ebd2",
"sha256:b502b5e2f08500cc4b8d29bfc4f51d805adcbc00f8d149e98fda8aae85ddb644", "sha256:b502b5e2f08500cc4b8d29bfc4f51d805adcbc00f8d149e98fda8aae85ddb644",
"sha256:b86d83fefc8a8c394f3490c37e1953bc16c311a3d1d1cf91518793bfb9847fb4", "sha256:b86d83fefc8a8c394f3490c37e1953bc16c311a3d1d1cf91518793bfb9847fb4",
"sha256:c0eb2cd3ad4967fcbdd9e066e8cd91fe2c23c671dbae9952f0b4d3d42832cc5f", "sha256:c0eb2cd3ad4967fcbdd9e066e8cd91fe2c23c671dbae9952f0b4d3d42832cc5f",
"sha256:e0d48456e1aa4f0537f9c9af7be71e1f0659ff68bc1cd538ebc785f6b007bd0d", "sha256:e0d48456e1aa4f0537f9c9af7be71e1f0659ff68bc1cd538ebc785f6b007bd0d",
"sha256:eaee5dd378f6f0d7c3ec49aeeb26564d55ac0ad73b9b4688bf29e66deabddf73", "sha256:eaee5dd378f6f0d7c3ec49aeeb26564d55ac0ad73b9b4688bf29e66deabddf73",
"sha256:f14acb0fd16d404fda9370f93aace682f284340c89c3442ac747c5466ac7e2b5", "sha256:f14acb0fd16d404fda9370f93aace682f284340c89c3442ac747c5466ac7e2b5",
"sha256:f6fc526bd70898489d02bf52c8f0632ab377592ae954d0c0a5bb38d618dddaa9", "sha256:f6fc526bd70898489d02bf52c8f0632ab377592ae954d0c0a5bb38d618dddaa9",
"sha256:fcd84e4d46a86291495d131a7824ba38d2e8278bda9425c50661a04633174319", "sha256:fcd84e4d46a86291495d131a7824ba38d2e8278bda9425c50661a04633174319",
"sha256:ff38ecf89c69a531a7326c2dae71982edfe2f805f3c016cdc5bfd1a04ebf80cb", "sha256:ff38ecf89c69a531a7326c2dae71982edfe2f805f3c016cdc5bfd1a04ebf80cb",
"sha256:ff8bebc7a9d297dff2003460e01db2c20c63818b45fb19170f388b1a72fe5a14" "sha256:ff8bebc7a9d297dff2003460e01db2c20c63818b45fb19170f388b1a72fe5a14"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.4.20" "version": "==1.4.20"
}, },
"starlette": { "starlette": {
"hashes": [ "hashes": [
"sha256:3c8e48e52736b3161e34c9f0e8153b4f32ec5d8995a3ee1d59410d92f75162ed", "sha256:3c8e48e52736b3161e34c9f0e8153b4f32ec5d8995a3ee1d59410d92f75162ed",
"sha256:7d49f4a27f8742262ef1470608c59ddbc66baf37c148e938c7038e6bc7a998aa" "sha256:7d49f4a27f8742262ef1470608c59ddbc66baf37c148e938c7038e6bc7a998aa"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.14.2" "version": "==0.14.2"
}, },
"typing-extensions": { "typing-extensions": {
"hashes": [ "hashes": [
"sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497", "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497",
"sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342", "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342",
"sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84" "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"
], ],
"version": "==3.10.0.0" "version": "==3.10.0.0"
}, },
"uvicorn": { "uvicorn": {
"hashes": [ "hashes": [
"sha256:2a76bb359171a504b3d1c853409af3adbfa5cef374a4a59e5881945a97a93eae", "sha256:2a76bb359171a504b3d1c853409af3adbfa5cef374a4a59e5881945a97a93eae",
"sha256:45ad7dfaaa7d55cab4cd1e85e03f27e9d60bc067ddc59db52a2b0aeca8870292" "sha256:45ad7dfaaa7d55cab4cd1e85e03f27e9d60bc067ddc59db52a2b0aeca8870292"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.14.0" "version": "==0.14.0"
} }
}, },
"develop": {} "develop": {}
} }

View File

@ -1,33 +1,33 @@
# discours-backend-next # discours-backend-next
Tech stack: Tech stack:
- pyjwt - pyjwt
- redis - redis
- ariadne - ariadne
- starlette - starlette
# Local development # Local development
Install redis and pipenv first Install redis and pipenv first
``` ```
brew install redis pipenv brew install redis pipenv
brew services start redis brew services start redis
``` ```
Then run API server Then run API server
``` ```
pipenv shell pipenv shell
python3 server.py python3 server.py
``` ```
# With Docker # With Docker
TODO TODO
# How to do an authorized request # How to do an authorized request
Put the header 'Auth' with token from signInQuery in requests. Put the header 'Auth' with token from signInQuery in requests.

View File

@ -1,77 +1,77 @@
from functools import wraps from functools import wraps
from typing import Optional, Tuple from typing import Optional, Tuple
from graphql import GraphQLResolveInfo from graphql import GraphQLResolveInfo
import jwt import jwt
from starlette.authentication import AuthenticationBackend from starlette.authentication import AuthenticationBackend
from starlette.requests import HTTPConnection from starlette.requests import HTTPConnection
from auth.credentials import AuthCredentials, AuthUser from auth.credentials import AuthCredentials, AuthUser
from auth.token import Token from auth.token import Token
from exceptions import InvalidToken, OperationNotAllowed from exceptions import InvalidToken, OperationNotAllowed
from orm import User from orm import User
from redis import redis from redis import redis
from settings import JWT_AUTH_HEADER from settings import JWT_AUTH_HEADER
class _Authenticate: class _Authenticate:
@classmethod @classmethod
async def verify(cls, token: str): async def verify(cls, token: str):
""" """
Rules for a token to be valid. Rules for a token to be valid.
1. token format is legal && 1. token format is legal &&
token exists in redis database && token exists in redis database &&
token is not expired token is not expired
2. token format is legal && 2. token format is legal &&
token exists in redis database && token exists in redis database &&
token is expired && token is expired &&
token is of specified type token is of specified type
""" """
try: try:
payload = Token.decode(token) payload = Token.decode(token)
except exceptions.ExpiredSignatureError: except exceptions.ExpiredSignatureError:
payload = Token.decode(token, verify_exp=False) payload = Token.decode(token, verify_exp=False)
if not await cls.exists(payload.user_id, token): if not await cls.exists(payload.user_id, token):
raise InvalidToken("Login expired, please login again") raise InvalidToken("Login expired, please login again")
if payload.device == "mobile": # noqa if payload.device == "mobile": # noqa
"we cat set mobile token to be valid forever" "we cat set mobile token to be valid forever"
return payload return payload
except exceptions.JWTDecodeError as e: except exceptions.JWTDecodeError as e:
raise InvalidToken("token format error") from e raise InvalidToken("token format error") from e
else: else:
if not await cls.exists(payload.user_id, token): if not await cls.exists(payload.user_id, token):
raise InvalidToken("Login expired, please login again") raise InvalidToken("Login expired, please login again")
return payload return payload
@classmethod @classmethod
async def exists(cls, user_id, token): async def exists(cls, user_id, token):
token = await redis.execute("GET", f"{user_id}-{token}") token = await redis.execute("GET", f"{user_id}-{token}")
return token is not None return token is not None
class JWTAuthenticate(AuthenticationBackend): class JWTAuthenticate(AuthenticationBackend):
async def authenticate( async def authenticate(
self, request: HTTPConnection self, request: HTTPConnection
) -> Optional[Tuple[AuthCredentials, AuthUser]]: ) -> Optional[Tuple[AuthCredentials, AuthUser]]:
if JWT_AUTH_HEADER not in request.headers: if JWT_AUTH_HEADER not in request.headers:
return AuthCredentials(scopes=[]), AuthUser(user_id=None) return AuthCredentials(scopes=[]), AuthUser(user_id=None)
token = request.headers[JWT_AUTH_HEADER] token = request.headers[JWT_AUTH_HEADER]
try: try:
payload = await _Authenticate.verify(token) payload = await _Authenticate.verify(token)
except Exception as exc: except Exception as exc:
return AuthCredentials(scopes=[], error_message=str(exc)), AuthUser(user_id=None) return AuthCredentials(scopes=[], error_message=str(exc)), AuthUser(user_id=None)
scopes = User.get_permission(user_id=payload.user_id) scopes = User.get_permission(user_id=payload.user_id)
return AuthCredentials(user_id=payload.user_id, scopes=scopes, logged_in=True), AuthUser(user_id=payload.user_id) return AuthCredentials(user_id=payload.user_id, scopes=scopes, logged_in=True), AuthUser(user_id=payload.user_id)
def login_required(func): def login_required(func):
@wraps(func) @wraps(func)
async def wrap(parent, info: GraphQLResolveInfo, *args, **kwargs): async def wrap(parent, info: GraphQLResolveInfo, *args, **kwargs):
auth: AuthCredentials = info.context["request"].auth auth: AuthCredentials = info.context["request"].auth
if not auth.logged_in: if not auth.logged_in:
raise OperationNotAllowed(auth.error_message or "Please login") raise OperationNotAllowed(auth.error_message or "Please login")
return await func(parent, info, *args, **kwargs) return await func(parent, info, *args, **kwargs)
return wrap return wrap

View File

@ -6,12 +6,20 @@ from auth.validations import User
class Identity: class Identity:
@staticmethod @staticmethod
def identity(user_id: int, password: str) -> User: def identity(user_id: int, password: str) -> User:
user = global_session.query(OrmUser).filter_by(id=user_id).first() user = global_session.query(OrmUser).filter_by(id=user_id).first()
if not user: if not user:
raise ObjectNotExist("User does not exist") raise ObjectNotExist("User does not exist")
user = User(**user.dict()) user = User(**user.dict())
if not Password.verify(password, user.password): if not Password.verify(password, user.password):
raise InvalidPassword("Wrong user password") raise InvalidPassword("Wrong user password")
return user return user
@staticmethod
def identity_oauth(oauth_id, input) -> User:
user = global_session.query(OrmUser).filter_by(oauth_id=oauth_id).first()
if not user:
user = OrmUser.create(**input)
user = User(**user.dict())
return user

68
auth/oauth.py Normal file
View File

@ -0,0 +1,68 @@
from authlib.integrations.starlette_client import OAuth
from starlette.responses import PlainTextResponse
from auth.authorize import Authorize
from auth.identity import Identity
from sensitive_settings import CLIENT_ID, CLIENT_SECRET
oauth = OAuth()
oauth.register(
name='facebook',
client_id=CLIENT_ID["FACEBOOK"],
client_secret=CLIENT_SECRET["FACEBOOK"],
access_token_url='https://graph.facebook.com/v11.0/oauth/access_token',
access_token_params=None,
authorize_url='https://www.facebook.com/v11.0/dialog/oauth',
authorize_params=None,
api_base_url='https://graph.facebook.com/',
client_kwargs={'scope': 'user:email'},
)
oauth.register(
name='github',
client_id=CLIENT_ID["GITHUB"],
client_secret=CLIENT_SECRET["GITHUB"],
access_token_url='https://github.com/login/oauth/access_token',
access_token_params=None,
authorize_url='https://github.com/login/oauth/authorize',
authorize_params=None,
api_base_url='https://api.github.com/',
client_kwargs={'scope': 'user:email'},
)
oauth.register(
name='google',
client_id=CLIENT_ID["GOOGLE"],
client_secret=CLIENT_SECRET["GOOGLE"],
access_token_url='https://oauth2.googleapis.com/token',
access_token_params=None,
authorize_url='https://accounts.google.com/o/oauth2/v2/auth',
authorize_params=None,
api_base_url='https://oauth2.googleapis.com/',
client_kwargs={'scope': 'openid email profile'}
)
async def oauth_login(request):
provider = request.path_params['provider']
request.session['provider'] = provider
client = oauth.create_client(provider)
redirect_uri = request.url_for('oauth_authorize')
return await client.authorize_redirect(request, redirect_uri)
async def oauth_authorize(request):
provider = request.session['provider']
client = oauth.create_client(provider)
token = await client.authorize_access_token(request)
resp = await client.get('user', token=token)
profile = resp.json()
oauth_id = profile["id"]
user_input = {
"oauth_id" : oauth_id,
"email" : profile["email"],
"username" : profile["name"]
}
user = Identity.identity_oauth(oauth_id=oauth_id, input=user_input)
token = await Authorize.authorize(user, device="pc", auto_delete=False)
return PlainTextResponse(token)

10
create_crt.sh Normal file
View File

@ -0,0 +1,10 @@
#!/bin/bash
openssl req -newkey rsa:4096 \
-x509 \
-sha256 \
-days 3650 \
-nodes \
-out discours.crt \
-keyout discours.key \
-subj "/C=RU/ST=Moscow/L=Moscow/O=Discours/OU=Site/CN=10.0.0.187"

65
main.py
View File

@ -1,28 +1,37 @@
from importlib import import_module from importlib import import_module
from ariadne import load_schema_from_path, make_executable_schema from ariadne import load_schema_from_path, make_executable_schema
from ariadne.asgi import GraphQL from ariadne.asgi import GraphQL
from starlette.applications import Starlette from starlette.applications import Starlette
from starlette.middleware import Middleware from starlette.middleware import Middleware
from starlette.middleware.authentication import AuthenticationMiddleware from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.middleware.sessions import SessionMiddleware
from auth.authenticate import JWTAuthenticate from starlette.routing import Route
from redis import redis
from resolvers.base import resolvers from auth.authenticate import JWTAuthenticate
from auth.oauth import oauth_login, oauth_authorize
import_module('resolvers') from redis import redis
schema = make_executable_schema(load_schema_from_path("schema.graphql"), resolvers) from resolvers.base import resolvers
middleware = [Middleware(AuthenticationMiddleware, backend=JWTAuthenticate())] import_module('resolvers')
schema = make_executable_schema(load_schema_from_path("schema.graphql"), resolvers)
async def start_up(): middleware = [
await redis.connect() Middleware(AuthenticationMiddleware, backend=JWTAuthenticate()),
Middleware(SessionMiddleware, secret_key="!secret")
]
async def shutdown():
await redis.disconnect() async def start_up():
await redis.connect()
app = Starlette(debug=True, on_startup=[start_up], on_shutdown=[shutdown], middleware=middleware)
app.mount("/", GraphQL(schema, debug=True)) async def shutdown():
await redis.disconnect()
routes = [
Route("/oauth/{provider}", endpoint=oauth_login),
Route("/authorize", endpoint=oauth_authorize)
]
app = Starlette(debug=True, on_startup=[start_up], on_shutdown=[shutdown], middleware=middleware, routes=routes)
app.mount("/", GraphQL(schema, debug=True))

View File

@ -1,8 +1,8 @@
from orm.rbac import Operation, Permission, Role from orm.rbac import Operation, Permission, Role
from orm.user import User from orm.user import User
from orm.message import Message from orm.message import Message
from orm.base import Base, engine from orm.base import Base, engine
__all__ = ["User", "Role", "Operation", "Permission", "Message"] __all__ = ["User", "Role", "Operation", "Permission", "Message"]
Base.metadata.create_all(engine) Base.metadata.create_all(engine)

View File

@ -11,9 +11,11 @@ class User(Base):
email: str = Column(String, nullable=False) email: str = Column(String, nullable=False)
username: str = Column(String, nullable=False, comment="Name") username: str = Column(String, nullable=False, comment="Name")
password: str = Column(String, nullable=False, comment="Password") password: str = Column(String, nullable=True, comment="Password")
role_id: int = Column(ForeignKey("role.id"), nullable=True, comment="Role") role_id: int = Column(ForeignKey("role.id"), nullable=True, comment="Role")
oauth_id: str = Column(String, nullable=True)
@classmethod @classmethod
def get_permission(cls, user_id): def get_permission(cls, user_id):

View File

@ -1,8 +1,8 @@
aioredis aioredis
ariadne ariadne
pyjwt pyjwt
starlette starlette
sqlalchemy sqlalchemy
uvicorn uvicorn
pydantic pydantic
passlib passlib

View File

@ -1,14 +1,14 @@
from resolvers.auth import sign_in, sign_out, register from resolvers.auth import sign_in, sign_out, register
from resolvers.inbox import create_message, delete_message, update_message, get_messages from resolvers.inbox import create_message, delete_message, update_message, get_messages
__all__ = [ __all__ = [
"sign_in", "sign_in",
"sign_out", "sign_out",
"register", "register",
# TODO: "reset_password_code", # TODO: "reset_password_code",
# TODO: "reset_password_confirm", # TODO: "reset_password_confirm",
"create_message", "create_message",
"delete_message", "delete_message",
"get_messages", "get_messages",
"update_messages" "update_messages"
] ]

View File

@ -13,34 +13,34 @@ from settings import JWT_AUTH_HEADER
@mutation.field("registerUser") @mutation.field("registerUser")
async def register(*_, input: dict = None) -> User: async def register(*_, input: dict = None) -> User:
create_user = CreateUser(**input) create_user = CreateUser(**input)
create_user.password = Password.encode(create_user.password) create_user.password = Password.encode(create_user.password)
return User.create(**create_user.dict()) return User.create(**create_user.dict())
@query.field("signIn") @query.field("signIn")
async def sign_in(_, info: GraphQLResolveInfo, id: int, password: str): async def sign_in(_, info: GraphQLResolveInfo, id: int, password: str):
try: try:
device = info.context["request"].headers['device'] device = info.context["request"].headers['device']
except KeyError: except KeyError:
device = "pc" device = "pc"
auto_delete = False if device == "mobile" else True auto_delete = False if device == "mobile" else True
user = Identity.identity(user_id=id, password=password) user = Identity.identity(user_id=id, password=password)
token = await Authorize.authorize(user, device=device, auto_delete=auto_delete) token = await Authorize.authorize(user, device=device, auto_delete=auto_delete)
return {"status" : True, "token" : token} return {"status" : True, "token" : token}
@query.field("signOut") @query.field("signOut")
@login_required @login_required
async def sign_out(_, info: GraphQLResolveInfo): async def sign_out(_, info: GraphQLResolveInfo):
token = info.context["request"].headers[JWT_AUTH_HEADER] token = info.context["request"].headers[JWT_AUTH_HEADER]
status = await Authorize.revoke(token) status = await Authorize.revoke(token)
return {"status" : status} return {"status" : status}
#@query.field("getUser") #@query.field("getUser")
#@login_required #@login_required
async def get_user(*_, id: int): async def get_user(*_, id: int):
return global_session.query(User).filter(User.id == id).first() return global_session.query(User).filter(User.id == id).first()

View File

@ -1,6 +1,15 @@
from ariadne import MutationType, QueryType from ariadne import MutationType, QueryType, SubscriptionType, ScalarType
query = QueryType() query = QueryType()
mutation = MutationType() mutation = MutationType()
subscription = SubscriptionType()
resolvers = [query, mutation]
datetime_scalar = ScalarType("DateTime")
@datetime_scalar.serializer
def serialize_datetime(value):
return value.isoformat()
resolvers = [query, mutation, subscription, datetime_scalar]

View File

@ -1,10 +1,20 @@
from orm import Message, User from orm import Message, User
from orm.base import global_session from orm.base import global_session
from resolvers.base import mutation, query from resolvers.base import mutation, query, subscription
from auth.authenticate import login_required from auth.authenticate import login_required
import asyncio
class MessageQueue:
new_message = asyncio.Queue()
updated_message = asyncio.Queue()
deleted_message = asyncio.Queue()
@mutation.field("createMessage") @mutation.field("createMessage")
@login_required @login_required
async def create_message(_, info, input): async def create_message(_, info, input):
@ -17,6 +27,8 @@ async def create_message(_, info, input):
replyTo = input.get("replyTo") replyTo = input.get("replyTo")
) )
MessageQueue.new_message.put_nowait(new_message)
return { return {
"status": True, "status": True,
"message" : new_message "message" : new_message
@ -61,6 +73,8 @@ async def update_message(_, info, input):
message.body = input["body"] message.body = input["body"]
global_session.commit() global_session.commit()
MessageQueue.updated_message.put_nowait(message)
return { return {
"status" : True, "status" : True,
"message" : message "message" : message
@ -83,6 +97,33 @@ async def delete_message(_, info, id):
global_session.delete(message) global_session.delete(message)
global_session.commit() global_session.commit()
MessageQueue.deleted_message.put_nowait(message)
return { return {
"status" : True "status" : True
} }
@subscription.source("messageCreated")
async def new_message_generator(obj, info):
while True:
new_message = await MessageQueue.new_message.get()
yield new_message
@subscription.source("messageUpdated")
async def updated_message_generator(obj, info):
while True:
message = await MessageQueue.updated_message.get()
yield message
@subscription.source("messageDeleted")
async def deleted_message_generator(obj, info):
while True:
message = await MessageQueue.deleted_message.get()
yield new_message
@subscription.field("messageCreated")
@subscription.field("messageUpdated")
@subscription.field("messageDeleted")
def message_resolver(message, info):
return message

View File

@ -1,167 +1,167 @@
scalar DateTime scalar DateTime
input registerUserInput { input registerUserInput {
email: String! email: String!
username: String! username: String!
password: String! password: String!
} }
type signInPayload { type signInPayload {
status: Boolean! status: Boolean!
error: String error: String
token: String token: String
} }
type ResultPayload { type ResultPayload {
status: Boolean! status: Boolean!
error: String error: String
} }
type Like { type Like {
author: Int! author: Int!
id: Int! id: Int!
shout: Int shout: Int
user: Int user: Int
value: Int! value: Int!
} }
type createMessagePayload { type createMessagePayload {
status: Boolean! status: Boolean!
error: String error: String
message: Message message: Message
} }
input MessageInput { input MessageInput {
body: String! body: String!
replyTo: Int replyTo: Int
} }
input updateMessageInput { input updateMessageInput {
id: Int! id: Int!
body: String! body: String!
} }
type Message { type Message {
author: Int! author: Int!
body: String! body: String!
createdAt: DateTime! createdAt: DateTime!
id: Int! id: Int!
replyTo: Int replyTo: Int
updatedAt: DateTime! updatedAt: DateTime!
visibleForUsers: [Int] visibleForUsers: [Int]
} }
type Mutation { type Mutation {
# message # message
createMessage(input: MessageInput!): createMessagePayload! createMessage(input: MessageInput!): createMessagePayload!
updateMessage(input: updateMessageInput!): createMessagePayload! updateMessage(input: updateMessageInput!): createMessagePayload!
deleteMessage(messageId: Int!): ResultPayload! deleteMessage(messageId: Int!): ResultPayload!
# auth # auth
confirmEmail(token: String!): Token! confirmEmail(token: String!): Token!
invalidateAllTokens: Boolean! invalidateAllTokens: Boolean!
invalidateTokenById(id: Int!): Boolean! invalidateTokenById(id: Int!): Boolean!
requestEmailConfirmation: User! requestEmailConfirmation: User!
requestPasswordReset(email: String!): Boolean! requestPasswordReset(email: String!): Boolean!
resetPassword(password: String!, token: String!): Token! resetPassword(password: String!, token: String!): Token!
signIn(email: String!, password: String!): Token! registerUser(input: registerUserInput!): User!
# signUp(email: String!, password: String!, username: String): User!
registerUser(input: registerUserInput!): User! # shout
createShout(body: String!, replyTo: [Int], title: String, versionOf: [Int], visibleForRoles: [Int], visibleForUsers: [Int]): Message!
# shout deleteShout(shoutId: Int!): Message!
createShout(body: String!, replyTo: [Int], title: String, versionOf: [Int], visibleForRoles: [Int], visibleForUsers: [Int]): Message! rateShout(value: Int!): Boolean!
deleteShout(shoutId: Int!): Message!
rateShout(value: Int!): Boolean! # profile
rateUser(value: Int!): Boolean!
# profile updateOnlineStatus: Boolean!
rateUser(value: Int!): Boolean! updateUsername(username: String!): User!
updateOnlineStatus: Boolean! }
updateUsername(username: String!): User!
} type Query {
# auth / user
type Query { signIn(id: Int!, password: String!): signInPayload!
# auth / user signOut: ResultPayload!
signIn(id: Int!, password: String!): signInPayload! getCurrentUser: User!
signOut: ResultPayload! getTokens: [Token!]!
getCurrentUser: User! isUsernameFree(username: String!): Boolean!
getTokens: [Token!]! getOnline: [User!]!
isUsernameFree(username: String!): Boolean! getUserById(id: Int!): User!
getOnline: [User!]! getUserRating(shout: Int): Int!
getUserById(id: Int!): User!
getUserRating(shout: Int): Int! # messages
getMessages(count: Int = 100, page: Int = 1): [Message!]!
# messages
getMessages(count: Int = 100, page: Int = 1): [Message!]! # shouts
getShoutRating(shout: Int): Int!
# shouts shoutsByAuthor(author: Int): [Shout]!
getShoutRating(shout: Int): Int! shoutsByReplyTo(shout: Int): [Shout]!
shoutsByAuthor(author: Int): [Shout]! shoutsByTags(tags: [String]): [Shout]!
shoutsByReplyTo(shout: Int): [Shout]! shoutsByTime(time: DateTime): [Shout]!
shoutsByTags(tags: [String]): [Shout]! topAuthors: [User]!
shoutsByTime(time: DateTime): [Shout]! topShouts: [Shout]!
topAuthors: [User]! }
topShouts: [Shout]!
} type Role {
id: Int!
type Role { name: String!
id: Int! }
name: String!
} type Shout {
author: Int!
type Shout { body: String!
author: Int! createdAt: DateTime!
body: String! deletedAt: DateTime
createdAt: DateTime! deletedBy: Int
deletedAt: DateTime id: Int!
deletedBy: Int rating: Int
id: Int! published: DateTime! # if there is no published field - it is not published
rating: Int replyTo: Int # another shout
published: DateTime! # if there is no published field - it is not published tags: [String]
replyTo: Int # another shout title: String
tags: [String] updatedAt: DateTime!
title: String versionOf: Int
updatedAt: DateTime! visibleForRoles: [Role]!
versionOf: Int visibleForUsers: [Int]
visibleForRoles: [Role]! }
visibleForUsers: [Int]
} type Proposal {
body: String!
type Proposal { shout: Int!
body: String! range: String # full / 0:2340
shout: Int! author: Int!
range: String # full / 0:2340 createdAt: DateTime!
author: Int! }
createdAt: DateTime!
} type Subscription {
messageCreated: Message!
type Subscription { messageUpdated: Message!
messageCreated: Message! messageDeleted: Message!
messageDeleted: Message!
onlineUpdated: [User!]! onlineUpdated: [User!]!
shoutUpdated: Shout! shoutUpdated: Shout!
userUpdated: User! userUpdated: User!
} }
type Token { type Token {
createdAt: DateTime! createdAt: DateTime!
expiresAt: DateTime expiresAt: DateTime
id: Int! id: Int!
ownerId: Int! ownerId: Int!
usedAt: DateTime usedAt: DateTime
value: String! value: String!
} }
type User { type User {
createdAt: DateTime! createdAt: DateTime!
email: String email: String
emailConfirmed: Boolean emailConfirmed: Boolean
id: Int! id: Int!
muted: Boolean muted: Boolean
rating: Int rating: Int
roles: [Role!]! roles: [Role!]!
updatedAt: DateTime! updatedAt: DateTime!
username: String username: String
userpic: String userpic: String
userpicId: String userpicId: String
wasOnlineAt: DateTime wasOnlineAt: DateTime
} }

View File

@ -1,5 +1,5 @@
import uvicorn import uvicorn
from settings import PORT from settings import PORT
if __name__ == '__main__': if __name__ == '__main__':
uvicorn.run("main:app", host="0.0.0.0", port=PORT, reload=True) uvicorn.run("main:app", host="0.0.0.0", port=PORT, ssl_keyfile="discours.key", ssl_certfile="discours.crt", reload=True)

View File

@ -1,10 +1,10 @@
from pathlib import Path from pathlib import Path
PORT = 24579 PORT = 8081
SQLITE_URI = Path(__file__).parent / "database.sqlite3" SQLITE_URI = Path(__file__).parent / "database.sqlite3"
JWT_ALGORITHM = "HS256" JWT_ALGORITHM = "HS256"
JWT_SECRET_KEY = "8f1bd7696ffb482d8486dfbc6e7d16dd-secret-key" JWT_SECRET_KEY = "8f1bd7696ffb482d8486dfbc6e7d16dd-secret-key"
JWT_LIFE_SPAN = 24 * 60 * 60 # seconds JWT_LIFE_SPAN = 24 * 60 * 60 # seconds
JWT_AUTH_HEADER = "Auth" JWT_AUTH_HEADER = "Auth"
REDIS_URL = "redis://127.0.0.1" REDIS_URL = "redis://127.0.0.1"