! This merge denotes the MVP completion for the backend decentralization endpoints

More routes are to be added in the future but for now this should suffice

Doc's are now required and the roadmap need to be updated as well
This commit is contained in:
shockrah 2021-05-12 14:27:34 -07:00
commit cfbee6d887
22 changed files with 584 additions and 256 deletions

261
json-api/Cargo.lock generated
View File

@ -14,18 +14,9 @@ checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
[[package]]
name = "aho-corasick"
version = "0.6.10"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5"
dependencies = [
"memchr",
]
[[package]]
name = "aho-corasick"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
@ -212,9 +203,9 @@ dependencies = [
[[package]]
name = "const_fn"
version = "0.4.6"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "076a6803b0dacd6a88cfe64deba628b01533ff5ef265687e6938280c1afd0a28"
checksum = "402da840495de3f976eaefc3485b7f5eb5b0bf9761f9a47be27fe975b3b8c2ec"
[[package]]
name = "core-foundation"
@ -233,10 +224,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
[[package]]
name = "cpuid-bool"
version = "0.1.2"
name = "cpufeatures"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
checksum = "5cd5a7748210e7ec1a9696610b1015e6e31fbf58f77a160801f124bd1c36592a"
[[package]]
name = "crc32fast"
@ -254,7 +245,8 @@ dependencies = [
"mysql_async",
"rand 0.8.3",
"serde",
"tokio 1.4.0",
"serde_json",
"tokio 1.5.0",
]
[[package]]
@ -272,15 +264,6 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
[[package]]
name = "dotenv"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "400b347fe65ccfbd8f545c9d9a75d04b0caf23fec49aaa838a9a05398f94c019"
dependencies = [
"regex 0.2.11",
]
[[package]]
name = "flate2"
version = "1.0.20"
@ -327,9 +310,9 @@ dependencies = [
[[package]]
name = "futures"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1"
checksum = "a9d5813545e459ad3ca1bff9915e9ad7f1a47dc6a91b627ce321d5863b7dd253"
dependencies = [
"futures-channel",
"futures-core",
@ -342,9 +325,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939"
checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25"
dependencies = [
"futures-core",
"futures-sink",
@ -352,15 +335,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94"
checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815"
[[package]]
name = "futures-executor"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1"
checksum = "10f6cb7042eda00f0049b1d2080aa4b93442997ee507eb3828e8bd7577f94c9d"
dependencies = [
"futures-core",
"futures-task",
@ -369,15 +352,15 @@ dependencies = [
[[package]]
name = "futures-io"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59"
checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04"
[[package]]
name = "futures-macro"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7"
checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b"
dependencies = [
"proc-macro-hack",
"proc-macro2",
@ -387,21 +370,21 @@ dependencies = [
[[package]]
name = "futures-sink"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3"
checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23"
[[package]]
name = "futures-task"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80"
checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc"
[[package]]
name = "futures-util"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1"
checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025"
dependencies = [
"futures-channel",
"futures-core",
@ -451,9 +434,9 @@ dependencies = [
[[package]]
name = "h2"
version = "0.3.2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc018e188373e2777d0ef2467ebff62a08e66c3f5857b23c8fbec3018210dc00"
checksum = "825343c4eef0b63f541f8903f395dc5beb362a979b5799a84062527ef1e37726"
dependencies = [
"bytes 1.0.1",
"fnv",
@ -463,7 +446,7 @@ dependencies = [
"http",
"indexmap",
"slab",
"tokio 1.4.0",
"tokio 1.5.0",
"tokio-util",
"tracing",
]
@ -510,21 +493,21 @@ dependencies = [
[[package]]
name = "httparse"
version = "1.3.6"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc35c995b9d93ec174cf9a27d425c7892722101e14993cd227fdb51d70cf9589"
checksum = "4a1ce40d6fc9764887c2fdc7305c3dcc429ba11ff981c1509416afd5697e4437"
[[package]]
name = "httpdate"
version = "0.3.2"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
checksum = "05842d0d43232b23ccb7060ecb0f0626922c21f30012e97b767b30afd4a5d4b9"
[[package]]
name = "hyper"
version = "0.14.5"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bf09f61b52cfcf4c00de50df88ae423d6c02354e385a86341133b5338630ad1"
checksum = "1e5f105c494081baa3bf9e200b279e27ec1623895cd504c7dbef8d0b080fcf54"
dependencies = [
"bytes 1.0.1",
"futures-channel",
@ -538,7 +521,7 @@ dependencies = [
"itoa",
"pin-project",
"socket2",
"tokio 1.4.0",
"tokio 1.5.0",
"tower-service",
"tracing",
"want",
@ -546,9 +529,9 @@ dependencies = [
[[package]]
name = "idna"
version = "0.2.2"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21"
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
dependencies = [
"matches",
"unicode-bidi",
@ -606,7 +589,6 @@ dependencies = [
"bcrypt",
"clap",
"db",
"dotenv",
"futures",
"getrandom 0.1.16",
"hyper",
@ -616,7 +598,7 @@ dependencies = [
"rand 0.7.3",
"serde",
"serde_json",
"tokio 1.4.0",
"tokio 1.5.0",
"tokio-test",
"tokio-tungstenite",
"url",
@ -644,9 +626,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lexical"
version = "5.2.1"
version = "5.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a11b9b772d42a9cf7ff6742d4695054070432742b19cecea07e7c4eca8173d25"
checksum = "f404a90a744e32e8be729034fc33b90cf2a56418fbf594d69aa3c0214ad414e5"
dependencies = [
"cfg-if 1.0.0",
"lexical-core",
@ -654,9 +636,9 @@ dependencies = [
[[package]]
name = "lexical-core"
version = "0.7.5"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374"
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
dependencies = [
"arrayvec",
"bitflags",
@ -667,15 +649,15 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.93"
version = "0.2.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
[[package]]
name = "libz-sys"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "602113192b08db8f38796c4e85c39e960c145965140e918018bcde1952429655"
checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
dependencies = [
"cc",
"pkg-config",
@ -684,9 +666,9 @@ dependencies = [
[[package]]
name = "lock_api"
version = "0.4.3"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176"
checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
dependencies = [
"scopeguard",
]
@ -717,9 +699,9 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
[[package]]
name = "memchr"
version = "2.3.4"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
[[package]]
name = "miniz_oxide"
@ -774,7 +756,7 @@ dependencies = [
"serde",
"serde_json",
"thiserror",
"tokio 1.4.0",
"tokio 1.5.0",
"tokio-native-tls",
"tokio-util",
"twox-hash",
@ -800,7 +782,7 @@ dependencies = [
"num-bigint 0.3.2",
"num-traits",
"rand 0.8.3",
"regex 1.4.5",
"regex",
"rust_decimal",
"serde",
"serde_json",
@ -903,9 +885,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "openssl"
version = "0.10.33"
version = "0.10.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577"
checksum = "6d7830286ad6a3973c0f1d9b73738f69c76b739301d0229c4b96501695cbe4c8"
dependencies = [
"bitflags",
"cfg-if 1.0.0",
@ -923,9 +905,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
[[package]]
name = "openssl-sys"
version = "0.9.61"
version = "0.9.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f"
checksum = "fa52160d45fa2e7608d504b7c3a3355afed615e6d8b627a74458634ba21b69bd"
dependencies = [
"autocfg",
"cc",
@ -967,7 +949,7 @@ checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb"
dependencies = [
"base64 0.13.0",
"once_cell",
"regex 1.4.5",
"regex",
]
[[package]]
@ -978,18 +960,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pin-project"
version = "1.0.6"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc174859768806e91ae575187ada95c91a29e96a98dc5d2cd9a1fed039501ba6"
checksum = "c7509cc106041c40a4518d2af7a61530e1eed0e6285296a3d8c5472806ccc4a4"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.0.6"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a490329918e856ed1b083f244e3bfe2d8c4f336407e4ea9e1a9f479ff09049e5"
checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f"
dependencies = [
"proc-macro2",
"quote",
@ -1139,51 +1121,29 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.2.5"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
version = "0.2.11"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384"
checksum = "ce5f1ceb7f74abbce32601642fcf8e8508a8a8991e0621c7d750295b9095702b"
dependencies = [
"aho-corasick 0.6.10",
"aho-corasick",
"memchr",
"regex-syntax 0.5.6",
"thread_local",
"utf8-ranges",
]
[[package]]
name = "regex"
version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
dependencies = [
"aho-corasick 0.7.15",
"memchr",
"regex-syntax 0.6.23",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.5.6"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7"
dependencies = [
"ucd-util",
]
[[package]]
name = "regex-syntax"
version = "0.6.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "remove_dir_all"
@ -1211,9 +1171,9 @@ dependencies = [
[[package]]
name = "rust_decimal"
version = "1.10.3"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc7f5b8840fb1f83869a3e1dfd06d93db79ea05311ac5b42b8337d3371caa4f1"
checksum = "72e80c4f9a71b5949e283189c3c3ae35fedddecfe112e75c9d58751c36605b62"
dependencies = [
"arrayvec",
"num-traits",
@ -1322,13 +1282,13 @@ dependencies = [
[[package]]
name = "sha-1"
version = "0.9.4"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f"
checksum = "b659df5fc3ce22274daac600ffb845300bd2125bcfaec047823075afdab81c00"
dependencies = [
"block-buffer",
"cfg-if 1.0.0",
"cpuid-bool",
"cpufeatures",
"digest",
"opaque-debug",
]
@ -1341,13 +1301,13 @@ checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
[[package]]
name = "sha2"
version = "0.9.3"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de"
checksum = "d8f6b75b17576b792bef0db1bcc4b8b8bcdf9506744cf34b974195487af6cff2"
dependencies = [
"block-buffer",
"cfg-if 1.0.0",
"cpuid-bool",
"cpufeatures",
"digest",
"opaque-debug",
]
@ -1374,9 +1334,9 @@ dependencies = [
[[package]]
name = "slab"
version = "0.4.2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
[[package]]
name = "smallvec"
@ -1472,9 +1432,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.69"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
dependencies = [
"proc-macro2",
"quote",
@ -1524,15 +1484,6 @@ dependencies = [
"syn",
]
[[package]]
name = "thread_local"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
dependencies = [
"lazy_static",
]
[[package]]
name = "time"
version = "0.1.43"
@ -1611,9 +1562,9 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722"
checksum = "83f0c8e7c0addab50b663055baf787d0af7f413a46e6e7fb9559a4e4db7137a5"
dependencies = [
"autocfg",
"bytes 1.0.1",
@ -1647,7 +1598,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
dependencies = [
"native-tls",
"tokio 1.4.0",
"tokio 1.5.0",
]
[[package]]
@ -1670,22 +1621,22 @@ dependencies = [
"futures-util",
"log",
"pin-project",
"tokio 1.4.0",
"tokio 1.5.0",
"tungstenite",
]
[[package]]
name = "tokio-util"
version = "0.6.5"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5143d049e85af7fbc36f5454d990e62c2df705b3589f123b71f441b6b59f443f"
checksum = "940a12c99365c31ea8dd9ba04ec1be183ffe4920102bb7122c2f515437601e8e"
dependencies = [
"bytes 1.0.1",
"futures-core",
"futures-sink",
"log",
"pin-project-lite 0.2.6",
"tokio 1.4.0",
"tokio 1.5.0",
]
[[package]]
@ -1696,9 +1647,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
[[package]]
name = "tracing"
version = "0.1.25"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f"
checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d"
dependencies = [
"cfg-if 1.0.0",
"pin-project-lite 0.2.6",
@ -1707,9 +1658,9 @@ dependencies = [
[[package]]
name = "tracing-core"
version = "0.1.17"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f"
checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052"
dependencies = [
"lazy_static",
]
@ -1757,12 +1708,6 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
[[package]]
name = "ucd-util"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c85f514e095d348c279b1e5cd76795082cf15bd59b93207832abe0b1d8fed236"
[[package]]
name = "unicode-bidi"
version = "0.3.5"
@ -1789,9 +1734,9 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "untrusted"
@ -1813,15 +1758,9 @@ dependencies = [
[[package]]
name = "utf-8"
version = "0.7.5"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7"
[[package]]
name = "utf8-ranges"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "uuid"
@ -1834,9 +1773,9 @@ dependencies = [
[[package]]
name = "vcpkg"
version = "0.2.11"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
checksum = "cbdbff6266a24120518560b5dc983096efb98462e51d0d68169895b237be3e5d"
[[package]]
name = "vec_map"

View File

@ -18,8 +18,6 @@ futures = "0.3"
url = "2.2.1"
dotenv = "0.9.0"
# Crypto things
getrandom = "0.1"
bcrypt = "0.8"

View File

@ -7,5 +7,9 @@ dep:
run:
cargo run --features rtc --release -- -s
test:
cd ../ && bash scripts/run-api-tests.sh
rm -f *.png
clean:
cargo clean

View File

@ -1,5 +1,6 @@
import subprocess
import os
import sys
import json
class Server:
@ -8,6 +9,15 @@ class Server:
self.wsurl = meta.get('wsurl')
self.serv_name = meta.get('name')
def __str__(self) -> str:
fields = {
'url': self.url,
'wsurl': self.wsurl,
'name': self.serv_name
}
return str(fields)
class Admin:
def __init__(self, user: dict, server: dict):
self.id = user.get('id')
@ -20,7 +30,15 @@ class Admin:
self.server = Server(server)
def __str__(self) -> str:
return f'{self.name}#{self.id}'
acc = {
'id': self.id,
'name': self.name,
'permissions': self.permissions,
'secret': self.secret,
'jwt': self.jwt,
}
container = {'user': acc, 'server': str(self.server)}
return str(container)
def create_admin() -> Admin :
CARGO_BIN = os.getenv('CARGO_BIN')
@ -37,10 +55,12 @@ def create_admin() -> Admin :
if user is None or server is None:
print('stderr ', e)
print('stdout ', proc.stdout)
print(f'User/Server Data was not serializable => {raw}', file=sys.stderr)
return None
else:
return Admin(user, server)
except:
except Exception as e:
print(f'[create_admin] General exception caught in parsing => {e}', file=sys.stderr)
print('stderr ', proc.stderr)
print('stdout ', proc.stdout)
return None

View File

@ -2,12 +2,14 @@ from time import time
from request import Request
from config import create_admin, Admin
from config import Server
from json import dumps as to_json
RESPONSES = []
VOICE_CHAN = 1
TEXT_CHAN = 2
def login() -> (Request, str):
def login(admin: Admin) -> (Request, str):
print(f'Provided admin account {admin}')
req = Request(
admin.server.url + '/login',
'post',
@ -67,9 +69,11 @@ def make_and_receive_invite(admin: Admin) -> (Request, Request):
make_inv_req.fire()
if make_inv_req.response is None:
return (make_inv_req, user_inv_req)
elif make_inv_req.response.status_code >= 400:
print('Params used ', make_inv_req)
return (make_inv_req, None)
# Setup to fire the second request, .fire() is safe to blindly use at this point
print('Response text from /join ', make_inv_req.response.text)
new_invite_body = make_inv_req.response.json()['invite']
code = new_invite_body['id']
user_inv_req = req(admin , 'get' , '/join', {'code': code}, 200, verbose=True)
@ -89,7 +93,7 @@ if __name__ == '__main__':
# First a quick sanity check for login
# add this after we fire the generic tests
login_req, jwt = login()
login_req, jwt = login(admin)
if jwt is None:
print('Unable to /login - stopping now to avoid pointless failure')
req.show_response()
@ -104,6 +108,19 @@ if __name__ == '__main__':
# Invite test
# Container for most/all the generic requests we want to fire off
now = time()
tmp_neighbor = {
'name': str(now),
'wsurl': 'wsurl',
'url': str(now),
'description': 'asdf',
'tags': ['red','blue']
}
# This neighbor is used to "update" the tmp one above in /neighbor tests
updated_neighbor = tmp_neighbor.copy()
updated_neighbor['name'] = 'new'
print('Updated neighbor as json ', to_json(updated_neighbor))
requests.extend([
req(fake_user, 'get', '/channels/list', {}, 401),
req(admin, 'post', '/channels/list', {'kind': TEXT_CHAN}, 404),
@ -117,8 +134,18 @@ if __name__ == '__main__':
req(admin, 'get', '/message/recent', {'channel_id': 123, 'limit': 20}, 404),
req(admin, 'get', '/members/me', {}, 200),
req(admin, 'get', '/members/get_online', {}, 200),
req(admin, 'post', '/members/me/nickname', {'nick': f'randy-{time()}'}, 200),
req(admin , 'get', '/invite/join', {'code': 123}, 404)
req(admin, 'post', '/members/me/nickname', {'nick': f'randy-{now}'}, 200),
req(admin , 'get', '/invite/join', {'code': 123}, 404),
req(admin , "get", "/meta", {}, 200),
req(admin, 'get', '/neighbor/list', {}, 200),
req(admin,'post', '/neighbor/add', {}, 200, body=to_json(tmp_neighbor)),
req(admin, 'get', '/neighbor/list', {}, 200, verbose=True),
req(admin, 'put', '/neighbor/update', {'url':str(now)}, 200, body=to_json(updated_neighbor)),
req(admin, 'put', '/neighbor/update', {'url':'fake'}, 404, body=to_json(updated_neighbor)),
req(admin, 'get', '/neighbor/list', {}, 200),
req(admin, 'delete', '/neighbor/delete', {'url':'fake'}, 200),
req(admin, 'delete', '/neighbor/delete', {'url': str(now)}, 200),
req(admin, 'get', '/neighbor/list', {}, 200, verbose=True),
])
# add this after fire the generic tests
@ -142,6 +169,7 @@ if __name__ == '__main__':
r.show_response()
if r.passing:
pass_count += 1
req_count = len(requests)
print(f'Passing {pass_count}/{req_count}')

View File

@ -4,6 +4,7 @@ import requests
NC = '\033[0m'
RED = '\033[1;31m'
GREEN = '\033[1;32m'
YELLOW = '\033[1;33m'
class Request:
@ -21,6 +22,9 @@ class Request:
self.verbose = verbose
def __str__(self):
return str(self.qs)
@property
def passing(self):
if self.response is None:
@ -36,6 +40,8 @@ class Request:
self.response = requests.post(self.url, headers=self.headers, params=self.qs, data=self.body)
elif self.method == 'delete':
self.response = requests.delete(self.url, headers=self.headers, params=self.qs)
elif self.method == 'put':
self.response = requests.put(self.url, headers=self.headers, params=self.qs, data=self.body)
return self.response
except Exception as e:
@ -45,34 +51,36 @@ class Request:
def show_response(self):
if self.response is None:
print('Response := None')
return
real_code = self.response.status_code
real_code = None
if self.response is not None:
real_code = self.response.status_code
if self.hope != real_code:
abstract = RED + 'Fail ' + NC + 'Expected ' + str(self.hope) + ' Got ' + str(real_code)
if self.response is None:
print('\tNo Response to show for')
print(abstract)
print('\t', self.method, ' ', self.url)
if len(self.headers) != 0:
print('\tRequest-Headers ', self.headers)
print(YELLOW + '\tRequest-Headers ' + NC, self.headers)
if len(self.qs) != 0:
print('\tQuery-Dictionary ', self.qs)
print(YELLOW + '\tQuery-Dictionary' + NC, self.qs)
if self.body is not None:
print('\tRequest-Body ', str(self.body))
print(YELLOW + '\tRequest-Body' + NC, str(self.body))
if self.verbose:
print(f'\tResponse-Status {self.response.status_code}')
print(f'\tResponse-Headers {self.response.headers}')
print(f'\tResponse-Text {self.response.text}')
print(f'\t{YELLOW}Response-Status{NC} {self.response.status_code}')
print(f'\t{YELLOW}Response-Headers{NC} {self.response.headers}')
print(f'\t{YELLOW}Response-Text{NC} {self.response.text}')
else:
print(f'{GREEN}Pass{NC} {self.method} {self.url}')
if self.verbose:
print(f'\tResponse-Status {self.response.status_code}')
print(f'\tResponse-Headers {self.response.headers}')
print(f'\tResponse-Text {self.response.text}')
print(f'\t{YELLOW}Response-Status{NC} {self.response.status_code}')
print(f'\t{YELLOW}Response-Headers{NC} {self.response.headers}')
print(f'\t{YELLOW}Response-Text{NC} {self.response.text}')

18
json-api/config.json Normal file
View File

@ -0,0 +1,18 @@
{
"#": "The fields below are required and can not be empty",
"#": "All other fields are ignored completely",
"database_url": "mysql://freechat_dev:password@localhost:3306/freechat",
"hmac_path": "hmac.secret",
"wss_hmac_path": "wss-hmac.secret",
"name": "Freechat Server",
"description": "Chatting server",
"url": "http://localhost:4536",
"wsurl": "ws://localhost:5648",
"tags": [
"Freechat",
"Libre software",
"Chatting"
]
}

View File

@ -10,5 +10,6 @@ edition = "2018"
mysql_async = "0.27"
serde = { version = "1.0.117", features = [ "derive" ] }
serde_json = "1.0"
tokio = { version = "1", features = ["fs", "io-util"] }
rand = "0.8.3"

19
json-api/db/src/jwt.rs Normal file
View File

@ -0,0 +1,19 @@
use mysql_async::{Pool, params, Result, prelude::Queryable};
pub async fn listed(p: &Pool, id: u64, given_rng_value: &str) -> Result<bool> {
let mut conn = p.get_conn().await?;
let q = "SELECT rng FROM jwt WHERE id = :id";
let row: Option<String> = conn.exec_first(q, params!{"id" => id}).await?;
if let Some(value) = row {
Ok(value == given_rng_value)
} else{
Ok(false)
}
}
pub async fn insert(p: &Pool, id: u64, given_rng_value: &str) -> Result<()> {
let mut conn = p.get_conn().await?;
let q = "INSERT INTO jwt (id, rng) VALUES (:id, :rng)";
conn.exec_drop(q, params!{"id" => id, "rng" => given_rng_value}).await?;
Ok(())
}

View File

@ -1,10 +1,12 @@
use serde::Serialize;
use serde::{Serialize, Deserialize};
pub mod member;
pub mod common;
pub mod invites;
pub mod channels;
pub mod messages;
pub mod neighbors;
pub mod jwt;
use std::vec::Vec;
@ -90,3 +92,11 @@ pub struct PublicMember {
pub permissions: UBigInt,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Neighbor {
pub name: String,
pub description: Option<String>,
pub tags: Vec<String>,
pub url: String,
pub wsurl: Option<String>,
}

View File

@ -0,0 +1,82 @@
use crate::Neighbor;
use mysql_async::Result as SqlResult;
use mysql_async::{params, Pool, prelude::Queryable};
use serde_json;
pub async fn get_all(p: &Pool) -> SqlResult<Vec<Neighbor>> {
let mut conn = p.get_conn().await?;
let q = "SELECT name, description, tags, url, wsurl FROM neighbors";
type Types = (String, Option<String>, String, String, Option<String>);
let set: Vec<Neighbor> = conn.exec_map(q, (), |(name, description, tags, url, wsurl): Types| {
let json: Vec<String> = serde_json::from_str(tags.as_str()).unwrap_or(vec![]);
Neighbor {
name, description, tags: json, url, wsurl
}
}).await?;
Ok(set)
}
pub async fn add_neighbor(p: &Pool, new: Neighbor) -> SqlResult<()> {
// Note we assume that the tags field has been verified as proper valid json prior to
// writing it to the db
let mut conn = p.get_conn().await?;
let q = "INSERT INTO neighbors(url, wsurl, name, description, tags)
VALUES(:url, :wsurl, :name, :desc, :tags)";
let tags = serde_json::to_string(&new.tags).unwrap_or("[]".to_string());
let sqlparams = params!{
"url"=> new.url,
"wsurl"=>new.wsurl,
"name"=>new.name,
"desc"=>new.description,
"tags"=>tags
};
conn.exec_drop(q, sqlparams).await?;
Ok(())
}
pub async fn get(p: &Pool, url: &str) -> SqlResult<Option<Neighbor>> {
let mut conn = p.get_conn().await?;
let q = "SELECT wsurl, name, description, tags FROM neighbors
WHERE url = :url";
type Fields = (Option<String>, String, Option<String>, String);
let row: Option<Fields> = conn.exec_first(q, params!{"url" => url}).await?;
if let Some(fields) = row {
let tags: Vec<String> = serde_json::from_str(fields.3.as_str()).unwrap_or(vec![]);
let n = Neighbor {
name: fields.1,
wsurl: fields.0,
description: fields.2,
tags,
url: url.into(),
};
return Ok(Some(n));
}
Ok(None)
}
pub async fn update(p: &Pool, url: &str, new: Neighbor) -> SqlResult<()> {
let mut conn = p.get_conn().await?;
let q = "UPDATE neighbors
SET name = :nm, description = :desc, tags = :tags, wsurl = :ws, url = :newurl
WHERE url = :url";
let sqlparams = params!{
"url" => url,
"nm" => new.name,
"desc" => new.description,
"tags" => serde_json::to_string(&new.tags).unwrap(),
"ws" => new.wsurl,
"newurl" => new.url
};
conn.exec_drop(q, sqlparams).await?;
Ok(())
}
pub async fn remove(p: &Pool, url: &str) -> SqlResult<()> {
let mut conn = p.get_conn().await?;
let q = "DELETE FROM neighbors WHERE url = :url";
conn.exec_drop(q, params!{"url" => url}).await?;
Ok(())
}

View File

@ -0,0 +1 @@
DROP TABLE `neighbors`;

View File

@ -0,0 +1,10 @@
-- everything else is nullable due to
-- A) Flexibility
-- B) They're part of optional features(such as the wsurl which not all servers may have)
CREATE TABLE IF NOT EXISTS `neighbors`(
`url` VARCHAR(512) UNIQUE NOT NULL,
`wsurl` VARCHAR(512),
`name` VARCHAR(512) NOT NULL,
`description` VARCHAR(1024),
`tags` VARCHAR(1024)
);

View File

@ -0,0 +1 @@
DROP TABLE `jwt`;

View File

@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS `jwt` (
`id` BIGINT UNSIGNED NOT NULL,
`rng` VARCHAR(48) NOT NULL,
PRIMARY KEY(`id`)
);

View File

@ -1,12 +1,12 @@
use serde::{Serialize, Deserialize};
use bcrypt::{self, BcryptResult};
use mysql_async::Pool;
use hyper::StatusCode;
use std::collections::HashMap;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use crate::routes;
use crate::qs_param;
use db::{Response, Member};
@ -26,15 +26,16 @@ lazy_static! {
}
#[derive(Debug, Serialize, Deserialize)]
struct Claim {
sub: db::UBigInt, // user id
exp: db::BigInt, // expiry date
nbf: i64,
cookie: String, // unique cookie value
pub struct Claim {
sub: u64,// user id
exp: i64, // expiry date
nbf: i64, // "valid-start" time for the claim
prm: u64, // user permissions
rng: String, // Cheesy way of helping reduce the amount
}
impl Claim {
pub fn new(id: db::UBigInt) -> Claim {
pub fn new(id: db::UBigInt, prm: u64) -> Claim {
// JWT's expire every 48 hours
let now = SystemTime::now();
@ -52,20 +53,20 @@ impl Claim {
sub: id,
exp,
nbf,
cookie: generate_cookie()
prm,
rng: generate_cookie()
}
}
}
// used when we create a new users for the first time
#[derive(Debug)]
pub enum AuthReason {
Good, //passed regular check
Good(Claim), //passed regular check
OpenAuth, // route does not require auth
NoKey, // key missing
BadKey, // key is bad
LoginValid, // used only to access the login route which is also our refresh
LoginValid(Member), // used only to access the login route which is also our refresh
ServerIssue(String) // for well 500's
}
@ -81,7 +82,7 @@ fn valid_secret(given_pass: &str, hash: &str) -> bool {
}
}
fn valid_perms(member: Member, path: &str) -> bool {
fn valid_perms(member: &Member, path: &str) -> bool {
use crate::perms;
// if there are perms on the current path make sure the user has them
if let Some(p) = perms::get_perm_mask(path) {
@ -111,7 +112,7 @@ pub fn generate_secret() -> String {
}
pub fn generate_cookie() -> String {
return rng_secret(32)
return rng_secret(16)
}
pub fn encrypt_secret(raw: &str) -> BcryptResult<String> {
@ -120,7 +121,13 @@ pub fn encrypt_secret(raw: &str) -> BcryptResult<String> {
}
async fn valid_jwt(token: &str) -> AuthReason {
async fn valid_jwt(pool: &Pool, path: &str, token: &str) -> AuthReason {
/*
* This function does a database check because of the requirement of always
* enforcing permissions
* TODO: Not all routes actually require a permissions check so we should
* try to only do that DB call when absolutely required
*/
use jsonwebtoken::{
decode, DecodingKey,
Validation, Algorithm
@ -136,7 +143,18 @@ async fn valid_jwt(token: &str) -> AuthReason {
let active = now < decoded.claims.exp;
if active {
AuthReason::Good
if routes::requires_perms(path) {
match db::jwt::listed(pool, decoded.claims.sub, decoded.claims.rng.as_str()).await {
Ok(listed) => if listed {
AuthReason::Good(decoded.claims)
} else {
AuthReason::BadKey
}
_ => AuthReason::BadKey
}
} else {
AuthReason::Good(decoded.claims)
}
} else {
AuthReason::BadKey
}
@ -181,8 +199,7 @@ pub async fn wall_entry<'path, 'pool, 'params>(
}
if let Some(jwt) = jwt {
// get the headers here
return valid_jwt(jwt).await;
return valid_jwt(pool, path, jwt).await;
}
if let Some((id, secret)) = login_params_from_qs(params) {
// Last chance we might be hitting the /login route so we have to do the heavy auth flow
@ -194,8 +211,8 @@ pub async fn wall_entry<'path, 'pool, 'params>(
match Member::get(pool, id).await {
Ok(response) => match response {
Response::Row(user) => {
if valid_secret(secret, &user.secret) && valid_perms(user, path){
AuthReason::LoginValid
if valid_secret(secret, &user.secret) && valid_perms(&user, path){
AuthReason::LoginValid(user)
}
else {
AuthReason::BadKey
@ -215,7 +232,7 @@ pub async fn wall_entry<'path, 'pool, 'params>(
}
}
pub async fn login_get_jwt(response: &mut hyper::Response<hyper::Body>, params: HashMap<String, String>) {
pub async fn login_get_jwt(p: &Pool, response: &mut hyper::Response<hyper::Body>, user: Member) {
// Login data has already been validated at this point
// Required data such as 'id' and 'secret' are there and validated
use jsonwebtoken::{
@ -225,10 +242,9 @@ pub async fn login_get_jwt(response: &mut hyper::Response<hyper::Body>, params:
use hyper::header::HeaderValue;
use crate::http;
let id = qs_param!(params, "id", u64).unwrap();
let id = user.id;
let claim = Claim::new(id);
let claim = Claim::new(id, user.permissions);
let header = Header::new(Algorithm::HS512);
let encoded = encode(
&header,
@ -238,7 +254,13 @@ pub async fn login_get_jwt(response: &mut hyper::Response<hyper::Body>, params:
response.headers_mut().insert("Content-Type",
HeaderValue::from_static("application/json"));
http::set_json_body(response, serde_json::json!({"jwt": encoded}));
match db::jwt::insert(p, id, &claim.rng).await {
Ok(_) => http::set_json_body(response, serde_json::json!({"jwt": encoded})),
Err(e) => {
eprintln!("/login 500 {}", e);
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
}
}
}
@ -259,7 +281,7 @@ mod auth_tests {
#[test]
fn verify_jwt() {
let claim = super::Claim::new(123); // example claim that we send out
let claim = super::Claim::new(123, 456); // example claim that we send out
let header = Header::new(Algorithm::HS512); // header that basically all clients get
let encoded = encode(
&header,

View File

@ -1,6 +1,5 @@
extern crate db;
extern crate clap;
extern crate dotenv;
extern crate getrandom;
extern crate bcrypt;
extern crate base64;
@ -23,8 +22,8 @@ use hyper::{
HeaderMap
};
use mysql_async::Pool;
use serde::Deserialize;
use dotenv::dotenv;
use clap::{Arg, App};
use auth::AuthReason;
@ -64,6 +63,7 @@ async fn route_dispatcher(
const GET: &Method = &Method::GET;
const POST: &Method = &Method::POST;
const DELETE: &Method = &Method::DELETE;
const PUT: &Method = &Method::PUT;
println!("[HTTP] {}: {}", meth, path);
match (meth, path) {
/* INVITES */
@ -88,6 +88,11 @@ async fn route_dispatcher(
(POST, routes::SET_NEW_ADMIN) => admin::new_admin(pool, resp, params).await,
/* META ROUTE */
(GET, routes::META) => meta::server_meta(resp).await,
/* Federated Routes */
(GET, routes::GET_NEIGHBORS) => meta::server_neighbors(pool, resp).await,
(POST, routes::ADD_NEIGHBOR) => meta::add_neighbor(pool, resp, body).await,
(PUT, routes::UPDATE_NEIGHBOR) => meta::update_neighbor(pool, resp, params, body).await,
(DELETE, routes::REMOVE_NEIGHBOR) => meta::remove_neighbor(pool, resp, params).await,
_ => {
println!("[HTTP]\tNOT FOUND: {}: {}", meth, path);
*resp.status_mut() = StatusCode::NOT_FOUND
@ -110,15 +115,18 @@ async fn main_responder(request: Request<Body>) -> Result<Response<Body>, hyper:
None
};
if let Some(params) = params_opt {
match auth::wall_entry(path, &DB_POOL, &params).await {
OpenAuth | Good => {
// route dispatch has its own more comprehensive logging
route_dispatcher(&DB_POOL, &mut response, &method, path, body, params, headers).await;
if let Some(qs) = params_opt {
match auth::wall_entry(path, &DB_POOL, &qs).await {
OpenAuth => {
route_dispatcher(&DB_POOL, &mut response, &method, path, body, qs, headers).await;
},
LoginValid => {
// Only with typical routes do we have to inject the permissions
Good(_claim/* TODO: start moving route handlers to use claims.id for faster access */) => {
route_dispatcher(&DB_POOL, &mut response, &method, path, body, qs, headers).await;
},
LoginValid(member) => {
println!("[HTTP] POST /login");
auth::login_get_jwt(&mut response, params).await;
auth::login_get_jwt(&DB_POOL, &mut response, member).await;
},
NoKey | BadKey => {
*response.status_mut() = StatusCode::UNAUTHORIZED;
@ -193,14 +201,56 @@ async fn attempt_owner_creation(name: &str) {
let _ = p.disconnect().await;
}
fn init_config() -> Result<(), Box<dyn std::error::Error>> {
#[derive(Deserialize, Debug)]
struct RequiredFields {
pub database_url: String,
pub hmac_path: String,
pub wss_hmac_path: String,
pub name: String,
pub description: Option<String>,
pub url: String,
pub wsurl: String,
pub tags: Option<Vec<String>>
}
use std::fs::File;
use std::io::BufReader;
let file = File::open("config.json")?;
let reader = BufReader::new(file);
let fields: RequiredFields = serde_json::from_reader(reader)?;
// Now we can setup each environment variable for this process from config.json
// Note that we only have to do this once since all of these are read from
// lazy statics so the cost is very minimal
set_var("DATABASE_URL", fields.database_url);
set_var("HMAC_PATH", fields.hmac_path);
set_var("WSS_HMAC_PATH", fields.wss_hmac_path);
set_var("SERVER_NAME", fields.name);
set_var("SERVER_DESCRIPTION", fields.description.unwrap_or("".into()));
set_var("PUBLIC_URL", fields.url);
set_var("PUBLIC_WS_URL", fields.wsurl);
// Mega cheesy way of forcing config initialization
if meta::get_config().tags.len() == 0 {
eprintln!("[API] [WARN] No tags have been set", );
}
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), u16>{
let mut main_ret: u16 = 0;
let d_result = dotenv();
let mut main_ret: u16 = 0; let d_result = init_config();
// check for a database_url before the override we get from the cmd line
if let Err(_d) = d_result {
if let Err(d) = d_result {
eprintln!("Config error: {}", d);
if let Err(_e) = env::var("DATABASE_URL") {
main_ret |= CONFIG_ERR;
}
@ -243,9 +293,6 @@ async fn main() -> Result<(), u16>{
.get_matches();
if let Some(db_url) = args.value_of("db-url") {
set_var("DATABASE_URL", db_url);
}
// safe because we have a default value set in code
let port = args.value_of("port").unwrap().to_string();
@ -255,7 +302,7 @@ async fn main() -> Result<(), u16>{
attempt_owner_creation(owner_name).await;
}
// This check overrides the value set in the .env since this
// Here we override some of the config.json variables
if let Some(hmac) = args.value_of("hmac") {
std::env::set_var("HMAC_PATH", hmac);
}

View File

@ -1,12 +1,12 @@
use mysql_async::Pool;
use hyper::{Response, Body, HeaderMap, StatusCode};
use hyper::body::to_bytes;
use hyper::body::Bytes;
use serde_json::json;
use std::collections::HashMap;
use crate::http::set_json_body;
use crate::perms;
use crate::qs_param;
use db::Message;
@ -62,7 +62,6 @@ pub async fn send_message(pool: &Pool, response: &mut Response<Body>, body: Body
* TODO: more features here because send_message is a large handler
*/
use db::Response::*;
use db::Member;
// NOTE: auth module guarantees that id will be present so the unwrap is safe
let uid = qs_param!(params, "id", u64).unwrap();
@ -76,25 +75,10 @@ pub async fn send_message(pool: &Pool, response: &mut Response<Body>, body: Body
None => None
};
let permissions = match Member::get(pool, uid).await {
Ok(dbresponse) => match dbresponse {
Row(user) => user.permissions,
_ => 0
},
Err(e) => {
eprintln!("[DB-SQL] {}",e );
0
}
};
if perms::has_perm(permissions, perms::SEND_MESSAGES) == false {
*response.status_mut() = StatusCode::BAD_REQUEST;
return;
}
let channel_id = qs_param!(params, "channel_id", u64);
// Black magic
let body_bytes: &[u8] = &to_bytes(body).await.unwrap(); // yolo
let body_bytes: &[u8] = &to_bytes(body).await.unwrap_or(Bytes::new());
let content = String::from_utf8_lossy(body_bytes);
// 400 on empty bodies or missing channel id's
@ -164,3 +148,4 @@ pub async fn recent_messages(pool: &Pool, response: &mut Response<Body>, params:
} else {
}
}

View File

@ -1,28 +1,138 @@
// Basic handler for getting meta data about the server
use std::env::var;
use std::collections::HashMap;
use crate::http::set_json_body;
use db::Neighbor;
use hyper::{Response, Body};
use serde_json::to_string;
use serde::Serialize;
use mysql_async::Pool;
use hyper::{Response, Body, StatusCode};
use hyper::body::to_bytes;
use hyper::body::Bytes;
use serde_json::{json, to_string, Result as JsonResult};
use serde::{Serialize, Deserialize};
use lazy_static::lazy_static;
#[derive( Serialize)]
#[derive(Debug, Deserialize, Serialize)]
pub struct Config {
pub name: String,
pub description: String,
pub url: String,
pub wsurl: String,
pub tags: Vec<String>
}
lazy_static! {
// NOTE: this object must be access by proxy through get_config()
#[derive(Deserialize, Serialize)]
static ref BASIC_CONFIG: Config = {
use std::fs::File;
use std::io::BufReader;
match File::open("config.json") {
Ok(file) => {
let reader = BufReader::new(file);
let rr: JsonResult<Config> = serde_json::from_reader(reader);
match rr {
Ok(meta) => meta,
Err(e) => panic!("[HTTP] [FATAL-INIT] {}", e)
}
},
Err(e) => panic!("{}", e)
}
};
}
pub fn get_config() -> Config {
// We have to do this (for now) because lazy_static silently hides the actual fields
// we care about
Config {
name: var("SERVER_NAME").unwrap_or("No name".into()),
description: var("SERVER_DESCRIPTION").unwrap_or("No description".into()),
url: var("PUBLIC_URL").unwrap(),
wsurl: var("PUBLIC_WS_URL").unwrap()
name: BASIC_CONFIG.name.clone(),
description: BASIC_CONFIG.description.clone(),
url: BASIC_CONFIG.url.clone(),
wsurl: BASIC_CONFIG.wsurl.clone(),
tags: BASIC_CONFIG.tags.clone()
}
}
pub async fn server_meta(response: &mut Response<Body>) {
// NOTE: This route _is_ open but we should do something to rate limit the
// number of requests we service as it could be ripe for abuse
*response.body_mut() = Body::from(to_string(&get_config()).unwrap());
}
pub async fn server_neighbors(p: &Pool, response: &mut Response<Body>) {
// This method refers to what servers have been added as **related** by the admins
// It returns a list of servers meta objects which converted to JSON
match db::neighbors::get_all(p).await {
Ok(neighbors) => set_json_body(response, json!({"neighbors": neighbors})),
Err(e) => {
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
eprintln!("500 /neighbors/list {}", e);
}
}
}
pub async fn add_neighbor(p: &Pool, response: &mut Response<Body>, body: Body) {
let body: Bytes = to_bytes(body).await.unwrap_or(Bytes::new());
let json: JsonResult<Neighbor> = serde_json::from_slice(&body);
if let Ok(neighbor) = json {
// Before we try adding the new neighbor, make sure there isn't already
// an entry with the same url
if let Ok(row) = db::neighbors::get(p, &neighbor.url).await {
match row.is_some() {
true => *response.status_mut() = StatusCode::CONFLICT,
false => if let Err(e) = db::neighbors::add_neighbor(p, neighbor).await {
eprintln!("[HTTP] [DB-LIB] {}", e);
}
};
} else {
eprintln!();
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
}
} else {
*response.status_mut() = StatusCode::BAD_REQUEST;
}
}
pub async fn update_neighbor(p: &Pool, response: &mut Response<Body>, params: HashMap<String, String>, body: Body) {
// First collect the target url from the map and try to parse the body
let target = params.get("url");
let body: Bytes = to_bytes(body).await.unwrap_or(Bytes::new());
let s: String = String::from_utf8_lossy(&body).to_string();
let json: JsonResult<Neighbor> = serde_json::from_str(&s);
// Verify query string parameter **and** body before attempting to reach database
match (target, json) {
(Some(target_url), Ok(neighbor)) => {
match db::neighbors::get(p, target_url).await {
Ok(row) => if row.is_some() {
if let Err(e) = db::neighbors::update(p, target_url, neighbor).await {
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
eprintln!("[HTTP] [DB-LIB] /neighbor/update [DB-LIB] {}", e);
}
// Nothing to do on success 200 is already set by hyper
} else{
*response.status_mut() = StatusCode::NOT_FOUND;
},
Err(e) => {
eprintln!("[HTTP] [DB-LIB] /neighbor/update {}", e);
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
}
}
},
_ => *response.status_mut() = StatusCode::BAD_REQUEST
}
}
pub async fn remove_neighbor(p: &Pool, response: &mut Response<Body>, params: HashMap<String, String>) {
match params.get("url") {
Some(url) => {
// As usual 500 on server errors otherwise there's nothing to do
if let Err(e) = db::neighbors::remove(p, url).await {
eprintln!("[HTTP] [DB-LIB] /neighbor/remove {}", e);
*response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
}
},
None => *response.status_mut() = StatusCode::BAD_REQUEST
}
}

View File

@ -13,6 +13,7 @@ pub const _ADMIN: u64 = 1 << 62; // can make other admins but can't really touch
// ADMIN PERMS
pub const CREATE_CHANNEL:u64 = 64;
pub const DELETE_CHANNEL:u64 = 128;
pub const ADD_NEIGHBOR:u64 = 256;
// BELOW ARE COLLECTIVE PERMISSION SETS
pub const OWNER: u64 = std::u64::MAX;
@ -21,16 +22,22 @@ pub const ADMIN_PERMS: u64 = !(std::u64::MAX & OWNER); // filter the only perm a
pub fn get_perm_mask(path: &str) -> Option<u64> {
use crate::routes::{
self,
INVITE_CREATE,
CHANNELS_LIST, CHANNELS_CREATE, CHANNELS_DELETE,
MESSAGE_SEND,
};
match path {
INVITE_CREATE => Some(CREATE_TMP_INVITES),
CHANNELS_LIST => None,
CHANNELS_CREATE => Some(CREATE_CHANNEL),
CHANNELS_DELETE => Some(DELETE_CHANNEL),
MESSAGE_SEND => Some(SEND_MESSAGES),
routes::ADD_NEIGHBOR => Some(ADD_NEIGHBOR),
_ => Some(0)
}
}
@ -39,4 +46,4 @@ pub fn get_perm_mask(path: &str) -> Option<u64> {
pub fn has_perm(mask: u64, target: u64) -> bool {
// Check if the given mask contains the target permissions mask
return (mask & target) == target;
}
}

View File

@ -24,7 +24,20 @@ pub const SELF_UPDATE_NICKNAME: Rstr= "/members/me/nickname";
pub const SET_PERMS_BY_ADMIN: Rstr = "/admin/setpermisions"; // @requires perms::ADMIN
pub const SET_NEW_ADMIN: Rstr = "/owner/newadmin"; // @requiers: owner perms
// Server -> Server Routes
pub const GET_NEIGHBORS: Rstr = "/neighbor/list"; // @requires: none @note must be a member for this list
pub const ADD_NEIGHBOR: Rstr = "/neighbor/add"; // @requires: perm::add_neighbor
pub const UPDATE_NEIGHBOR: Rstr = "/neighbor/update"; // @requires perms::add_neighbor @url(unique)
pub const REMOVE_NEIGHBOR: Rstr = "/neighbor/delete"; // @requires perms::add_neighbor @url(unique)
pub fn is_open(path: &str) -> bool {
return path.starts_with("/join") || path.starts_with("/meta");
}
pub fn requires_perms(path: &str) -> bool {
return match path {
/* These routes _don't_ require any permissions */
AUTH_LOGIN | META | CHANNELS_LIST | GET_MYSELF | GET_NEIGHBORS => false,
_ => true
}
}

View File

@ -3,10 +3,10 @@
#[cfg(test)]
pub fn get_pool() -> mysql_async::Pool {
use dotenv::dotenv;
// NOTE: this assumes that DATABASE_URL has been set in the environment
// prior to running
use mysql_async::Pool;
dotenv().ok();
return Pool::new(&std::env::var("DATABASE_URL").unwrap())
}