Compare commits
No commits in common. "a1a2aa27547edb45558476a487d3933340676e19" and "50c275a4ee826e5a1b720a3df002178ef7288480" have entirely different histories.
a1a2aa2754
...
50c275a4ee
14 changed files with 48 additions and 694 deletions
268
Cargo.lock
generated
268
Cargo.lock
generated
|
@ -76,54 +76,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.4"
|
||||
|
@ -311,25 +263,7 @@ dependencies = [
|
|||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"toml 0.8.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "binctl"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"clap",
|
||||
"confy",
|
||||
"dirs",
|
||||
"exitcode",
|
||||
"open",
|
||||
"openidconnect",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -422,64 +356,6 @@ dependencies = [
|
|||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "confy"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e37668cb35145dcfaa1931a5f37fde375eeae8068b4c0d2f289da28a270b2d2c"
|
||||
dependencies = [
|
||||
"directories",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"toml 0.5.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.5"
|
||||
|
@ -654,47 +530,6 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "directories"
|
||||
version = "4.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210"
|
||||
dependencies = [
|
||||
"dirs-sys 0.3.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
||||
dependencies = [
|
||||
"dirs-sys 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
|
@ -823,12 +658,6 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exitcode"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193"
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.13.0"
|
||||
|
@ -913,7 +742,6 @@ dependencies = [
|
|||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
|
@ -1217,15 +1045,6 @@ version = "2.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
|
||||
|
||||
[[package]]
|
||||
name = "is-docker"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.9"
|
||||
|
@ -1237,16 +1056,6 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-wsl"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5"
|
||||
dependencies = [
|
||||
"is-docker",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
|
@ -1530,17 +1339,6 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "open"
|
||||
version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfabf1927dce4d6fdf563d63328a0a506101ced3ec780ca2135747336c98cef8"
|
||||
dependencies = [
|
||||
"is-wsl",
|
||||
"libc",
|
||||
"pathdiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openidconnect"
|
||||
version = "3.4.0"
|
||||
|
@ -1573,12 +1371,6 @@ dependencies = [
|
|||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "2.10.1"
|
||||
|
@ -1630,17 +1422,11 @@ checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.4.1",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "1.1.1"
|
||||
|
@ -1835,15 +1621,6 @@ dependencies = [
|
|||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
|
@ -1853,17 +1630,6 @@ dependencies = [
|
|||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"redox_syscall 0.2.16",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.2"
|
||||
|
@ -1943,12 +1709,10 @@ dependencies = [
|
|||
"system-configuration",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-util",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-streams",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
"winreg",
|
||||
|
@ -2536,15 +2300,6 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.2"
|
||||
|
@ -2688,12 +2443,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
|
@ -2781,19 +2530,6 @@ version = "0.2.87"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-streams"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.64"
|
||||
|
|
35
Cargo.toml
35
Cargo.toml
|
@ -1,10 +1,29 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"server",
|
||||
"cli"
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
[package]
|
||||
name = "bin"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.33", features = ["full"] }
|
||||
tokio-util = { version="0.7", features = ["io"]}
|
||||
futures-util = "0.3"
|
||||
axum = {version="0.6", features=["macros", "headers", "multipart"]}
|
||||
serde = "1.0"
|
||||
toml = "0.8"
|
||||
duration-str = "0.7.0"
|
||||
render = { git="https://github.com/render-rs/render.rs" }
|
||||
thiserror = "1.0"
|
||||
rand = "0.8"
|
||||
dotenvy = "0.15"
|
||||
markdown = "0.3"
|
||||
axum_oidc = {git="https://git2.zettoit.eu/pfz4/axum_oidc"}
|
||||
log = "0.4"
|
||||
env_logger = "0.10"
|
||||
|
||||
chacha20 = "0.9"
|
||||
sha3 = "0.10"
|
||||
hex = "0.4"
|
||||
bytes = "1.5"
|
||||
pin-project-lite = "0.2"
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
[package]
|
||||
name = "binctl"
|
||||
edition = "2021"
|
||||
version.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version="4.4", features = ["derive"] }
|
||||
reqwest = { version="0.11", features = ["rustls-tls", "stream"], default-features=false}
|
||||
openidconnect = "3.4"
|
||||
thiserror = "1.0"
|
||||
serde = { version="1.0", features = [ "derive" ] }
|
||||
axum = "0.6"
|
||||
tokio = { version = "1.33", features = ["full"] }
|
||||
open = "5.0"
|
||||
tokio-util = { version="0.7.9", features = ["io"]}
|
||||
dirs = "5.0"
|
||||
confy = "0.5"
|
||||
exitcode = "1.1.2"
|
174
cli/src/auth.rs
174
cli/src/auth.rs
|
@ -1,174 +0,0 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
extract::{Query, State},
|
||||
response::{Html, IntoResponse},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use openidconnect::{
|
||||
core::{CoreAuthenticationFlow, CoreClient, CoreErrorResponseType, CoreProviderMetadata},
|
||||
reqwest::async_http_client,
|
||||
AccessTokenHash, AuthorizationCode, ClaimsVerificationError, ClientId, CsrfToken,
|
||||
DiscoveryError, IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, RedirectUrl,
|
||||
RefreshToken, RequestTokenError, Scope, SigningError, StandardErrorResponse, TokenResponse,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("url parse error: {:?}", 0)]
|
||||
UrlParse(#[from] openidconnect::url::ParseError),
|
||||
|
||||
#[error("discovery error: {:?}", 0)]
|
||||
Discovery(#[from] DiscoveryError<openidconnect::reqwest::Error<reqwest::Error>>),
|
||||
|
||||
#[error("request token error: {:?}", 0)]
|
||||
RequestToken(
|
||||
#[from]
|
||||
RequestTokenError<
|
||||
openidconnect::reqwest::Error<reqwest::Error>,
|
||||
StandardErrorResponse<CoreErrorResponseType>,
|
||||
>,
|
||||
),
|
||||
|
||||
#[error("claims verification error: {:?}", 0)]
|
||||
ClaimsVerification(#[from] ClaimsVerificationError),
|
||||
|
||||
#[error("signing error: {:?}", 0)]
|
||||
Signing(#[from] SigningError),
|
||||
|
||||
#[error("server did not return an id token")]
|
||||
NoIdToken,
|
||||
|
||||
#[error("invalid access token")]
|
||||
InvalidAccessToken,
|
||||
|
||||
#[error("no response received")]
|
||||
NoResponse,
|
||||
|
||||
#[error("csrf mismatch")]
|
||||
CsrfMismatch,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct ResponseData {
|
||||
pub code: String,
|
||||
pub state: String,
|
||||
}
|
||||
|
||||
pub(crate) async fn login(
|
||||
issuer: &str,
|
||||
client_id: &str,
|
||||
scopes: &[String],
|
||||
refresh_token: &mut Option<String>,
|
||||
) -> Result<String, Error> {
|
||||
let provider_metadata = CoreProviderMetadata::discover_async(
|
||||
IssuerUrl::new(issuer.to_string())?,
|
||||
async_http_client,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Create an OpenID Connect client by specifying the client ID, client secret, authorization URL
|
||||
// and token URL.
|
||||
let client = CoreClient::from_provider_metadata(
|
||||
provider_metadata,
|
||||
ClientId::new(client_id.to_string()),
|
||||
None,
|
||||
)
|
||||
// Set the URL the user will be redirected to after the authorization process.
|
||||
.set_redirect_uri(RedirectUrl::new("http://[::1]:8080".to_string())?);
|
||||
|
||||
// Generate a PKCE challenge.
|
||||
let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256();
|
||||
|
||||
if let Some(refresh_token) = refresh_token {
|
||||
if let Ok(token_response) = client
|
||||
.exchange_refresh_token(&RefreshToken::new(refresh_token.to_string()))
|
||||
.request_async(async_http_client)
|
||||
.await
|
||||
{
|
||||
eprintln!("authenticated with oidc provider");
|
||||
return Ok(token_response.access_token().secret().clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the full authorization URL.
|
||||
let mut auth = client.authorize_url(
|
||||
CoreAuthenticationFlow::AuthorizationCode,
|
||||
CsrfToken::new_random,
|
||||
Nonce::new_random,
|
||||
);
|
||||
|
||||
for scope in scopes {
|
||||
auth = auth.add_scope(Scope::new(scope.to_string()));
|
||||
}
|
||||
let (auth_url, csrf_token, nonce) = auth
|
||||
// Set the PKCE code challenge.
|
||||
.set_pkce_challenge(pkce_challenge)
|
||||
.url();
|
||||
open::that(auth_url.to_string()).unwrap();
|
||||
eprintln!("a browser should have been opened with the url {auth_url}. please login with your oidc provider.");
|
||||
|
||||
let (fuse_tx, mut fuse_rx) = mpsc::channel::<ResponseData>(1);
|
||||
let app = Router::new()
|
||||
.route("/", get(handle_post))
|
||||
.with_state(Arc::new(fuse_tx));
|
||||
|
||||
let server = axum::Server::bind(&"[::1]:8080".parse().unwrap()).serve(app.into_make_service());
|
||||
|
||||
let data = tokio::select! {
|
||||
x = fuse_rx.recv() => {
|
||||
x
|
||||
}
|
||||
_ = server => {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let data = data.ok_or(Error::NoResponse)?;
|
||||
|
||||
// match csrf_state
|
||||
|
||||
if *csrf_token.secret() != data.state {
|
||||
return Err(Error::CsrfMismatch);
|
||||
}
|
||||
|
||||
let token_response = client
|
||||
.exchange_code(AuthorizationCode::new(data.code))
|
||||
// Set the PKCE code verifier.
|
||||
.set_pkce_verifier(pkce_verifier)
|
||||
.request_async(async_http_client)
|
||||
.await?;
|
||||
|
||||
// Extract the ID token claims after verifying its authenticity and nonce.
|
||||
let id_token = token_response.id_token().ok_or_else(|| Error::NoIdToken)?;
|
||||
let claims = id_token.claims(&client.id_token_verifier(), &nonce)?;
|
||||
|
||||
// Verify the access token hash to ensure that the access token hasn't been substituted for
|
||||
// another user's.
|
||||
if let Some(expected_access_token_hash) = claims.access_token_hash() {
|
||||
let actual_access_token_hash =
|
||||
AccessTokenHash::from_token(token_response.access_token(), &id_token.signing_alg()?)?;
|
||||
if actual_access_token_hash != *expected_access_token_hash {
|
||||
return Err(Error::InvalidAccessToken);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(new_refresh_token) = token_response.refresh_token() {
|
||||
*refresh_token = Some(new_refresh_token.secret().to_string());
|
||||
}
|
||||
|
||||
eprintln!("authenticated with oidc provider");
|
||||
Ok(token_response.access_token().secret().clone())
|
||||
}
|
||||
|
||||
async fn handle_post(
|
||||
State(fuse_tx): State<Arc<mpsc::Sender<ResponseData>>>,
|
||||
Query(data): Query<ResponseData>,
|
||||
) -> impl IntoResponse {
|
||||
fuse_tx.clone().send(data).await;
|
||||
Html("<html><body>Die Anmeldung war erfolgreich. Du kannst dieses Fenster jetzt schließen.<script>window.close()</script></body></html>")
|
||||
}
|
103
cli/src/main.rs
103
cli/src/main.rs
|
@ -1,103 +0,0 @@
|
|||
use std::process::ExitCode;
|
||||
|
||||
use clap::Parser;
|
||||
use reqwest::{Body, Url};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::io::stdin;
|
||||
use tokio_util::io::ReaderStream;
|
||||
|
||||
use crate::auth::login;
|
||||
|
||||
mod auth;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Config {
|
||||
pub refresh_token: Option<String>,
|
||||
pub binurl: String,
|
||||
pub issuer: String,
|
||||
pub client_id: String,
|
||||
pub scopes: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[arg(short, long)]
|
||||
content_type: Option<String>,
|
||||
|
||||
#[arg(short, long)]
|
||||
ttl: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
refresh_token: None,
|
||||
binurl: "https://bin.zettoit.eu".to_string(),
|
||||
issuer: "https://auth.zettoit.eu/realms/zettoit".to_string(),
|
||||
client_id: "binctl".to_string(),
|
||||
scopes: vec!["zettoit-bin".to_string()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let mut cfg: Config = confy::load("binctl", None).unwrap_or_default();
|
||||
|
||||
let args = Args::parse();
|
||||
let access_token = login(
|
||||
&cfg.issuer,
|
||||
&cfg.client_id,
|
||||
cfg.scopes.as_slice(),
|
||||
&mut cfg.refresh_token,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let mut bin = create_bin(&cfg.binurl, &access_token).await.unwrap();
|
||||
eprintln!("created bin at {}. uploading...", bin);
|
||||
bin.set_query(args.ttl.map(|x| format!("ttl={}", x)).as_deref());
|
||||
|
||||
upload_to_bin(
|
||||
bin.as_ref(),
|
||||
&args
|
||||
.content_type
|
||||
.unwrap_or("application/octet-stream".to_string()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let _ = confy::store("binctl", None, cfg);
|
||||
bin.set_query(None);
|
||||
print!("{bin}");
|
||||
}
|
||||
|
||||
async fn create_bin(binurl: &str, access_token: &str) -> Result<Url, reqwest::Error> {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
Ok(client
|
||||
.get(binurl)
|
||||
.header("Authorization", format!("Bearer {}", access_token))
|
||||
.send()
|
||||
.await?
|
||||
.url()
|
||||
.to_owned())
|
||||
}
|
||||
|
||||
async fn upload_to_bin(url: &str, content_type: &str) -> Result<(), reqwest::Error> {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
if let Err(error) = client
|
||||
.post(url)
|
||||
.header("Content-Type", content_type)
|
||||
.body(Body::wrap_stream(ReaderStream::new(stdin())))
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await
|
||||
{
|
||||
eprintln!("{:?}", error);
|
||||
std::process::exit(exitcode::TEMPFAIL);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
28
flake.nix
28
flake.nix
|
@ -26,22 +26,20 @@
|
|||
nixpkgs.lib.genAttrs [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
] (system: function system nixpkgs.legacyPackages.${system});
|
||||
in rec {
|
||||
packages = forAllSystems(system: syspkgs: let
|
||||
] (system: let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [ (import rust-overlay) ];
|
||||
};
|
||||
rustToolchain = pkgs.rust-bin.stable.latest.default;
|
||||
|
||||
markdownFilter = path: _type: builtins.match ".*md$" path != null;
|
||||
markdownOrCargo = path: type: (markdownFilter path type) || (craneLib.filterCargoSources path type);
|
||||
|
||||
craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain;
|
||||
src = pkgs.lib.cleanSourceWith {
|
||||
src = craneLib.path ./.;
|
||||
filter = path: type:
|
||||
(pkgs.lib.hasSuffix "\.md" path) ||
|
||||
(craneLib.filterCargoSources path type)
|
||||
;
|
||||
filter = markdownOrCargo;
|
||||
};
|
||||
|
||||
nativeBuildInputs = with pkgs; [ rustToolchain pkg-config ];
|
||||
|
@ -54,20 +52,18 @@
|
|||
|
||||
bin = craneLib.buildPackage (commonArgs // {
|
||||
inherit cargoArtifacts;
|
||||
pname = "bin";
|
||||
});
|
||||
binctl = craneLib.buildPackage (commonArgs // {
|
||||
inherit cargoArtifacts;
|
||||
pname = "binctl";
|
||||
in function {
|
||||
inherit bin pkgs;
|
||||
});
|
||||
in {
|
||||
inherit bin binctl;
|
||||
packages = forAllSystems({pkgs, bin}: {
|
||||
inherit bin;
|
||||
default = bin;
|
||||
});
|
||||
devShells = forAllSystems(system: pkgs: pkgs.mkShell {
|
||||
inputsFrom = [packages.${system}.bin packages.${system}.binctl];
|
||||
devShells = forAllSystems({pkgs, bin}: pkgs.mkShell {
|
||||
inputsFrom = bin;
|
||||
});
|
||||
hydraJobs."bin" = forAllSystems(system: pkgs: packages.${system}.bin);
|
||||
hydraJobs."binctl" = forAllSystems(system: pkgs: packages.${system}.binctl);
|
||||
hydraJobs."build" = forAllSystems({pkgs, bin}: bin);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
[package]
|
||||
name = "bin"
|
||||
version.workspace = true
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.33", features = ["full"] }
|
||||
tokio-util = { version="0.7", features = ["io"]}
|
||||
futures-util = "0.3"
|
||||
axum = {version="0.6", features=["macros", "headers", "multipart"]}
|
||||
serde = "1.0"
|
||||
toml = "0.8"
|
||||
duration-str = "0.7.0"
|
||||
render = { git="https://github.com/render-rs/render.rs" }
|
||||
thiserror = "1.0"
|
||||
rand = "0.8"
|
||||
dotenvy = "0.15"
|
||||
markdown = "0.3"
|
||||
axum_oidc = {git="https://git2.zettoit.eu/pfz4/axum_oidc"}
|
||||
log = "0.4"
|
||||
env_logger = "0.10"
|
||||
|
||||
chacha20 = "0.9"
|
||||
sha3 = "0.10"
|
||||
hex = "0.4"
|
||||
bytes = "1.5"
|
||||
pin-project-lite = "0.2"
|
|
@ -38,12 +38,6 @@ pub enum Error {
|
|||
|
||||
#[error("invalid ttl")]
|
||||
InvalidTtl,
|
||||
|
||||
#[error("unauthorized")]
|
||||
Unauthorized,
|
||||
|
||||
#[error("forbidden")]
|
||||
Forbidden,
|
||||
}
|
||||
|
||||
impl IntoResponse for Error {
|
||||
|
@ -60,8 +54,6 @@ impl IntoResponse for Error {
|
|||
(StatusCode::BAD_REQUEST, "invalid multipart data").into_response()
|
||||
}
|
||||
Self::InvalidTtl => (StatusCode::BAD_REQUEST, "invalid ttl specified").into_response(),
|
||||
Self::Unauthorized => (StatusCode::UNAUTHORIZED, "unauthorized\n").into_response(),
|
||||
Self::Forbidden => (StatusCode::FORBIDDEN, "forbidden\n").into_response(),
|
||||
_ => {
|
||||
error!("{:?}", self);
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, "internal server error\n").into_response()
|
|
@ -32,12 +32,12 @@ use chacha20::{
|
|||
};
|
||||
use futures_util::StreamExt;
|
||||
use garbage_collector::GarbageCollector;
|
||||
use log::{debug, warn};
|
||||
use log::debug;
|
||||
use render::{html, raw};
|
||||
use serde::Deserialize;
|
||||
use sha3::{Digest, Sha3_256};
|
||||
use tokio::{
|
||||
fs::{self, File},
|
||||
fs::File,
|
||||
io::{AsyncWriteExt, BufReader, BufWriter},
|
||||
};
|
||||
use util::{IdSalt, KeySalt};
|
||||
|
@ -147,14 +147,7 @@ async fn main() {
|
|||
|
||||
let app = Router::new()
|
||||
.route("/", get(get_index))
|
||||
.route(
|
||||
"/:id",
|
||||
get(get_item)
|
||||
.post(upload_bin)
|
||||
.put(upload_bin)
|
||||
.delete(delete_bin),
|
||||
)
|
||||
.route("/:id/delete", get(delete_bin_interactive).post(delete_bin))
|
||||
.route("/:id", get(get_item).post(post_item).put(post_item))
|
||||
.with_state(state);
|
||||
axum::Server::bind(&"[::]:8080".parse().expect("valid listen address"))
|
||||
.serve(app.into_make_service())
|
||||
|
@ -202,69 +195,12 @@ async fn get_index(
|
|||
)))
|
||||
}
|
||||
|
||||
async fn delete_bin(
|
||||
Path(phrase): Path<String>,
|
||||
State(app_state): State<AppState>,
|
||||
oidc_extractor: Result<OidcExtractor<EmptyAdditionalClaims>, axum_oidc::error::Error>,
|
||||
jwt_claims: Option<Claims<EmptyAdditionalClaims>>,
|
||||
) -> HandlerResult<impl IntoResponse> {
|
||||
let subject = match (oidc_extractor, jwt_claims) {
|
||||
(_, Some(claims)) => claims.sub.to_string(),
|
||||
(Ok(oidc), None) => oidc.claims.subject().to_string(),
|
||||
(Err(_), None) => return Err(Error::Unauthorized),
|
||||
};
|
||||
|
||||
let phrase = Phrase::from_str(&phrase)?;
|
||||
let id = Id::from_phrase(&phrase, &app_state.id_salt);
|
||||
|
||||
let metadata_path = format!("{}/{}.toml", app_state.data, id);
|
||||
let metadata = Metadata::from_file(&metadata_path).await?;
|
||||
|
||||
if metadata.subject != subject {
|
||||
return Err(Error::Forbidden);
|
||||
}
|
||||
|
||||
debug!("deleting bin {}", id);
|
||||
let res_meta = fs::remove_file(&format!("{}/{}.toml", app_state.data, id)).await;
|
||||
let res_data = fs::remove_file(&format!("{}/{}.dat", app_state.data, id)).await;
|
||||
|
||||
if res_meta.is_err() || res_data.is_err() {
|
||||
warn!("failed to delete bin {} for manual deletion", id);
|
||||
}
|
||||
|
||||
Ok("ok\n")
|
||||
}
|
||||
|
||||
async fn delete_bin_interactive(
|
||||
_: Path<String>,
|
||||
_: OidcExtractor<EmptyAdditionalClaims>,
|
||||
) -> HandlerResult<impl IntoResponse> {
|
||||
let body = html! {
|
||||
<html>
|
||||
<head>
|
||||
<title>{"zettoit bin"}</title>
|
||||
<link rel={"icon"} type={"image/svg"} href={"https://static.zettoit.eu/img/zettoit-logo.svg"}/>
|
||||
</head>
|
||||
<body style={"font-family: monospace; background-color: black; color: white;"}>
|
||||
<div style={"margin: auto; max-width: 80ch;"}>
|
||||
<h2>{"Confirm Deletion"}</h2>
|
||||
<p>{"The bin will be deleted. All data will be permanently lost."}</p>
|
||||
<form method={"post"}>
|
||||
<button type={"submit"}>{"Delete"}</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
};
|
||||
Ok(Html(body))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PostQuery {
|
||||
ttl: Option<String>,
|
||||
}
|
||||
|
||||
async fn upload_bin(
|
||||
async fn post_item(
|
||||
Path(phrase): Path<String>,
|
||||
Query(params): Query<PostQuery>,
|
||||
State(app_state): State<AppState>,
|
||||
|
@ -372,6 +308,7 @@ async fn upload_bin(
|
|||
}
|
||||
}
|
||||
|
||||
#[debug_handler]
|
||||
async fn get_item(
|
||||
Path(phrase): Path<String>,
|
||||
State(app_state): State<AppState>,
|
Loading…
Reference in a new issue