From 01ed3318cb5b11a43bc6994339b1f528a97e4f78 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Tue, 27 Jul 2021 16:40:51 -0600 Subject: [PATCH] middleware --- appmgr/Cargo.lock | 188 +++++++++++++++++++++- appmgr/Cargo.toml | 42 +++-- appmgr/migrations/20210629193146_Init.sql | 11 +- appmgr/src/bin/embassyd.rs | 4 + appmgr/src/lib.rs | 3 +- appmgr/src/middleware/auth.rs | 105 ++++++++++++ appmgr/src/middleware/cors.rs | 48 ++++++ appmgr/src/middleware/mod.rs | 2 + 8 files changed, 381 insertions(+), 22 deletions(-) create mode 100644 appmgr/src/middleware/auth.rs create mode 100644 appmgr/src/middleware/cors.rs create mode 100644 appmgr/src/middleware/mod.rs diff --git a/appmgr/Cargo.lock b/appmgr/Cargo.lock index de4235125..8e067461c 100644 --- a/appmgr/Cargo.lock +++ b/appmgr/Cargo.lock @@ -49,6 +49,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "ascii-canvas" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" +dependencies = [ + "term 0.7.0", +] + [[package]] name = "async-trait" version = "0.1.51" @@ -116,6 +125,17 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "basic-cookies" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb53b6b315f924c7f113b162e53b3901c05fc9966baf84d201dfcc7432a4bb38" +dependencies = [ + "lalrpop", + "lalrpop-util", + "regex", +] + [[package]] name = "bindgen" version = "0.55.1" @@ -140,6 +160,21 @@ dependencies = [ "which", ] +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.2.1" @@ -463,6 +498,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-mac" version = "0.7.0" @@ -557,6 +598,12 @@ dependencies = [ "syn 0.15.44", ] +[[package]] +name = "diff" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" + [[package]] name = "digest" version = "0.8.1" @@ -661,6 +708,7 @@ dependencies = [ "async-trait", "avahi-sys", "base32", + "basic-cookies", "bollard", "chrono", "clap", @@ -676,7 +724,7 @@ dependencies = [ "lazy_static", "libc", "log", - "nix 0.20.0", + "nix 0.22.0", "openssl", "patch-db", "pin-project", @@ -720,6 +768,15 @@ dependencies = [ "serde", ] +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + [[package]] name = "encode_unicode" version = "0.3.6" @@ -775,6 +832,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + [[package]] name = "fnv" version = "1.0.7" @@ -1266,6 +1329,38 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +[[package]] +name = "lalrpop" +version = "0.19.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15174f1c529af5bf1283c3bc0058266b483a67156f79589fab2a25e23cf8988" +dependencies = [ + "ascii-canvas", + "atty", + "bit-set", + "diff", + "ena", + "itertools 0.10.1", + "lalrpop-util", + "petgraph", + "pico-args", + "regex", + "regex-syntax", + "string_cache", + "term 0.7.0", + "tiny-keccak", + "unicode-xid 0.2.2", +] + +[[package]] +name = "lalrpop-util" +version = "0.19.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e58cce361efcc90ba8a0a5f982c741ff86b603495bb15a998412e957dcd278" +dependencies = [ + "regex", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1415,6 +1510,12 @@ dependencies = [ "tempfile", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" + [[package]] name = "nix" version = "0.19.1" @@ -1439,6 +1540,19 @@ dependencies = [ "libc", ] +[[package]] +name = "nix" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1e25ee6b412c2a1e3fcb6a4499a5c1bfe7f43e014bdce9a6b6666e5aa2d187" +dependencies = [ + "bitflags", + "cc", + "cfg-if 1.0.0", + "libc", + "memoffset", +] + [[package]] name = "nom" version = "5.1.2" @@ -1638,6 +1752,31 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "petgraph" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pico-args" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" + [[package]] name = "pin-project" version = "1.0.8" @@ -1688,6 +1827,12 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "prettytable-rs" version = "0.8.0" @@ -1698,7 +1843,7 @@ dependencies = [ "csv", "encode_unicode", "lazy_static", - "term", + "term 0.5.2", "unicode-width", ] @@ -2314,6 +2459,12 @@ dependencies = [ "thread-id", ] +[[package]] +name = "siphasher" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbce6d4507c7e4a3962091436e56e95290cb71fa302d0d270e32130b75fbff27" + [[package]] name = "slab" version = "0.4.4" @@ -2376,6 +2527,7 @@ dependencies = [ "bitflags", "byteorder", "bytes 1.0.1", + "chrono", "crc", "crossbeam-channel", "crossbeam-queue", @@ -2449,6 +2601,18 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "string_cache" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ddb1139b5353f96e429e1a5e19fbaf663bddedaa06d1dbd49f82e352601209a" +dependencies = [ + "lazy_static", + "new_debug_unreachable", + "phf_shared", + "precomputed-hash", +] + [[package]] name = "stringprep" version = "0.1.2" @@ -2559,6 +2723,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "termcolor" version = "1.1.2" @@ -2619,6 +2794,15 @@ dependencies = [ "winapi", ] +[[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.3.1" diff --git a/appmgr/Cargo.toml b/appmgr/Cargo.toml index 50874805a..697fe1be4 100644 --- a/appmgr/Cargo.toml +++ b/appmgr/Cargo.toml @@ -45,50 +45,58 @@ production = [] [dependencies] anyhow = "1.0.40" async-trait = "0.1.42" -avahi-sys = { git="https://github.com/Start9Labs/avahi-sys", version="0.10.0", branch="feature/dynamic-linking", features=["dynamic"], optional=true } +avahi-sys = { git = "https://github.com/Start9Labs/avahi-sys", version = "0.10.0", branch = "feature/dynamic-linking", features = [ + "dynamic", +], optional = true } base32 = "0.4.0" +basic-cookies = "0.1.4" bollard = "0.11.0" -chrono = { version="0.4.19", features=["serde"] } +chrono = { version = "0.4.19", features = ["serde"] } clap = "2.33" digest = "0.9.0" -ed25519-dalek = { version="1.0.1", features=["serde"] } -emver = { version="0.1.2", features=["serde"] } +ed25519-dalek = { version = "1.0.1", features = ["serde"] } +emver = { version = "0.1.2", features = ["serde"] } futures = "0.3.8" git-version = "0.3.4" http = "0.2.3" -indexmap = { version="1.6.2", features=["serde"] } +indexmap = { version = "1.6.2", features = ["serde"] } itertools = "0.10.0" jsonpath_lib = "0.3.0" lazy_static = "1.4" libc = "0.2.86" log = "0.4.11" -nix = "0.20.0" -openssl = { version="0.10.30", features=["vendored"] } -patch-db = { version="*", path="../../patch-db/patch-db" } +nix = "0.22.0" +openssl = { version = "0.10.30", features = ["vendored"] } +patch-db = { version = "*", path = "../../patch-db/patch-db" } pin-project = "1.0.6" prettytable-rs = "0.8.0" rand = "0.7.3" regex = "1.4.2" -reqwest = { version="0.11.2", features=["stream", "json"] } +reqwest = { version = "0.11.2", features = ["stream", "json"] } rpassword = "5.0.0" -rpc-toolkit = { version="*", path="../../rpc-toolkit/rpc-toolkit" } +rpc-toolkit = { version = "*", path = "../../rpc-toolkit/rpc-toolkit" } rust-argon2 = "0.8.3" scopeguard = "1.1" # because avahi-sys fucks your shit up -serde = { version="1.0.118", features=["derive", "rc"] } +serde = { version = "1.0.118", features = ["derive", "rc"] } serde_cbor = "0.11.1" serde_json = "1.0.59" -serde_toml = { package="toml", version="0.5.8" } +serde_toml = { package = "toml", version = "0.5.8" } serde_yaml = "0.8.14" sha2 = "0.9.3" simple-logging = "2.0" -sqlx = { version="0.5", features=["runtime-tokio-rustls", "sqlite", "offline"] } +sqlx = { version = "0.5", features = [ + "runtime-tokio-rustls", + "sqlite", + "offline", + "chrono", +] } tar = "0.4.35" thiserror = "1.0.24" -tokio = { version="1.8.1", features=["full"] } +tokio = { version = "1.8.1", features = ["full"] } tokio-compat-02 = "0.2.0" -tokio-stream = { version="0.1.5", features=["io-util", "sync"] } +tokio-stream = { version = "0.1.5", features = ["io-util", "sync"] } tokio-tar = "0.3.0" -tokio-util = { version="0.6.6", features=["io"] } +tokio-util = { version = "0.6.6", features = ["io"] } torut = "0.1.9" typed-builder = "0.9.0" -url = { version="2.2.1", features=["serde"] } +url = { version = "2.2.1", features = ["serde"] } diff --git a/appmgr/migrations/20210629193146_Init.sql b/appmgr/migrations/20210629193146_Init.sql index 87b06ec61..6b2878b2b 100644 --- a/appmgr/migrations/20210629193146_Init.sql +++ b/appmgr/migrations/20210629193146_Init.sql @@ -2,7 +2,14 @@ CREATE TABLE IF NOT EXISTS tor ( package TEXT NOT NULL, - interface TEXT NOT NULL, - key BLOB NOT NULL, + interface TEXT NOT NULL, + key BLOB NOT NULL, PRIMARY KEY (package, interface) +); +CREATE TABLE IF NOT EXISTS session +( + id TEXT NOT NULL PRIMARY KEY, + created_at TIMESTAMP NOT NULL, + expires_at TIMESTAMP NOT NULL, + metadata JSON ); \ No newline at end of file diff --git a/appmgr/src/bin/embassyd.rs b/appmgr/src/bin/embassyd.rs index 0a17ccc9b..b0d0c76ac 100644 --- a/appmgr/src/bin/embassyd.rs +++ b/appmgr/src/bin/embassyd.rs @@ -3,6 +3,7 @@ use std::time::Duration; use embassy::context::{EitherContext, RpcContext}; use embassy::db::model::Database; +use embassy::middleware::cors::cors; use embassy::status::{check_all, synchronize_all}; use embassy::util::daemon; use embassy::{Error, ErrorKind}; @@ -28,6 +29,9 @@ async fn inner_main(cfg_path: Option<&str>) -> Result<(), Error> { command: embassy::main_api, context: ctx, status: status_fn, + middleware: [ + cors + ] }); let status_ctx = rpc_ctx.clone(); let status_daemon = daemon( diff --git a/appmgr/src/lib.rs b/appmgr/src/lib.rs index c46d4b1ef..b9d551755 100644 --- a/appmgr/src/lib.rs +++ b/appmgr/src/lib.rs @@ -29,6 +29,7 @@ pub mod id; pub mod inspect; pub mod install; pub mod manager; +pub mod middleware; pub mod migration; pub mod net; pub mod registry; @@ -45,7 +46,7 @@ use rpc_toolkit::command; use rpc_toolkit::yajrc::RpcError; pub use version::{init, self_update}; -#[command] +#[command(metadata(authenticated = false))] pub fn echo(#[context] _ctx: EitherContext, #[arg] message: String) -> Result { Ok(message) } diff --git a/appmgr/src/middleware/auth.rs b/appmgr/src/middleware/auth.rs new file mode 100644 index 000000000..3a0ed5b92 --- /dev/null +++ b/appmgr/src/middleware/auth.rs @@ -0,0 +1,105 @@ +use anyhow::anyhow; +use basic_cookies::Cookie; +use chrono::Utc; +use futures::future::BoxFuture; +use futures::FutureExt; +use rpc_toolkit::hyper::header::COOKIE; +use rpc_toolkit::hyper::http::Error as HttpError; +use rpc_toolkit::hyper::{Body, Request, Response, StatusCode}; +use rpc_toolkit::rpc_server_helpers::{ + noop2, noop3, DynMiddleware, DynMiddlewareStage2, DynMiddlewareStage3, +}; +use rpc_toolkit::yajrc::RpcMethod; +use rpc_toolkit::Metadata; +use serde::Deserialize; + +use crate::context::RpcContext; +use crate::{Error, ResultExt}; +async fn is_authed(ctx: &RpcContext, req: &Request) -> Result { + if let Some(cookie_header) = req.headers().get(COOKIE) { + let cookies = Cookie::parse( + cookie_header + .to_str() + .with_kind(crate::ErrorKind::Authorization)?, + ) + .with_kind(crate::ErrorKind::Authorization)?; + if let Some(session) = cookies.iter().find(|c| c.get_name() == "session") { + let id = session.get_value(); + let exp = sqlx::query!("SELECT expires_at FROM session WHERE id = ?", id) + .fetch_one(&mut ctx.secret_store.acquire().await?) + .await?; + if exp.expires_at < Utc::now().naive_utc() { + return Ok(true); + } + } + } + Ok(false) +} + +pub async fn auth Deserialize<'de> + 'static, M: Metadata>( + ctx: RpcContext, +) -> DynMiddleware { + Box::new( + |req: &mut Request, + metadata: M| + -> BoxFuture< + Result, Response>, HttpError>, + > { + async move { + match is_authed(&ctx, req).await { + Ok(true) => Ok(Ok(noop2())), + Ok(false) => Ok(Ok({ + let mut fake_req = Request::new(Body::empty()); + *fake_req.headers_mut() = req.headers().clone(); + let m2: DynMiddlewareStage2 = + Box::new(move |rpc_req| { + let method = rpc_req.method.as_str(); + let res: Result< + Result>, + HttpError, + > = if metadata.get(method, "login").unwrap_or(false) { + todo!("set cookie on success") + } else if !metadata.get(method, "authenticated").unwrap_or(true) { + Ok(Ok(noop3())) + } else { + rpc_toolkit::rpc_server_helpers::to_response( + &fake_req, + Ok(( + rpc_req.id.clone(), + Err(Error::new( + anyhow!("UNAUTHORIZED"), + crate::ErrorKind::Authorization, + ) + .into()), + )), + |_| StatusCode::OK, + ) + .map(|a| Err(a)) + }; + async { res }.boxed() + }); + m2 + })), + Err(e) => Ok(Ok({ + let mut fake_req = Request::new(Body::empty()); + *fake_req.headers_mut() = req.headers().clone(); + let m2: DynMiddlewareStage2 = Box::new(move |rpc_req| { + let res: Result< + Result>, + HttpError, + > = rpc_toolkit::rpc_server_helpers::to_response( + &fake_req, + Ok((rpc_req.id.clone(), Err(e.into()))), + |_| StatusCode::OK, + ) + .map(|a| Err(a)); + async { res }.boxed() + }); + m2 + })), + } + } + .boxed() + }, + ) +} diff --git a/appmgr/src/middleware/cors.rs b/appmgr/src/middleware/cors.rs new file mode 100644 index 000000000..0fa860f1d --- /dev/null +++ b/appmgr/src/middleware/cors.rs @@ -0,0 +1,48 @@ +use futures::FutureExt; +use rpc_toolkit::hyper::http::Error as HttpError; +use rpc_toolkit::hyper::{Body, Method, Request, Response}; +use rpc_toolkit::rpc_server_helpers::{ + DynMiddlewareStage2, DynMiddlewareStage3, DynMiddlewareStage4, +}; +use serde::Deserialize; + +pub async fn cors Deserialize<'de> + 'static, Metadata>( + req: &mut Request, +) -> Result, Response>, HttpError> { + if req.method() == Method::OPTIONS { + Ok(Err(Response::builder() + .header( + "Access-Control-Allow-Origin", + if let Some(origin) = req.headers().get("origin").and_then(|s| s.to_str().ok()) { + origin + } else { + "*" + }, + ) + .header("Access-Control-Allow-Methods", "*") + .header("Access-Control-Allow-Headers", "*") + .header("Access-Control-Allow-Credentials", "true") + .body(Body::empty())?)) + } else { + Ok(Ok(Box::new(|_| { + async move { + let res: DynMiddlewareStage3 = Box::new(|_| { + async move { + let res: DynMiddlewareStage4 = Box::new(|res| { + async move { + res.headers_mut() + .insert("Access-Control-Allow-Origin", "*".parse()?); + Ok::<_, HttpError>(()) + } + .boxed() + }); + Ok::<_, HttpError>(Ok(res)) + } + .boxed() + }); + Ok::<_, HttpError>(Ok(res)) + } + .boxed() + }))) + } +} diff --git a/appmgr/src/middleware/mod.rs b/appmgr/src/middleware/mod.rs new file mode 100644 index 000000000..7b5029640 --- /dev/null +++ b/appmgr/src/middleware/mod.rs @@ -0,0 +1,2 @@ +pub mod auth; +pub mod cors;