Add web-based authentication
Signed-off-by: Sebastian Martinez <me@sebastinez.dev>
This commit is contained in:
parent
deeb160c2d
commit
f07a875ce1
|
|
@ -29,15 +29,6 @@ dependencies = [
|
|||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "amplify"
|
||||
version = "4.0.0-beta.7"
|
||||
|
|
@ -94,12 +85,6 @@ version = "1.0.68"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
||||
|
||||
[[package]]
|
||||
name = "ascii"
|
||||
version = "1.1.0"
|
||||
|
|
@ -128,18 +113,6 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "auto_impl"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a8c1df849285fbacd587de7818cc7d13be6cd2cbcd47a04fb1801b0e2706e33"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
|
|
@ -259,18 +232,6 @@ version = "1.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium",
|
||||
"tap",
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
|
|
@ -314,12 +275,6 @@ version = "3.12.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
||||
|
||||
[[package]]
|
||||
name = "byte-slice-cast"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
|
|
@ -331,9 +286,6 @@ name = "bytes"
|
|||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
|
|
@ -500,12 +452,6 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.4.9"
|
||||
|
|
@ -685,17 +631,6 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dialoguer"
|
||||
version = "0.10.3"
|
||||
|
|
@ -837,80 +772,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethabi"
|
||||
version = "18.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898"
|
||||
dependencies = [
|
||||
"ethereum-types",
|
||||
"hex",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha3",
|
||||
"thiserror",
|
||||
"uint",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethbloom"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
"fixed-hash",
|
||||
"impl-codec",
|
||||
"impl-rlp",
|
||||
"impl-serde",
|
||||
"scale-info",
|
||||
"tiny-keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethereum-types"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee"
|
||||
dependencies = [
|
||||
"ethbloom",
|
||||
"fixed-hash",
|
||||
"impl-codec",
|
||||
"impl-rlp",
|
||||
"impl-serde",
|
||||
"primitive-types",
|
||||
"scale-info",
|
||||
"uint",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethers-core"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade3e9c97727343984e1ceada4fdab11142d2ee3472d2c67027d56b1251d4f15"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"elliptic-curve",
|
||||
"ethabi",
|
||||
"generic-array",
|
||||
"hex",
|
||||
"k256",
|
||||
"open-fastrlp",
|
||||
"rand 0.8.5",
|
||||
"rlp",
|
||||
"rlp-derive",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum",
|
||||
"thiserror",
|
||||
"tiny-keccak",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.8.0"
|
||||
|
|
@ -942,18 +803,6 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixed-hash"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"rand 0.8.5",
|
||||
"rustc-hex",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.25"
|
||||
|
|
@ -979,12 +828,6 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.25"
|
||||
|
|
@ -1180,12 +1023,6 @@ dependencies = [
|
|||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
|
|
@ -1204,12 +1041,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
|
|
@ -1317,44 +1148,6 @@ dependencies = [
|
|||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-codec"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-rlp"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808"
|
||||
dependencies = [
|
||||
"rlp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-serde"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "impl-trait-for-tuples"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.2"
|
||||
|
|
@ -1417,16 +1210,6 @@ dependencies = [
|
|||
"popol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iri-string"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d0586ad318a04c73acdbad33f67969519b5452c80770c4c72059a686da48a7e"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.2"
|
||||
|
|
@ -1474,19 +1257,6 @@ dependencies = [
|
|||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "k256"
|
||||
version = "0.11.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"sha2 0.10.6",
|
||||
"sha3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "keccak"
|
||||
version = "0.1.3"
|
||||
|
|
@ -1684,15 +1454,6 @@ dependencies = [
|
|||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom8"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nonempty"
|
||||
version = "0.5.0"
|
||||
|
|
@ -1811,31 +1572,6 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "open-fastrlp"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"auto_impl",
|
||||
"bytes",
|
||||
"ethereum-types",
|
||||
"open-fastrlp-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "open-fastrlp-derive"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "output_vt100"
|
||||
version = "0.1.3"
|
||||
|
|
@ -1873,32 +1609,6 @@ dependencies = [
|
|||
"sha2 0.10.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-scale-codec"
|
||||
version = "3.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7ab01d0f889e957861bc65888d5ccbe82c158d0270136ba46820d43837cdf72"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitvec",
|
||||
"byte-slice-cast",
|
||||
"impl-trait-for-tuples",
|
||||
"parity-scale-codec-derive",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-scale-codec-derive"
|
||||
version = "3.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pbkdf2"
|
||||
version = "0.11.0"
|
||||
|
|
@ -2021,30 +1731,6 @@ dependencies = [
|
|||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "primitive-types"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66"
|
||||
dependencies = [
|
||||
"fixed-hash",
|
||||
"impl-codec",
|
||||
"impl-rlp",
|
||||
"impl-serde",
|
||||
"scale-info",
|
||||
"uint",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
|
|
@ -2162,6 +1848,7 @@ dependencies = [
|
|||
"tempfile",
|
||||
"thiserror",
|
||||
"timeago",
|
||||
"ureq",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
|
|
@ -2252,7 +1939,6 @@ dependencies = [
|
|||
"axum",
|
||||
"axum-server",
|
||||
"chrono",
|
||||
"ethers-core",
|
||||
"fastrand",
|
||||
"flate2",
|
||||
"hyper",
|
||||
|
|
@ -2263,7 +1949,6 @@ dependencies = [
|
|||
"radicle-surf",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"siwe",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"time 0.3.17",
|
||||
|
|
@ -2360,12 +2045,6 @@ dependencies = [
|
|||
"radicle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
|
|
@ -2452,8 +2131,6 @@ version = "1.7.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
|
|
@ -2492,27 +2169,6 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rlp"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"rustc-hex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rlp-derive"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.7.2"
|
||||
|
|
@ -2534,12 +2190,6 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hex"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.7"
|
||||
|
|
@ -2575,30 +2225,6 @@ dependencies = [
|
|||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scale-info"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "001cf62ece89779fd16105b5f515ad0e5cedcd5440d3dd806bb067978e7c3608"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"derive_more",
|
||||
"parity-scale-codec",
|
||||
"scale-info-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scale-info-derive"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.3"
|
||||
|
|
@ -2784,22 +2410,6 @@ version = "0.3.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||
|
||||
[[package]]
|
||||
name = "siwe"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec4cc2eafb2354c1aeaeac5f53b7726ca4ea5ad16314a0b5ebacb6676f599c2b"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"http",
|
||||
"iri-string",
|
||||
"k256",
|
||||
"rand 0.8.5",
|
||||
"sha3",
|
||||
"thiserror",
|
||||
"time 0.3.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.7"
|
||||
|
|
@ -2932,34 +2542,6 @@ dependencies = [
|
|||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.24.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.1"
|
||||
|
|
@ -2995,12 +2577,6 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.38"
|
||||
|
|
@ -3108,15 +2684,6 @@ version = "0.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ec32dde57efb15c035ac074118d7f32820451395f28cb0524a01d4e94983b26"
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
|
|
@ -3175,23 +2742,6 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "729bfd096e40da9c001f778f5cdecbd2957929a24e10e5883d9392220a751581"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"nom8",
|
||||
"toml_datetime",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.4.13"
|
||||
|
|
@ -3327,18 +2877,6 @@ version = "1.16.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
|
||||
[[package]]
|
||||
name = "uint"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"crunchy",
|
||||
"hex",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.10"
|
||||
|
|
@ -3382,6 +2920,20 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "2.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"log",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.3.1"
|
||||
|
|
@ -3581,15 +3133,6 @@ version = "0.42.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||
dependencies = [
|
||||
"tap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "0.2.3"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ serde_json = { version = "1" }
|
|||
serde_yaml = { version = "0.8" }
|
||||
thiserror = { version = "1" }
|
||||
timeago = { version = "0.3", default-features = false }
|
||||
ureq = { version = "2.6.1", default-features = false, features = ["json"] }
|
||||
zeroize = { version = "1.1" }
|
||||
|
||||
[dependencies.radicle]
|
||||
|
|
|
|||
|
|
@ -42,3 +42,5 @@ pub mod rad_track;
|
|||
pub mod rad_unassign;
|
||||
#[path = "commands/untrack.rs"]
|
||||
pub mod rad_untrack;
|
||||
#[path = "commands/web.rs"]
|
||||
pub mod rad_web;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
use std::ffi::OsString;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use radicle::crypto::{PublicKey, Signature, Signer};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::{Args, Error, Help};
|
||||
|
||||
pub const HELP: Help = Help {
|
||||
name: "web",
|
||||
description: "Connect web with node",
|
||||
version: env!("CARGO_PKG_VERSION"),
|
||||
usage: r#"
|
||||
Usage
|
||||
|
||||
rad web [<options>...]
|
||||
|
||||
Options
|
||||
|
||||
--host, -h httpd host to bind to
|
||||
--web, -w interface host to bind to
|
||||
--verbose, -v Verbose output
|
||||
--help Print help
|
||||
"#,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SessionInfo {
|
||||
pub session_id: String,
|
||||
pub public_key: PublicKey,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Options {
|
||||
pub host: String,
|
||||
pub web: String,
|
||||
pub verbose: bool,
|
||||
}
|
||||
|
||||
impl Args for Options {
|
||||
fn from_args(args: Vec<OsString>) -> anyhow::Result<(Self, Vec<OsString>)> {
|
||||
use lexopt::prelude::*;
|
||||
|
||||
let mut parser = lexopt::Parser::from_args(args);
|
||||
let mut host = None;
|
||||
let mut web = None;
|
||||
let mut verbose = false;
|
||||
|
||||
while let Some(arg) = parser.next()? {
|
||||
match arg {
|
||||
Long("verbose") | Short('v') => verbose = true,
|
||||
Long("host") | Short('h') => {
|
||||
host = Some(parser.value()?.to_string_lossy().to_string())
|
||||
}
|
||||
Long("web") | Short('w') => {
|
||||
web = Some(parser.value()?.to_string_lossy().to_string())
|
||||
}
|
||||
Long("help") => {
|
||||
return Err(Error::Help.into());
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow!(arg.unexpected()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((
|
||||
Options {
|
||||
verbose,
|
||||
host: host.unwrap_or(String::from("0.0.0.0:8080")),
|
||||
web: web.unwrap_or(String::from("0.0.0.0:3000")),
|
||||
},
|
||||
vec![],
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sign(signer: Box<dyn Signer>, session: &SessionInfo) -> Result<Signature, anyhow::Error> {
|
||||
signer
|
||||
.try_sign(format!("{}:{}", session.session_id, session.public_key).as_bytes())
|
||||
.map_err(anyhow::Error::from)
|
||||
}
|
||||
|
||||
pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||
let session: SessionInfo = ureq::post(&format!("http://{}/api/v1/sessions", options.host))
|
||||
.call()?
|
||||
.into_json()?;
|
||||
let profile = ctx.profile()?;
|
||||
let signer = profile.signer()?;
|
||||
let signature = sign(signer, &session)?;
|
||||
term::info!(
|
||||
"http://{}/session/{}?pk={}&sig={}",
|
||||
options.web,
|
||||
session.session_id,
|
||||
session.public_key,
|
||||
signature,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -278,6 +278,12 @@ fn run_other(exe: &str, args: &[OsString]) -> Result<(), Option<anyhow::Error>>
|
|||
args.to_vec(),
|
||||
);
|
||||
}
|
||||
"web" => term::run_command_args::<rad_web::Options, _>(
|
||||
rad_web::HELP,
|
||||
"Web",
|
||||
rad_web::run,
|
||||
args.to_vec(),
|
||||
),
|
||||
_ => {
|
||||
let exe = format!("{}-{}", NAME, exe);
|
||||
let status = process::Command::new(exe.clone()).args(args).status();
|
||||
|
|
|
|||
|
|
@ -19,14 +19,12 @@ anyhow = { version = "1" }
|
|||
axum = { version = "0.6.2", default-features = false, features = ["headers", "json", "query", "tokio"] }
|
||||
axum-server = { version = "0.4.4", default-features = false }
|
||||
chrono = { version = "0.4.22" }
|
||||
ethers-core = { version = "1.0" }
|
||||
fastrand = { version = "1.7.0" }
|
||||
flate2 = { version = "1" }
|
||||
hyper = { version = "0.14.17", default-features = false }
|
||||
lexopt = { version = "0.2.1" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = { version = "1", features = ["preserve_order"] }
|
||||
siwe = { version = "0.5" }
|
||||
thiserror = { version = "1" }
|
||||
time = { version = "0.3.17" }
|
||||
tokio = { version = "1.21", default-features = false, features = ["macros", "rt-multi-thread"] }
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ pub fn router(ctx: Context) -> Router {
|
|||
CorsLayer::new()
|
||||
.max_age(Duration::from_secs(86400))
|
||||
.allow_origin(cors::Any)
|
||||
.allow_methods([Method::GET, Method::POST, Method::PUT])
|
||||
.allow_methods([Method::GET, Method::POST, Method::PATCH, Method::PUT])
|
||||
.allow_headers([CONTENT_TYPE, AUTHORIZATION]),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,8 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
|
||||
use ethers_core::types::{Signature, H160};
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
use radicle::crypto::PublicKey;
|
||||
use serde::{Serialize, Serializer};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use crate::api::error::Error;
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, PartialEq, PartialOrd)]
|
||||
pub struct DateTime(pub OffsetDateTime);
|
||||
|
||||
impl Serialize for DateTime {
|
||||
|
|
@ -16,26 +11,11 @@ impl Serialize for DateTime {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct AuthRequest {
|
||||
pub message: String,
|
||||
#[serde(deserialize_with = "deserialize_signature")]
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
fn deserialize_signature<'de, D>(deserializer: D) -> Result<Signature, D::Error>
|
||||
where
|
||||
D: serde::de::Deserializer<'de>,
|
||||
{
|
||||
let buf = String::deserialize(deserializer)?;
|
||||
Signature::from_str(&buf).map_err(serde::de::Error::custom)
|
||||
}
|
||||
|
||||
pub enum AuthState {
|
||||
Authorized(Session),
|
||||
Unauthorized {
|
||||
nonce: String,
|
||||
expiration_time: DateTime,
|
||||
public_key: PublicKey,
|
||||
expires_at: DateTime,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -43,48 +23,7 @@ pub enum AuthState {
|
|||
#[derive(Clone, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Session {
|
||||
pub domain: String,
|
||||
pub address: H160,
|
||||
pub statement: Option<String>,
|
||||
pub uri: String,
|
||||
pub version: u64,
|
||||
pub chain_id: u64,
|
||||
pub nonce: String,
|
||||
pub public_key: PublicKey,
|
||||
pub issued_at: DateTime,
|
||||
pub expiration_time: Option<DateTime>,
|
||||
pub resources: Vec<String>,
|
||||
}
|
||||
|
||||
impl TryFrom<siwe::Message> for Session {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(message: siwe::Message) -> Result<Session, Error> {
|
||||
Ok(Session {
|
||||
domain: message.domain.host().to_string(),
|
||||
address: H160(message.address),
|
||||
statement: None,
|
||||
uri: message.uri.to_string(),
|
||||
version: message.version as u64,
|
||||
chain_id: message.chain_id,
|
||||
nonce: message.nonce,
|
||||
issued_at: DateTime(message.issued_at.as_ref().to_owned()),
|
||||
expiration_time: message
|
||||
.expiration_time
|
||||
.map(|x| DateTime(x.as_ref().to_owned())),
|
||||
resources: message.resources.iter().map(|r| r.to_string()).collect(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[test]
|
||||
fn test_auth_request_de() {
|
||||
let json = serde_json::json!({
|
||||
"message": "Hello World!",
|
||||
"signature": "20096c6ed2bcccb88c9cafbbbbda7a5a3cff6d0ca318c07faa58464083ca40a92f899fbeb26a4c763a7004b13fd0f1ba6c321d4e3a023e30f63c40d4154b99a41c"
|
||||
});
|
||||
|
||||
let _req: super::AuthRequest = serde_json::from_value(json).unwrap();
|
||||
}
|
||||
pub expires_at: DateTime,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,13 +18,13 @@ pub enum Error {
|
|||
#[error(transparent)]
|
||||
Env(#[from] std::env::VarError),
|
||||
|
||||
/// An error occurred while verifying the siwe message.
|
||||
/// Profile error.
|
||||
#[error(transparent)]
|
||||
SiweVerification(#[from] siwe::VerificationError),
|
||||
Profile(#[from] radicle::profile::Error),
|
||||
|
||||
/// An error occurred while parsing the siwe message.
|
||||
/// Crypto error.
|
||||
#[error(transparent)]
|
||||
SiweParse(#[from] siwe::ParseError),
|
||||
Crypto(#[from] radicle::crypto::Error),
|
||||
|
||||
/// Storage error.
|
||||
#[error(transparent)]
|
||||
|
|
@ -60,8 +60,7 @@ impl IntoResponse for Error {
|
|||
let (status, msg) = match &self {
|
||||
Error::NotFound => (StatusCode::NOT_FOUND, None),
|
||||
Error::Auth(msg) => (StatusCode::BAD_REQUEST, Some(msg.to_string())),
|
||||
Error::SiweParse(msg) => (StatusCode::BAD_REQUEST, Some(msg.to_string())),
|
||||
Error::SiweVerification(msg) => (StatusCode::BAD_REQUEST, Some(msg.to_string())),
|
||||
Error::Crypto(msg) => (StatusCode::BAD_REQUEST, Some(msg.to_string())),
|
||||
Error::Git2(e) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Some(e.message().to_owned()),
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
|||
use std::{env, fs};
|
||||
|
||||
use axum::body::Body;
|
||||
use axum::http::Request;
|
||||
use axum::http::{Method, Request};
|
||||
use axum::Router;
|
||||
use serde_json::Value;
|
||||
use tower::ServiceExt;
|
||||
|
|
@ -116,20 +116,42 @@ pub fn seed(dir: &Path) -> Context {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn request(app: &Router, path: impl ToString) -> Response {
|
||||
pub async fn get(app: &Router, path: impl ToString) -> Response {
|
||||
Response(
|
||||
app.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.uri(path.to_string())
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
.oneshot(request(path, Method::GET, None))
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn post(app: &Router, path: impl ToString, body: Option<Body>) -> Response {
|
||||
Response(
|
||||
app.clone()
|
||||
.oneshot(request(path, Method::POST, body))
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn put(app: &Router, path: impl ToString, body: Option<Body>) -> Response {
|
||||
Response(
|
||||
app.clone()
|
||||
.oneshot(request(path, Method::PUT, body))
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn request(path: impl ToString, method: Method, body: Option<Body>) -> Request<Body> {
|
||||
Request::builder()
|
||||
.method(method)
|
||||
.uri(path.to_string())
|
||||
.header("Content-Type", "application/json")
|
||||
.body(body.unwrap_or_else(Body::empty))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub struct Response(axum::response::Response);
|
||||
|
||||
impl Response {
|
||||
|
|
|
|||
|
|
@ -69,13 +69,13 @@ mod routes {
|
|||
use axum::http::StatusCode;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::api::test::{self, request, HEAD};
|
||||
use crate::api::test::{self, get, HEAD};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delegates_projects() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(
|
||||
let response = get(
|
||||
&app,
|
||||
"/delegates/did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/projects",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -465,13 +465,13 @@ mod routes {
|
|||
use axum::http::StatusCode;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::api::test::{self, request, HEAD, HEAD_1};
|
||||
use crate::api::test::{self, get, HEAD, HEAD_1};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_projects_root() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(&app, "/projects").await;
|
||||
let response = get(&app, "/projects").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
|
|
@ -494,7 +494,7 @@ mod routes {
|
|||
async fn test_projects() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(&app, "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp").await;
|
||||
let response = get(&app, "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
|
|
@ -515,7 +515,7 @@ mod routes {
|
|||
async fn test_projects_commits_root() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(&app, "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/commits").await;
|
||||
let response = get(&app, "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/commits").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::FOUND);
|
||||
assert_eq!(
|
||||
|
|
@ -637,7 +637,7 @@ mod routes {
|
|||
async fn test_projects_commits() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(
|
||||
let response = get(
|
||||
&app,
|
||||
format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/commits/{HEAD}"),
|
||||
)
|
||||
|
|
@ -703,7 +703,7 @@ mod routes {
|
|||
async fn test_projects_tree() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(
|
||||
let response = get(
|
||||
&app,
|
||||
format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/tree/{HEAD}/"),
|
||||
)
|
||||
|
|
@ -750,7 +750,7 @@ mod routes {
|
|||
)
|
||||
);
|
||||
|
||||
let response = request(
|
||||
let response = get(
|
||||
&app,
|
||||
format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/tree/{HEAD}/dir1"),
|
||||
)
|
||||
|
|
@ -796,7 +796,7 @@ mod routes {
|
|||
async fn test_projects_remotes_root() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(&app, "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/remotes").await;
|
||||
let response = get(&app, "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/remotes").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
|
|
@ -817,7 +817,7 @@ mod routes {
|
|||
async fn test_projects_remotes() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(&app, "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/remotes/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi").await;
|
||||
let response = get(&app, "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/remotes/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
|
|
@ -836,7 +836,7 @@ mod routes {
|
|||
async fn test_projects_blob() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(
|
||||
let response = get(
|
||||
&app,
|
||||
format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/blob/{HEAD}/README"),
|
||||
)
|
||||
|
|
@ -872,7 +872,7 @@ mod routes {
|
|||
async fn test_projects_readme() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(
|
||||
let response = get(
|
||||
&app,
|
||||
format!("/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/readme/{HEAD}"),
|
||||
)
|
||||
|
|
@ -908,7 +908,7 @@ mod routes {
|
|||
async fn test_projects_issues_root() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(&app, "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/issues").await;
|
||||
let response = get(&app, "/projects/rad:z4FucBZHZMCsxTyQE1dfE2YR59Qbp/issues").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
|
|
|
|||
|
|
@ -1,131 +1,135 @@
|
|||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::env;
|
||||
use std::iter::repeat_with;
|
||||
use std::str::FromStr;
|
||||
|
||||
use axum::extract::State;
|
||||
use axum::response::IntoResponse;
|
||||
use axum::routing::{get, post};
|
||||
use axum::routing::{post, put};
|
||||
use axum::{Json, Router};
|
||||
use ethers_core::utils::hex;
|
||||
use hyper::http::uri::Authority;
|
||||
use radicle::crypto::{PublicKey, Signature};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use siwe::Message;
|
||||
use time::{Duration, OffsetDateTime};
|
||||
|
||||
use crate::api::auth::{AuthRequest, AuthState, DateTime, Session};
|
||||
use crate::api::auth::{AuthState, DateTime, Session};
|
||||
use crate::api::axum_extra::Path;
|
||||
use crate::api::error::Error;
|
||||
use crate::api::Context;
|
||||
|
||||
pub const UNAUTHORIZED_SESSIONS_EXPIRATION: Duration = Duration::seconds(60);
|
||||
pub const AUTHORIZED_SESSIONS_EXPIRATION: Duration = Duration::weeks(1);
|
||||
|
||||
pub fn router(ctx: Context) -> Router {
|
||||
Router::new()
|
||||
.route("/sessions", post(session_create_handler))
|
||||
.route(
|
||||
"/sessions/:id",
|
||||
get(session_get_handler).put(session_signin_handler),
|
||||
)
|
||||
.route("/sessions/:id", put(session_signin_handler))
|
||||
.with_state(ctx)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct AuthChallenge {
|
||||
sig: Signature,
|
||||
pk: PublicKey,
|
||||
}
|
||||
|
||||
/// Create session.
|
||||
/// `POST /sessions`
|
||||
async fn session_create_handler(State(ctx): State<Context>) -> impl IntoResponse {
|
||||
let rng = fastrand::Rng::new();
|
||||
let session_id = repeat_with(|| rng.alphanumeric())
|
||||
.take(32)
|
||||
.collect::<String>();
|
||||
let signer = ctx.profile.signer().map_err(Error::from)?;
|
||||
let expiration_time = OffsetDateTime::now_utc()
|
||||
.checked_add(UNAUTHORIZED_SESSIONS_EXPIRATION)
|
||||
.unwrap();
|
||||
let auth_state = AuthState::Unauthorized {
|
||||
public_key: *signer.public_key(),
|
||||
expires_at: DateTime(expiration_time),
|
||||
};
|
||||
let mut sessions = ctx.sessions.write().await;
|
||||
let (session_id, nonce) = create_session(&mut sessions, DateTime(expiration_time));
|
||||
sessions.insert(session_id.clone(), auth_state);
|
||||
|
||||
Json(json!({ "id": session_id, "nonce": nonce }))
|
||||
}
|
||||
|
||||
/// Get session.
|
||||
/// `GET /sessions/:id`
|
||||
async fn session_get_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path(id): Path<String>,
|
||||
) -> impl IntoResponse {
|
||||
let sessions = ctx.sessions.read().await;
|
||||
let session = sessions.get(&id).ok_or(Error::NotFound)?;
|
||||
|
||||
match session {
|
||||
AuthState::Authorized(session) => {
|
||||
Ok::<_, Error>(Json(json!({ "id": id, "session": session })))
|
||||
}
|
||||
AuthState::Unauthorized {
|
||||
nonce,
|
||||
expiration_time,
|
||||
} => Ok::<_, Error>(Json(
|
||||
json!({ "id": id, "nonce": nonce, "expirationTime": expiration_time }),
|
||||
)),
|
||||
}
|
||||
Ok::<_, Error>(Json(
|
||||
json!({"sessionId": session_id, "publicKey": signer.public_key()}),
|
||||
))
|
||||
}
|
||||
|
||||
/// Update session.
|
||||
/// `PUT /sessions/:id`
|
||||
async fn session_signin_handler(
|
||||
State(ctx): State<Context>,
|
||||
Path(id): Path<String>,
|
||||
Json(request): Json<AuthRequest>,
|
||||
Path(session_id): Path<String>,
|
||||
Json(request): Json<AuthChallenge>,
|
||||
) -> impl IntoResponse {
|
||||
// Get unauthenticated session data, return early if not found
|
||||
let mut sessions = ctx.sessions.write().await;
|
||||
let session = sessions.get(&id).ok_or(Error::NotFound)?;
|
||||
|
||||
if let AuthState::Unauthorized { nonce, .. } = session {
|
||||
let message = Message::from_str(request.message.as_str()).map_err(Error::from)?;
|
||||
|
||||
let host = env::var("RADICLE_DOMAIN").map_err(Error::from)?;
|
||||
|
||||
// Validate nonce
|
||||
if *nonce != message.nonce {
|
||||
return Err(Error::Auth("Invalid nonce"));
|
||||
let session = sessions.get(&session_id).ok_or(Error::NotFound)?;
|
||||
if let AuthState::Unauthorized {
|
||||
public_key,
|
||||
expires_at,
|
||||
} = session
|
||||
{
|
||||
if public_key != &request.pk {
|
||||
return Err(Error::Auth("Invalid public key"));
|
||||
}
|
||||
|
||||
// Verify that domain is the correct one
|
||||
let authority = Authority::from_str(&host).map_err(|_| Error::Auth("Invalid host"))?;
|
||||
if authority != message.domain {
|
||||
return Err(Error::Auth("Invalid domain"));
|
||||
if expires_at <= &DateTime(OffsetDateTime::now_utc()) {
|
||||
return Err(Error::Auth("Session expired"));
|
||||
}
|
||||
|
||||
// Verifies the following:
|
||||
// - AuthRequest sig matches the address passed in the AuthRequest message.
|
||||
// - expirationTime is not in the past.
|
||||
// - notBefore time is in the future.
|
||||
message
|
||||
.verify(&request.signature.to_vec(), &Default::default())
|
||||
.await
|
||||
let payload = format!("{}:{}", session_id, request.pk);
|
||||
request
|
||||
.pk
|
||||
.verify(payload.as_bytes(), &request.sig)
|
||||
.map_err(Error::from)?;
|
||||
let expiration_time = OffsetDateTime::now_utc()
|
||||
.checked_add(AUTHORIZED_SESSIONS_EXPIRATION)
|
||||
.unwrap();
|
||||
let session = Session {
|
||||
public_key: request.pk,
|
||||
issued_at: DateTime(OffsetDateTime::now_utc()),
|
||||
expires_at: DateTime(expiration_time),
|
||||
};
|
||||
sessions.insert(session_id.clone(), AuthState::Authorized(session));
|
||||
|
||||
let session: Session = message.try_into()?;
|
||||
sessions.insert(id.clone(), AuthState::Authorized(session.clone()));
|
||||
|
||||
return Ok::<_, Error>(Json(json!({ "id": id, "session": session })));
|
||||
return Ok::<_, Error>(());
|
||||
}
|
||||
|
||||
Err(Error::Auth("Session already authorized"))
|
||||
}
|
||||
|
||||
fn create_session(
|
||||
map: &mut HashMap<String, AuthState>,
|
||||
expiration_time: DateTime,
|
||||
) -> (String, String) {
|
||||
let nonce = siwe::generate_nonce();
|
||||
#[cfg(test)]
|
||||
mod routes {
|
||||
use axum::body::Body;
|
||||
use axum::http::StatusCode;
|
||||
use radicle_cli::commands::rad_web::{self, SessionInfo};
|
||||
|
||||
// We generate a value from the RNG for the session id
|
||||
let rng = fastrand::Rng::new();
|
||||
let id = hex::encode(repeat_with(|| rng.u8(..)).take(32).collect::<Vec<u8>>());
|
||||
use crate::api::test::{self, post, put};
|
||||
|
||||
let auth_state = AuthState::Unauthorized {
|
||||
nonce: nonce.clone(),
|
||||
expiration_time,
|
||||
};
|
||||
#[tokio::test]
|
||||
async fn test_session() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let ctx = test::seed(tmp.path());
|
||||
let app = super::router(ctx.to_owned());
|
||||
|
||||
map.insert(id.clone(), auth_state);
|
||||
// Create session
|
||||
let response = post(&app, "/sessions", None).await;
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let json = response.json().await;
|
||||
let session_info: SessionInfo = serde_json::from_value(json).unwrap();
|
||||
|
||||
(id, nonce)
|
||||
// Create request body
|
||||
let signer = ctx.profile.signer().unwrap();
|
||||
let signature = rad_web::sign(signer, &session_info).unwrap();
|
||||
let body = serde_json::to_vec(&super::AuthChallenge {
|
||||
sig: signature,
|
||||
pk: session_info.public_key,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let response = put(
|
||||
&app,
|
||||
format!("/sessions/{}", session_info.session_id),
|
||||
Some(Body::from(body)),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ mod routes {
|
|||
use axum::http::StatusCode;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::api::test::{self, request};
|
||||
use crate::api::test::{self, get};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_stats() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let app = super::router(test::seed(tmp.path()));
|
||||
let response = request(&app, "/stats").await;
|
||||
let response = get(&app, "/stats").await;
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_eq!(
|
||||
|
|
|
|||
Loading…
Reference in New Issue