mirror of
https://codeberg.org/Cyborus/forgejo-cli.git
synced 2024-11-24 02:41:47 +01:00
implement fj auth login
This commit is contained in:
parent
c47a24ad22
commit
3ff6a86e8e
4 changed files with 396 additions and 16 deletions
237
Cargo.lock
generated
237
Cargo.lock
generated
|
@ -65,6 +65,23 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-trait"
|
||||||
|
version = "0.1.80"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic-waker"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "auth-git2"
|
name = "auth-git2"
|
||||||
version = "0.5.4"
|
version = "0.5.4"
|
||||||
|
@ -121,6 +138,15 @@ version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.16.0"
|
version = "3.16.0"
|
||||||
|
@ -212,6 +238,25 @@ version = "0.8.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
|
@ -222,6 +267,16 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "directories"
|
name = "directories"
|
||||||
version = "5.0.1"
|
version = "5.0.1"
|
||||||
|
@ -298,15 +353,20 @@ name = "fj"
|
||||||
version = "0.0.4"
|
version = "0.0.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"auth-git2",
|
"auth-git2",
|
||||||
|
"base64ct",
|
||||||
"clap",
|
"clap",
|
||||||
"directories",
|
"directories",
|
||||||
"eyre",
|
"eyre",
|
||||||
"forgejo-api",
|
"forgejo-api",
|
||||||
"futures",
|
"futures",
|
||||||
"git2",
|
"git2",
|
||||||
|
"hyper 1.3.1",
|
||||||
|
"hyper-util",
|
||||||
"open",
|
"open",
|
||||||
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"sha256",
|
||||||
"soft_assert",
|
"soft_assert",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -452,6 +512,16 @@ dependencies = [
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
|
@ -495,7 +565,26 @@ dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http 0.2.12",
|
||||||
|
"indexmap",
|
||||||
|
"slab",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "h2"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-waker",
|
||||||
|
"bytes",
|
||||||
|
"fnv",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"http 1.1.0",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -521,6 +610,12 @@ version = "0.3.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
|
@ -532,6 +627,17 @@ dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"fnv",
|
||||||
|
"itoa",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http-body"
|
name = "http-body"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
|
@ -539,10 +645,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"http",
|
"http 0.2.12",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-body"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"http 1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
@ -565,9 +681,9 @@ dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2",
|
"h2 0.3.26",
|
||||||
"http",
|
"http 0.2.12",
|
||||||
"http-body",
|
"http-body 0.4.6",
|
||||||
"httparse",
|
"httparse",
|
||||||
"httpdate",
|
"httpdate",
|
||||||
"itoa",
|
"itoa",
|
||||||
|
@ -579,6 +695,26 @@ dependencies = [
|
||||||
"want",
|
"want",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-util",
|
||||||
|
"h2 0.4.5",
|
||||||
|
"http 1.1.0",
|
||||||
|
"http-body 1.0.0",
|
||||||
|
"httparse",
|
||||||
|
"httpdate",
|
||||||
|
"itoa",
|
||||||
|
"pin-project-lite",
|
||||||
|
"smallvec",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-tls"
|
name = "hyper-tls"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
@ -586,12 +722,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"hyper",
|
"hyper 0.14.28",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-native-tls",
|
"tokio-native-tls",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-util"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"http 1.1.0",
|
||||||
|
"http-body 1.0.0",
|
||||||
|
"hyper 1.3.1",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
@ -962,6 +1113,12 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.81"
|
version = "1.0.81"
|
||||||
|
@ -980,6 +1137,36 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -1011,10 +1198,10 @@ dependencies = [
|
||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2",
|
"h2 0.3.26",
|
||||||
"http",
|
"http 0.2.12",
|
||||||
"http-body",
|
"http-body 0.4.6",
|
||||||
"hyper",
|
"hyper 0.14.28",
|
||||||
"hyper-tls",
|
"hyper-tls",
|
||||||
"ipnet",
|
"ipnet",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
@ -1156,6 +1343,30 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.10.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha256"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"bytes",
|
||||||
|
"hex",
|
||||||
|
"sha2",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
|
@ -1413,6 +1624,12 @@ version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicase"
|
name = "unicase"
|
||||||
version = "2.7.0"
|
version = "2.7.0"
|
||||||
|
|
|
@ -7,15 +7,20 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
auth-git2 = "0.5.3"
|
auth-git2 = "0.5.3"
|
||||||
|
base64ct = { version = "1.6.0", features = ["std"] }
|
||||||
clap = { version = "4.3.11", features = ["derive"] }
|
clap = { version = "4.3.11", features = ["derive"] }
|
||||||
directories = "5.0.1"
|
directories = "5.0.1"
|
||||||
eyre = "0.6.8"
|
eyre = "0.6.8"
|
||||||
forgejo-api = "0.3.0"
|
forgejo-api = "0.3.0"
|
||||||
futures = "0.3.28"
|
futures = "0.3.28"
|
||||||
git2 = "0.17.2"
|
git2 = "0.17.2"
|
||||||
|
hyper = "1.3.1"
|
||||||
|
hyper-util = { version = "0.1.5", features = ["tokio", "server", "http1", "http2"] }
|
||||||
open = "5.0.0"
|
open = "5.0.0"
|
||||||
|
rand = "0.8.5"
|
||||||
serde = { version = "1.0.170", features = ["derive"] }
|
serde = { version = "1.0.170", features = ["derive"] }
|
||||||
serde_json = "1.0.100"
|
serde_json = "1.0.100"
|
||||||
|
sha256 = "1.5.0"
|
||||||
soft_assert = "0.1.1"
|
soft_assert = "0.1.1"
|
||||||
time = { version = "0.3.30", features = ["formatting", "macros"] }
|
time = { version = "0.3.30", features = ["formatting", "macros"] }
|
||||||
tokio = { version = "1.29.1", features = ["full"] }
|
tokio = { version = "1.29.1", features = ["full"] }
|
||||||
|
|
168
src/auth.rs
168
src/auth.rs
|
@ -1,4 +1,5 @@
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
|
use eyre::OptionExt;
|
||||||
|
|
||||||
#[derive(Subcommand, Clone, Debug)]
|
#[derive(Subcommand, Clone, Debug)]
|
||||||
pub enum AuthCommand {
|
pub enum AuthCommand {
|
||||||
|
@ -18,12 +19,28 @@ pub enum AuthCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthCommand {
|
impl AuthCommand {
|
||||||
pub async fn run(self, keys: &mut crate::KeyInfo) -> eyre::Result<()> {
|
pub async fn run(self, keys: &mut crate::KeyInfo, host_name: Option<&str>) -> eyre::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
AuthCommand::Login => {
|
AuthCommand::Login => {
|
||||||
todo!();
|
let repo_info = crate::repo::RepoInfo::get_current(host_name, None, None)?;
|
||||||
// let user = readline("username: ").await?;
|
let host_url = repo_info.host_url();
|
||||||
// let pass = readline("password: ").await?;
|
let client_info = get_client_info_for(host_url);
|
||||||
|
if let Some((client_id, _)) = client_info {
|
||||||
|
oauth_login(keys, host_url, client_id).await?;
|
||||||
|
} else {
|
||||||
|
let host_domain = host_url.host_str().ok_or_eyre("invalid host")?;
|
||||||
|
let host_path = host_url.path();
|
||||||
|
let mut applications_url = host_url.clone();
|
||||||
|
applications_url
|
||||||
|
.path_segments_mut()
|
||||||
|
.map_err(|_| eyre::eyre!("invalid url"))?
|
||||||
|
.extend(["user", "settings", "applications"]);
|
||||||
|
|
||||||
|
println!("{host_domain}{host_path} doesn't support easy login");
|
||||||
|
println!();
|
||||||
|
println!("Please visit {applications_url}");
|
||||||
|
println!("to create a token, and use it to log in with `fj auth add-token`");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
AuthCommand::Logout { host } => {
|
AuthCommand::Logout { host } => {
|
||||||
let info_opt = keys.hosts.remove(&host);
|
let info_opt = keys.hosts.remove(&host);
|
||||||
|
@ -64,10 +81,151 @@ impl AuthCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_client_info_for(url: &url::Url) -> Option<(&'static str, &'static str)> {
|
pub fn get_client_info_for(url: &url::Url) -> Option<(&'static str, &'static str)> {
|
||||||
let host = url.host_str()?;
|
|
||||||
let client_info = match (url.host_str()?, url.path()) {
|
let client_info = match (url.host_str()?, url.path()) {
|
||||||
("codeberg.org", "/") => option_env!("CLIENT_INFO_CODEBERG"),
|
("codeberg.org", "/") => option_env!("CLIENT_INFO_CODEBERG"),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
client_info.and_then(|info| info.split_once(":"))
|
client_info.and_then(|info| info.split_once(":"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn oauth_login(
|
||||||
|
keys: &mut crate::KeyInfo,
|
||||||
|
host: &url::Url,
|
||||||
|
client_id: &'static str,
|
||||||
|
) -> eyre::Result<()> {
|
||||||
|
use base64ct::Encoding;
|
||||||
|
use rand::{distributions::Alphanumeric, prelude::*};
|
||||||
|
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
|
let state = (0..32)
|
||||||
|
.map(|_| rng.sample(Alphanumeric) as char)
|
||||||
|
.collect::<String>();
|
||||||
|
let code_verifier = (0..43)
|
||||||
|
.map(|_| rng.sample(Alphanumeric) as char)
|
||||||
|
.collect::<String>();
|
||||||
|
let code_challenge =
|
||||||
|
base64ct::Base64Url::encode_string(sha256::digest(&code_verifier).as_bytes());
|
||||||
|
|
||||||
|
let mut auth_url = host.clone();
|
||||||
|
auth_url
|
||||||
|
.path_segments_mut()
|
||||||
|
.map_err(|_| eyre::eyre!("invalid url"))?
|
||||||
|
.extend(["login", "oauth", "authorize"]);
|
||||||
|
auth_url.query_pairs_mut().extend_pairs([
|
||||||
|
("client_id", client_id),
|
||||||
|
("redirect_uri", "http://127.0.0.1:26218/"),
|
||||||
|
("response_type", "code"),
|
||||||
|
("code_challenge_method", "S256"),
|
||||||
|
("code_challenge", &code_challenge),
|
||||||
|
("state", &state),
|
||||||
|
]);
|
||||||
|
open::that(auth_url.as_str()).unwrap();
|
||||||
|
|
||||||
|
let (handle, mut rx) = auth_server();
|
||||||
|
let res = rx.recv().await.unwrap();
|
||||||
|
handle.abort();
|
||||||
|
let code = match res {
|
||||||
|
Ok(Some((code, returned_state))) => {
|
||||||
|
if returned_state == state {
|
||||||
|
code
|
||||||
|
} else {
|
||||||
|
eyre::bail!("returned with invalid state");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
println!("Login canceled");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eyre::bail!("Failed to authenticate: {e}");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let api = forgejo_api::Forgejo::new(forgejo_api::Auth::None, host.clone())?;
|
||||||
|
let request = forgejo_api::structs::OAuthTokenRequest::Public {
|
||||||
|
client_id,
|
||||||
|
code_verifier: &code_verifier,
|
||||||
|
code: &code,
|
||||||
|
redirect_uri: url::Url::parse("http://127.0.0.1:26218/").unwrap(),
|
||||||
|
};
|
||||||
|
let response = api.oauth_get_access_token(request).await?;
|
||||||
|
|
||||||
|
let api = forgejo_api::Forgejo::new(
|
||||||
|
forgejo_api::Auth::OAuth2(&response.access_token),
|
||||||
|
host.clone(),
|
||||||
|
)?;
|
||||||
|
let current_user = api.user_get_current().await?;
|
||||||
|
let name = current_user
|
||||||
|
.login
|
||||||
|
.ok_or_eyre("user does not have login name")?;
|
||||||
|
|
||||||
|
// A minute less, in case any weirdness happens at the exact moment it
|
||||||
|
// expires. Better to refresh slightly too soon than slightly too late.
|
||||||
|
let expires_in = std::time::Duration::from_secs(response.expires_in.saturating_sub(60) as u64);
|
||||||
|
let expires_at = time::OffsetDateTime::now_utc() + expires_in;
|
||||||
|
let login_info = crate::keys::LoginInfo::OAuth {
|
||||||
|
name,
|
||||||
|
token: response.access_token,
|
||||||
|
refresh_token: response.refresh_token,
|
||||||
|
expires_at,
|
||||||
|
};
|
||||||
|
keys.hosts
|
||||||
|
.insert(host.host_str().unwrap().to_string(), login_info);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
use tokio::{sync::mpsc::Receiver, task::JoinHandle};
|
||||||
|
|
||||||
|
fn auth_server() -> (
|
||||||
|
JoinHandle<eyre::Result<()>>,
|
||||||
|
Receiver<Result<Option<(String, String)>, String>>,
|
||||||
|
) {
|
||||||
|
let addr: std::net::SocketAddr = ([127, 0, 0, 1], 26218).into();
|
||||||
|
let (tx, rx) = tokio::sync::mpsc::channel(1);
|
||||||
|
let tx = std::sync::Arc::new(tx);
|
||||||
|
let handle = tokio::spawn(async move {
|
||||||
|
let listener = tokio::net::TcpListener::bind(addr).await?;
|
||||||
|
let server =
|
||||||
|
hyper_util::server::conn::auto::Builder::new(hyper_util::rt::TokioExecutor::new());
|
||||||
|
let svc = hyper::service::service_fn(|req: hyper::Request<hyper::body::Incoming>| {
|
||||||
|
let tx = std::sync::Arc::clone(&tx);
|
||||||
|
async move {
|
||||||
|
let mut code = None;
|
||||||
|
let mut state = None;
|
||||||
|
let mut error_description = None;
|
||||||
|
if let Some(query) = req.uri().query() {
|
||||||
|
for item in query.split("&") {
|
||||||
|
let (key, value) = item.split_once("=").unwrap_or((item, ""));
|
||||||
|
match key {
|
||||||
|
"code" => code = Some(value),
|
||||||
|
"state" => state = Some(value),
|
||||||
|
"error_description" => error_description = Some(value),
|
||||||
|
_ => eprintln!("unknown key {key} {value}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (response, message) = match (code, state, error_description) {
|
||||||
|
(_, _, Some(error)) => (Err(error.to_owned()), "Failed to authenticate"),
|
||||||
|
(Some(code), Some(state), None) => (
|
||||||
|
Ok(Some((code.to_owned(), state.to_owned()))),
|
||||||
|
"Authenticated! Close this tab and head back to your terminal",
|
||||||
|
),
|
||||||
|
_ => (Ok(None), "Canceled"),
|
||||||
|
};
|
||||||
|
tx.send(response).await.unwrap();
|
||||||
|
Ok::<_, hyper::Error>(hyper::Response::new(message.to_owned()))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
loop {
|
||||||
|
let (connection, _addr) = listener.accept().await.unwrap();
|
||||||
|
server
|
||||||
|
.serve_connection(hyper_util::rt::TokioIo::new(connection), svc)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
(handle, rx)
|
||||||
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ async fn main() -> eyre::Result<()> {
|
||||||
println!("currently signed in to {name}@{host}{}", url.path());
|
println!("currently signed in to {name}@{host}{}", url.path());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::Auth(subcommand) => subcommand.run(&mut keys).await?,
|
Command::Auth(subcommand) => subcommand.run(&mut keys, host_name).await?,
|
||||||
Command::Release(subcommand) => subcommand.run(&mut keys, host_name).await?,
|
Command::Release(subcommand) => subcommand.run(&mut keys, host_name).await?,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue