diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index c621844..f078e3b 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -7,6 +7,6 @@ tag = True
search = version = "{current_version}"
replace = version = "{new_version}"
-[bumpversion:file:warpgate-admin/Cargo.toml]
+[bumpversion:file:warpgate-protocol-http/Cargo.toml]
search = version = "{current_version}"
replace = version = "{new_version}"
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 5732ce0..38542a0 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -10,7 +10,7 @@ updates:
schedule:
interval: "weekly"
- package-ecosystem: "npm"
- directory: "/warpgate-admin/app"
+ directory: "/web"
labels: ["type/deps"]
open-pull-requests-limit: 25
schedule:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 26a6cd4..3756fe0 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -35,7 +35,7 @@ jobs:
- name: Build admin UI
run: |
- just yarn openapi-client
+ just openapi
just yarn build
- name: Build
diff --git a/Cargo.lock b/Cargo.lock
index c5a062a..86cb088 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -905,6 +905,17 @@ version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
+[[package]]
+name = "delegate"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35c47a31748d9cfa641f6cccb3608385fafe261ba36054f3d40d5a3ca11eb1af"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "derive_more"
version = "0.99.17"
@@ -1287,7 +1298,7 @@ dependencies = [
"indexmap",
"slab",
"tokio",
- "tokio-util",
+ "tokio-util 0.7.1",
"tracing",
]
@@ -1503,6 +1514,19 @@ dependencies = [
"want",
]
+[[package]]
+name = "hyper-rustls"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac"
+dependencies = [
+ "http",
+ "hyper",
+ "rustls",
+ "tokio",
+ "tokio-rustls",
+]
+
[[package]]
name = "hyper-timeout"
version = "0.4.1"
@@ -1515,6 +1539,19 @@ dependencies = [
"tokio-io-timeout",
]
+[[package]]
+name = "hyper-tls"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
+dependencies = [
+ "bytes",
+ "hyper",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+]
+
[[package]]
name = "ident_case"
version = "1.0.1"
@@ -1585,6 +1622,12 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "ipnet"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
+
[[package]]
name = "itertools"
version = "0.10.3"
@@ -2261,7 +2304,7 @@ dependencies = [
"rand",
"regex",
"rust-embed",
- "rustls-pemfile",
+ "rustls-pemfile 1.0.0",
"serde",
"serde_json",
"serde_urlencoded",
@@ -2273,7 +2316,7 @@ dependencies = [
"tokio-rustls",
"tokio-stream",
"tokio-tungstenite",
- "tokio-util",
+ "tokio-util 0.7.1",
"tracing",
]
@@ -2572,6 +2615,48 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "reqwest"
+version = "0.11.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb"
+dependencies = [
+ "base64",
+ "bytes",
+ "encoding_rs",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "hyper",
+ "hyper-rustls",
+ "hyper-tls",
+ "ipnet",
+ "js-sys",
+ "lazy_static",
+ "log",
+ "mime",
+ "native-tls",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustls",
+ "rustls-native-certs",
+ "rustls-pemfile 0.3.0",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "tokio",
+ "tokio-native-tls",
+ "tokio-rustls",
+ "tokio-util 0.6.10",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "winreg",
+]
+
[[package]]
name = "ring"
version = "0.16.20"
@@ -2753,6 +2838,27 @@ dependencies = [
"webpki",
]
+[[package]]
+name = "rustls-native-certs"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile 1.0.0",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360"
+dependencies = [
+ "base64",
+]
+
[[package]]
name = "rustls-pemfile"
version = "1.0.0"
@@ -3638,8 +3744,26 @@ checksum = "06cda1232a49558c46f8a504d5b93101d42c0bf7f911f12a105ba48168f821ae"
dependencies = [
"futures-util",
"log",
+ "rustls",
+ "rustls-native-certs",
"tokio",
+ "tokio-rustls",
"tungstenite",
+ "webpki",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.6.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "log",
+ "pin-project-lite",
+ "tokio",
]
[[package]]
@@ -3689,7 +3813,7 @@ dependencies = [
"prost-derive",
"tokio",
"tokio-stream",
- "tokio-util",
+ "tokio-util 0.7.1",
"tower",
"tower-layer",
"tower-service",
@@ -3724,7 +3848,7 @@ dependencies = [
"rand",
"slab",
"tokio",
- "tokio-util",
+ "tokio-util 0.7.1",
"tower-layer",
"tower-service",
"tracing",
@@ -3854,10 +3978,12 @@ dependencies = [
"httparse",
"log",
"rand",
+ "rustls",
"sha-1",
"thiserror",
"url",
"utf-8",
+ "webpki",
]
[[package]]
@@ -4048,6 +4174,7 @@ dependencies = [
"tracing-subscriber",
"warpgate-admin",
"warpgate-common",
+ "warpgate-protocol-http",
"warpgate-protocol-ssh",
]
@@ -4076,6 +4203,7 @@ dependencies = [
"warpgate-common",
"warpgate-db-entities",
"warpgate-protocol-ssh",
+ "warpgate-web",
]
[[package]]
@@ -4135,6 +4263,34 @@ dependencies = [
"uuid",
]
+[[package]]
+name = "warpgate-protocol-http"
+version = "0.2.5"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "cookie",
+ "data-encoding",
+ "delegate",
+ "futures",
+ "http",
+ "lazy_static",
+ "percent-encoding",
+ "poem",
+ "poem-openapi",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "tokio",
+ "tokio-tungstenite",
+ "tracing",
+ "uuid",
+ "warpgate-admin",
+ "warpgate-common",
+ "warpgate-db-entities",
+ "warpgate-web",
+]
+
[[package]]
name = "warpgate-protocol-ssh"
version = "0.1.0"
@@ -4158,6 +4314,16 @@ dependencies = [
"warpgate-db-entities",
]
+[[package]]
+name = "warpgate-web"
+version = "0.1.0"
+dependencies = [
+ "rust-embed",
+ "serde",
+ "serde_json",
+ "thiserror",
+]
+
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
@@ -4339,6 +4505,15 @@ version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9"
+[[package]]
+name = "winreg"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
+dependencies = [
+ "winapi",
+]
+
[[package]]
name = "yaml-rust"
version = "0.4.5"
diff --git a/Cargo.toml b/Cargo.toml
index 4940e8c..a7fc1ad 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,7 +5,9 @@ members = [
"warpgate-common",
"warpgate-db-migrations",
"warpgate-db-entities",
+ "warpgate-protocol-http",
"warpgate-protocol-ssh",
+ "warpgate-web",
]
default-members = ["warpgate"]
diff --git a/README.md b/README.md
index f2e7700..99b2078 100644
--- a/README.md
+++ b/README.md
@@ -58,11 +58,11 @@ You can use the web interface to view the live session list, review session reco
## Contributing / building from source
-* You'll need nightly Rust (will be installed automatically), NodeJS and Yarn
+* You'll need Rust, NodeJS and Yarn
* Clone the repo
* [Just](https://github.com/casey/just) is used to run tasks - install it: `cargo install just`
* Install the admin UI deps: `just yarn`
-* Build the API SDK: `just openapi-client`
+* Build the API SDK: `just openapi`
* Build the frontend: `just yarn build`
* Build Warpgate: `cargo build` (optionally `--release`)
diff --git a/justfile b/justfile
index 8af9fae..84e8730 100644
--- a/justfile
+++ b/justfile
@@ -13,21 +13,21 @@ clippy *ARGS:
for p in {{projects}}; do cargo clippy -p $p {{ARGS}}; done
yarn *ARGS:
- cd warpgate-admin/app/ && yarn {{ARGS}}
+ cd warpgate-web && yarn {{ARGS}}
migrate *ARGS:
cargo run -p warpgate-db-migrations -- {{ARGS}}
lint:
- cd warpgate-admin/app/ && yarn run lint
+ cd warpgate-web && yarn run lint
svelte-check:
- cd warpgate-admin/app/ && yarn run check
+ cd warpgate-web && yarn run check
openapi-all:
- cd warpgate-admin/app/ && yarn openapi-schema && yarn openapi-client
+ cd warpgate-web && yarn openapi:schema:admin && yarn openapi:schema:gateway && yarn openapi:client:admin && yarn openapi:client:gateway
openapi:
- cd warpgate-admin/app/ && yarn openapi-client
+ cd warpgate-web && yarn openapi:client:admin && yarn openapi:client:gateway
cleanup: (fix "--allow-dirty") (clippy "--fix" "--allow-dirty") fmt svelte-check lint
diff --git a/warpgate-admin/Cargo.toml b/warpgate-admin/Cargo.toml
index cf1e6a6..e27b8eb 100644
--- a/warpgate-admin/Cargo.toml
+++ b/warpgate-admin/Cargo.toml
@@ -12,7 +12,7 @@ chrono = "0.4"
futures = "0.3"
hex = "0.4"
mime_guess = {version = "2.0", default_features = false}
-poem = {version = "^1.3.24", features = ["cookie", "session", "anyhow", "rustls", "websocket", "embed"]}
+poem = {version = "^1.3.30", features = ["cookie", "session", "anyhow", "websocket"]}
poem-openapi = {version = "^1.3.30", features = ["swagger-ui", "chrono", "uuid", "static-files"]}
russh-keys = {version = "0.22.0-beta.2", features = ["openssl"]}
rust-embed = "6.3"
@@ -26,3 +26,4 @@ uuid = {version = "0.8", features = ["v4", "serde"]}
warpgate-common = {version = "*", path = "../warpgate-common"}
warpgate-db-entities = {version = "*", path = "../warpgate-db-entities"}
warpgate-protocol-ssh = {version = "*", path = "../warpgate-protocol-ssh"}
+warpgate-web = {version = "*", path = "../warpgate-web"}
diff --git a/warpgate-admin/app/src/Login.svelte b/warpgate-admin/app/src/Login.svelte
deleted file mode 100644
index def97c4..0000000
--- a/warpgate-admin/app/src/Login.svelte
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-
-
-
diff --git a/warpgate-admin/app/src/lib/api.ts b/warpgate-admin/app/src/lib/api.ts
deleted file mode 100644
index f703c49..0000000
--- a/warpgate-admin/app/src/lib/api.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { DefaultApi, Configuration } from '../../api-client/dist'
-
-const configuration = new Configuration({
- basePath: '/api',
-})
-
-export const api = new DefaultApi(configuration)
-export * from '../../api-client'
diff --git a/warpgate-admin/app/src/lib/store.ts b/warpgate-admin/app/src/lib/store.ts
deleted file mode 100644
index ec292eb..0000000
--- a/warpgate-admin/app/src/lib/store.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { writable } from 'svelte/store'
-
-export const authenticatedUsername = writable(null)
diff --git a/warpgate-admin/app/src/main.ts b/warpgate-admin/app/src/main.ts
deleted file mode 100644
index 8bb81a6..0000000
--- a/warpgate-admin/app/src/main.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import '@fontsource/work-sans'
-import './theme.scss'
-import App from './App.svelte'
-
-const app = new App({
- target: document.getElementById('app')!,
-})
-
-export default app
diff --git a/warpgate-admin/src/api/auth.rs b/warpgate-admin/src/api/auth.rs
deleted file mode 100644
index ba35a9a..0000000
--- a/warpgate-admin/src/api/auth.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-use crate::helpers::ApiResult;
-use poem::session::Session;
-use poem::web::Data;
-use poem_openapi::payload::Json;
-use poem_openapi::{ApiResponse, Object, OpenApi};
-use std::sync::Arc;
-use tokio::sync::Mutex;
-use warpgate_common::{AuthCredential, AuthResult, ConfigProvider, Secret};
-
-pub struct Api;
-
-#[derive(Object)]
-struct LoginRequest {
- username: String,
- password: String,
-}
-
-#[derive(ApiResponse)]
-enum LoginResponse {
- #[oai(status = 201)]
- Success,
-
- #[oai(status = 401)]
- Failure,
-}
-
-#[derive(ApiResponse)]
-enum LogoutResponse {
- #[oai(status = 201)]
- Success,
-}
-
-#[OpenApi]
-impl Api {
- #[oai(path = "/auth/login", method = "post", operation_id = "login")]
- async fn api_auth_login(
- &self,
- session: &Session,
- config_provider: Data<&Arc>>,
- body: Json,
- ) -> ApiResult {
- let mut config_provider = config_provider.lock().await;
- let result = config_provider
- .authorize(
- &body.username,
- &[AuthCredential::Password(Secret::new(body.password.clone()))],
- )
- .await
- .map_err(|e| e.context("Failed to authorize user"))?;
- match result {
- AuthResult::Accepted { username } => {
- let targets = config_provider.list_targets().await?;
- for target in targets {
- if target.web_admin.is_some()
- && config_provider
- .authorize_target(&username, &target.name)
- .await?
- {
- session.set("username", username);
- return Ok(LoginResponse::Success);
- }
- }
- Ok(LoginResponse::Failure)
- }
- AuthResult::Rejected => Ok(LoginResponse::Failure),
- AuthResult::OTPNeeded => Ok(LoginResponse::Failure), // TODO
- }
- }
-
- #[oai(path = "/auth/logout", method = "post", operation_id = "logout")]
- async fn api_auth_logout(&self, session: &Session) -> ApiResult {
- session.clear();
- Ok(LogoutResponse::Success)
- }
-}
diff --git a/warpgate-admin/src/api/info.rs b/warpgate-admin/src/api/info.rs
deleted file mode 100644
index 226cc86..0000000
--- a/warpgate-admin/src/api/info.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-use crate::helpers::ApiResult;
-use poem::session::Session;
-use poem_openapi::payload::Json;
-use poem_openapi::{ApiResponse, Object, OpenApi};
-use serde::Serialize;
-
-pub struct Api;
-
-#[derive(Serialize, Object)]
-pub struct Info {
- version: String,
- username: Option,
-}
-
-#[derive(ApiResponse)]
-enum InstanceInfoResponse {
- #[oai(status = 200)]
- Ok(Json),
-}
-
-#[OpenApi]
-impl Api {
- #[oai(path = "/info", method = "get", operation_id = "get_info")]
- async fn api_get_info(&self, session: &Session) -> ApiResult {
- Ok(InstanceInfoResponse::Ok(Json(Info {
- version: env!("CARGO_PKG_VERSION").to_string(),
- username: session.get::("username"),
- })))
- }
-}
diff --git a/warpgate-admin/src/api/known_hosts_detail.rs b/warpgate-admin/src/api/known_hosts_detail.rs
index d69f532..9ca5515 100644
--- a/warpgate-admin/src/api/known_hosts_detail.rs
+++ b/warpgate-admin/src/api/known_hosts_detail.rs
@@ -1,5 +1,3 @@
-use crate::helpers::{authorized, ApiResult};
-use poem::session::Session;
use poem::web::Data;
use poem_openapi::param::Path;
use poem_openapi::{ApiResponse, OpenApi};
@@ -29,28 +27,24 @@ impl Api {
&self,
db: Data<&Arc>>,
id: Path,
- session: &Session,
- ) -> ApiResult {
- authorized(session, || async move {
- use warpgate_db_entities::KnownHost;
- let db = db.lock().await;
+ ) -> poem::Result {
+ use warpgate_db_entities::KnownHost;
+ let db = db.lock().await;
- let known_host = KnownHost::Entity::find_by_id(id.0)
- .one(&*db)
- .await
- .map_err(poem::error::InternalServerError)?;
+ let known_host = KnownHost::Entity::find_by_id(id.0)
+ .one(&*db)
+ .await
+ .map_err(poem::error::InternalServerError)?;
- match known_host {
- Some(known_host) => {
- known_host
- .delete(&*db)
- .await
- .map_err(poem::error::InternalServerError)?;
- Ok(DeleteSSHKnownHostResponse::Deleted)
- }
- None => Ok(DeleteSSHKnownHostResponse::NotFound),
+ match known_host {
+ Some(known_host) => {
+ known_host
+ .delete(&*db)
+ .await
+ .map_err(poem::error::InternalServerError)?;
+ Ok(DeleteSSHKnownHostResponse::Deleted)
}
- })
- .await
+ None => Ok(DeleteSSHKnownHostResponse::NotFound),
+ }
}
}
diff --git a/warpgate-admin/src/api/known_hosts_list.rs b/warpgate-admin/src/api/known_hosts_list.rs
index be41e9a..a4bedc7 100644
--- a/warpgate-admin/src/api/known_hosts_list.rs
+++ b/warpgate-admin/src/api/known_hosts_list.rs
@@ -1,5 +1,3 @@
-use crate::helpers::{authorized, ApiResult};
-use poem::session::Session;
use poem::web::Data;
use poem_openapi::payload::Json;
use poem_openapi::{ApiResponse, OpenApi};
@@ -26,18 +24,14 @@ impl Api {
async fn api_ssh_get_all_known_hosts(
&self,
db: Data<&Arc>>,
- session: &Session,
- ) -> ApiResult {
- authorized(session, || async move {
- use warpgate_db_entities::KnownHost;
+ ) -> poem::Result {
+ use warpgate_db_entities::KnownHost;
- let db = db.lock().await;
- let hosts = KnownHost::Entity::find()
- .all(&*db)
- .await
- .map_err(poem::error::InternalServerError)?;
- Ok(GetSSHKnownHostsResponse::Ok(Json(hosts)))
- })
- .await
+ let db = db.lock().await;
+ let hosts = KnownHost::Entity::find()
+ .all(&*db)
+ .await
+ .map_err(poem::error::InternalServerError)?;
+ Ok(GetSSHKnownHostsResponse::Ok(Json(hosts)))
}
}
diff --git a/warpgate-admin/src/api/logs.rs b/warpgate-admin/src/api/logs.rs
index a948eea..08998fc 100644
--- a/warpgate-admin/src/api/logs.rs
+++ b/warpgate-admin/src/api/logs.rs
@@ -1,6 +1,4 @@
-use crate::helpers::{authorized, ApiResult};
use chrono::{DateTime, Utc};
-use poem::session::Session;
use poem::web::Data;
use poem_openapi::payload::Json;
use poem_openapi::{ApiResponse, Object, OpenApi};
@@ -35,48 +33,44 @@ impl Api {
&self,
db: Data<&Arc>>,
body: Json,
- session: &Session,
- ) -> ApiResult {
- authorized(session, || async move {
- use warpgate_db_entities::LogEntry;
+ ) -> poem::Result {
+ use warpgate_db_entities::LogEntry;
- let db = db.lock().await;
- let mut q = LogEntry::Entity::find()
- .order_by_desc(LogEntry::Column::Timestamp)
- .limit(body.limit.unwrap_or(100));
+ let db = db.lock().await;
+ let mut q = LogEntry::Entity::find()
+ .order_by_desc(LogEntry::Column::Timestamp)
+ .limit(body.limit.unwrap_or(100));
- if let Some(before) = body.before {
- q = q.filter(LogEntry::Column::Timestamp.lt(before));
- }
- if let Some(after) = body.after {
- q = q
- .filter(LogEntry::Column::Timestamp.gt(after))
- .order_by_asc(LogEntry::Column::Timestamp);
- }
- if let Some(ref session_id) = body.session_id {
- q = q.filter(LogEntry::Column::SessionId.eq(*session_id));
- }
- if let Some(ref username) = body.username {
- q = q.filter(LogEntry::Column::SessionId.eq(username.clone()));
- }
- if let Some(ref search) = body.search {
- q = q.filter(
- LogEntry::Column::Text
- .contains(search)
- .or(LogEntry::Column::Username.contains(search)),
- );
- }
+ if let Some(before) = body.before {
+ q = q.filter(LogEntry::Column::Timestamp.lt(before));
+ }
+ if let Some(after) = body.after {
+ q = q
+ .filter(LogEntry::Column::Timestamp.gt(after))
+ .order_by_asc(LogEntry::Column::Timestamp);
+ }
+ if let Some(ref session_id) = body.session_id {
+ q = q.filter(LogEntry::Column::SessionId.eq(*session_id));
+ }
+ if let Some(ref username) = body.username {
+ q = q.filter(LogEntry::Column::SessionId.eq(username.clone()));
+ }
+ if let Some(ref search) = body.search {
+ q = q.filter(
+ LogEntry::Column::Text
+ .contains(search)
+ .or(LogEntry::Column::Username.contains(search)),
+ );
+ }
- let logs = q
- .all(&*db)
- .await
- .map_err(poem::error::InternalServerError)?;
- let logs = logs
- .into_iter()
- .map(Into::into)
- .collect::>();
- Ok(GetLogsResponse::Ok(Json(logs)))
- })
- .await
+ let logs = q
+ .all(&*db)
+ .await
+ .map_err(poem::error::InternalServerError)?;
+ let logs = logs
+ .into_iter()
+ .map(Into::into)
+ .collect::>();
+ Ok(GetLogsResponse::Ok(Json(logs)))
}
}
diff --git a/warpgate-admin/src/api/mod.rs b/warpgate-admin/src/api/mod.rs
index dbb6856..f098273 100644
--- a/warpgate-admin/src/api/mod.rs
+++ b/warpgate-admin/src/api/mod.rs
@@ -1,5 +1,5 @@
-pub mod auth;
-pub mod info;
+use poem_openapi::OpenApi;
+
pub mod known_hosts_detail;
pub mod known_hosts_list;
pub mod logs;
@@ -11,3 +11,19 @@ pub mod targets_list;
pub mod tickets_detail;
pub mod tickets_list;
pub mod users_list;
+
+pub fn get() -> impl OpenApi {
+ (
+ sessions_list::Api,
+ sessions_detail::Api,
+ recordings_detail::Api,
+ users_list::Api,
+ targets_list::Api,
+ tickets_list::Api,
+ tickets_detail::Api,
+ known_hosts_list::Api,
+ known_hosts_detail::Api,
+ ssh_keys::Api,
+ logs::Api,
+ )
+}
diff --git a/warpgate-admin/src/api/recordings_detail.rs b/warpgate-admin/src/api/recordings_detail.rs
index 271d81e..7aedd18 100644
--- a/warpgate-admin/src/api/recordings_detail.rs
+++ b/warpgate-admin/src/api/recordings_detail.rs
@@ -1,8 +1,6 @@
-use crate::helpers::{authorized, ApiResult};
use bytes::Bytes;
use futures::{SinkExt, StreamExt};
use poem::error::{InternalServerError, NotFoundError};
-use poem::session::Session;
use poem::web::websocket::{Message, WebSocket};
use poem::web::Data;
use poem::{handler, IntoResponse};
@@ -41,22 +39,18 @@ impl Api {
&self,
db: Data<&Arc>>,
id: Path,
- session: &Session,
- ) -> ApiResult {
- authorized(session, || async move {
- let db = db.lock().await;
+ ) -> poem::Result {
+ let db = db.lock().await;
- let recording = Recording::Entity::find_by_id(id.0)
- .one(&*db)
- .await
- .map_err(InternalServerError)?;
+ let recording = Recording::Entity::find_by_id(id.0)
+ .one(&*db)
+ .await
+ .map_err(InternalServerError)?;
- match recording {
- Some(recording) => Ok(GetRecordingResponse::Ok(Json(recording))),
- None => Ok(GetRecordingResponse::NotFound),
- }
- })
- .await
+ match recording {
+ Some(recording) => Ok(GetRecordingResponse::Ok(Json(recording))),
+ None => Ok(GetRecordingResponse::NotFound),
+ }
}
}
@@ -65,62 +59,58 @@ pub async fn api_get_recording_cast(
db: Data<&Arc>>,
recordings: Data<&Arc>>,
id: poem::web::Path,
- session: &Session,
-) -> ApiResult {
- authorized(session, || async move {
- let db = db.lock().await;
+) -> poem::Result {
+ let db = db.lock().await;
- let recording = Recording::Entity::find_by_id(id.0)
- .one(&*db)
- .await
- .map_err(InternalServerError)?;
+ let recording = Recording::Entity::find_by_id(id.0)
+ .one(&*db)
+ .await
+ .map_err(InternalServerError)?;
- let Some(recording) = recording else {
+ let Some(recording) = recording else {
return Err(NotFoundError.into())
};
- if recording.kind != RecordingKind::Terminal {
- return Err(NotFoundError.into());
+ if recording.kind != RecordingKind::Terminal {
+ return Err(NotFoundError.into());
+ }
+
+ let path = {
+ recordings
+ .lock()
+ .await
+ .path_for(&recording.session_id, &recording.name)
+ };
+
+ let mut response = vec![]; //String::new();
+
+ let mut last_size = (0, 0);
+ let file = File::open(&path).await.map_err(InternalServerError)?;
+ let reader = BufReader::new(file);
+ let mut lines = reader.lines();
+ while let Some(line) = lines.next_line().await.map_err(InternalServerError)? {
+ let entry: TerminalRecordingItem =
+ serde_json::from_str(&line[..]).map_err(InternalServerError)?;
+ let asciicast: AsciiCast = entry.into();
+ response.push(serde_json::to_string(&asciicast).map_err(InternalServerError)?);
+ if let AsciiCast::Header { width, height, .. } = asciicast {
+ last_size = (width, height);
}
+ }
- let path = {
- recordings
- .lock()
- .await
- .path_for(&recording.session_id, &recording.name)
- };
+ response.insert(
+ 0,
+ serde_json::to_string(&AsciiCast::Header {
+ time: 0.0,
+ version: 2,
+ width: last_size.0,
+ height: last_size.1,
+ title: recording.name,
+ })
+ .map_err(InternalServerError)?,
+ );
- let mut response = vec![]; //String::new();
-
- let mut last_size = (0, 0);
- let file = File::open(&path).await.map_err(InternalServerError)?;
- let reader = BufReader::new(file);
- let mut lines = reader.lines();
- while let Some(line) = lines.next_line().await.map_err(InternalServerError)? {
- let entry: TerminalRecordingItem =
- serde_json::from_str(&line[..]).map_err(InternalServerError)?;
- let asciicast: AsciiCast = entry.into();
- response.push(serde_json::to_string(&asciicast).map_err(InternalServerError)?);
- if let AsciiCast::Header { width, height, .. } = asciicast {
- last_size = (width, height);
- }
- }
-
- response.insert(
- 0,
- serde_json::to_string(&AsciiCast::Header {
- time: 0.0,
- version: 2,
- width: last_size.0,
- height: last_size.1,
- title: recording.name,
- })
- .map_err(InternalServerError)?,
- );
-
- Ok(response.join("\n"))
- })
- .await
+ Ok(response.join("\n"))
}
#[handler]
@@ -128,36 +118,32 @@ pub async fn api_get_recording_tcpdump(
db: Data<&Arc>>,
recordings: Data<&Arc>>,
id: poem::web::Path,
- session: &Session,
-) -> ApiResult {
- authorized(session, || async move {
- let db = db.lock().await;
+) -> poem::Result {
+ let db = db.lock().await;
- let recording = Recording::Entity::find_by_id(id.0)
- .one(&*db)
- .await
- .map_err(poem::error::InternalServerError)?;
+ let recording = Recording::Entity::find_by_id(id.0)
+ .one(&*db)
+ .await
+ .map_err(poem::error::InternalServerError)?;
- let Some(recording) = recording else {
+ let Some(recording) = recording else {
return Err(NotFoundError.into())
};
- if recording.kind != RecordingKind::Traffic {
- return Err(NotFoundError.into());
- }
+ if recording.kind != RecordingKind::Traffic {
+ return Err(NotFoundError.into());
+ }
- let path = {
- recordings
- .lock()
- .await
- .path_for(&recording.session_id, &recording.name)
- };
+ let path = {
+ recordings
+ .lock()
+ .await
+ .path_for(&recording.session_id, &recording.name)
+ };
- let content = std::fs::read(path).map_err(InternalServerError)?;
+ let content = std::fs::read(path).map_err(InternalServerError)?;
- Ok(Bytes::from(content))
- })
- .await
+ Ok(Bytes::from(content))
}
#[handler]
diff --git a/warpgate-admin/src/api/sessions_detail.rs b/warpgate-admin/src/api/sessions_detail.rs
index 45a5df0..f625dcb 100644
--- a/warpgate-admin/src/api/sessions_detail.rs
+++ b/warpgate-admin/src/api/sessions_detail.rs
@@ -1,4 +1,3 @@
-use crate::helpers::{authorized, ApiResult};
use poem::web::Data;
use poem_openapi::param::Path;
use poem_openapi::payload::Json;
@@ -42,22 +41,18 @@ impl Api {
&self,
db: Data<&Arc>>,
id: Path,
- session: &poem::session::Session,
- ) -> ApiResult {
- authorized(session, || async move {
- let db = db.lock().await;
+ ) -> poem::Result {
+ let db = db.lock().await;
- let session = Session::Entity::find_by_id(id.0)
- .one(&*db)
- .await
- .map_err(poem::error::InternalServerError)?;
+ let session = Session::Entity::find_by_id(id.0)
+ .one(&*db)
+ .await
+ .map_err(poem::error::InternalServerError)?;
- match session {
- Some(session) => Ok(GetSessionResponse::Ok(Json(session.into()))),
- None => Ok(GetSessionResponse::NotFound),
- }
- })
- .await
+ match session {
+ Some(session) => Ok(GetSessionResponse::Ok(Json(session.into()))),
+ None => Ok(GetSessionResponse::NotFound),
+ }
}
#[oai(
@@ -69,19 +64,15 @@ impl Api {
&self,
db: Data<&Arc>>,
id: Path,
- session: &poem::session::Session,
- ) -> ApiResult {
- authorized(session, || async move {
- let db = db.lock().await;
- let recordings: Vec = Recording::Entity::find()
- .order_by_desc(Recording::Column::Started)
- .filter(Recording::Column::SessionId.eq(id.0))
- .all(&*db)
- .await
- .map_err(poem::error::InternalServerError)?;
- Ok(GetSessionRecordingsResponse::Ok(Json(recordings)))
- })
- .await
+ ) -> poem::Result {
+ let db = db.lock().await;
+ let recordings: Vec = Recording::Entity::find()
+ .order_by_desc(Recording::Column::Started)
+ .filter(Recording::Column::SessionId.eq(id.0))
+ .all(&*db)
+ .await
+ .map_err(poem::error::InternalServerError)?;
+ Ok(GetSessionRecordingsResponse::Ok(Json(recordings)))
}
#[oai(
@@ -93,19 +84,15 @@ impl Api {
&self,
state: Data<&Arc>>,
id: Path,
- session: &poem::session::Session,
- ) -> ApiResult {
- authorized(session, || async move {
- let state = state.lock().await;
+ ) -> poem::Result {
+ let state = state.lock().await;
- if let Some(s) = state.sessions.get(&id) {
- let mut session = s.lock().await;
- session.handle.close();
- Ok(CloseSessionResponse::Ok)
- } else {
- Ok(CloseSessionResponse::NotFound)
- }
- })
- .await
+ if let Some(s) = state.sessions.get(&id) {
+ let mut session = s.lock().await;
+ session.handle.close();
+ Ok(CloseSessionResponse::Ok)
+ } else {
+ Ok(CloseSessionResponse::NotFound)
+ }
}
}
diff --git a/warpgate-admin/src/api/sessions_list.rs b/warpgate-admin/src/api/sessions_list.rs
index 44a4461..2a97a76 100644
--- a/warpgate-admin/src/api/sessions_list.rs
+++ b/warpgate-admin/src/api/sessions_list.rs
@@ -1,5 +1,3 @@
-use crate::helpers::{authorized, ApiResult};
-use poem::session::Session;
use poem::web::Data;
use poem_openapi::payload::Json;
use poem_openapi::{ApiResponse, OpenApi};
@@ -28,24 +26,20 @@ impl Api {
async fn api_get_all_sessions(
&self,
db: Data<&Arc>>,
- session: &Session,
- ) -> ApiResult {
- authorized(session, || async move {
- use warpgate_db_entities::Session;
+ ) -> poem::Result {
+ use warpgate_db_entities::Session;
- let db = db.lock().await;
- let sessions = Session::Entity::find()
- .order_by_desc(Session::Column::Started)
- .all(&*db)
- .await
- .map_err(poem::error::InternalServerError)?;
- let sessions = sessions
- .into_iter()
- .map(Into::into)
- .collect::>();
- Ok(GetSessionsResponse::Ok(Json(sessions)))
- })
- .await
+ let db = db.lock().await;
+ let sessions = Session::Entity::find()
+ .order_by_desc(Session::Column::Started)
+ .all(&*db)
+ .await
+ .map_err(poem::error::InternalServerError)?;
+ let sessions = sessions
+ .into_iter()
+ .map(Into::into)
+ .collect::>();
+ Ok(GetSessionsResponse::Ok(Json(sessions)))
}
#[oai(
@@ -56,18 +50,14 @@ impl Api {
async fn api_close_all_sessions(
&self,
state: Data<&Arc>>,
- session: &Session,
- ) -> ApiResult {
- authorized(session, || async move {
- let state = state.lock().await;
+ ) -> poem::Result {
+ let state = state.lock().await;
- for s in state.sessions.values() {
- let mut session = s.lock().await;
- session.handle.close();
- }
+ for s in state.sessions.values() {
+ let mut session = s.lock().await;
+ session.handle.close();
+ }
- Ok(CloseAllSessionsResponse::Ok)
- })
- .await
+ Ok(CloseAllSessionsResponse::Ok)
}
}
diff --git a/warpgate-admin/src/api/ssh_keys.rs b/warpgate-admin/src/api/ssh_keys.rs
index 30b6114..102d3c1 100644
--- a/warpgate-admin/src/api/ssh_keys.rs
+++ b/warpgate-admin/src/api/ssh_keys.rs
@@ -1,5 +1,3 @@
-use crate::helpers::{authorized, ApiResult};
-use poem::session::Session;
use poem::web::Data;
use poem_openapi::payload::Json;
use poem_openapi::{ApiResponse, Object, OpenApi};
@@ -33,22 +31,18 @@ impl Api {
async fn api_ssh_get_own_keys(
&self,
config: Data<&Arc>>,
- session: &Session,
- ) -> ApiResult {
- authorized(session, || async move {
- let config = config.lock().await;
- let keys = warpgate_protocol_ssh::load_client_keys(&config)
- .map_err(poem::error::InternalServerError)?;
+ ) -> poem::Result {
+ let config = config.lock().await;
+ let keys = warpgate_protocol_ssh::load_client_keys(&config)
+ .map_err(poem::error::InternalServerError)?;
- let keys = keys
- .into_iter()
- .map(|k| SSHKey {
- kind: k.name().to_owned(),
- public_key_base64: k.public_key_base64().replace('\n', "").replace('\r', ""),
- })
- .collect();
- Ok(GetSSHOwnKeysResponse::Ok(Json(keys)))
- })
- .await
+ let keys = keys
+ .into_iter()
+ .map(|k| SSHKey {
+ kind: k.name().to_owned(),
+ public_key_base64: k.public_key_base64().replace('\n', "").replace('\r', ""),
+ })
+ .collect();
+ Ok(GetSSHOwnKeysResponse::Ok(Json(keys)))
}
}
diff --git a/warpgate-admin/src/api/targets_list.rs b/warpgate-admin/src/api/targets_list.rs
index 5339929..94ae6bd 100644
--- a/warpgate-admin/src/api/targets_list.rs
+++ b/warpgate-admin/src/api/targets_list.rs
@@ -1,5 +1,3 @@
-use crate::helpers::{authorized, ApiResult};
-use poem::session::Session;
use poem::web::Data;
use poem_openapi::payload::Json;
use poem_openapi::{ApiResponse, OpenApi};
@@ -21,13 +19,9 @@ impl Api {
async fn api_get_all_targets(
&self,
config_provider: Data<&Arc>>,
- session: &Session,
- ) -> ApiResult {
- authorized(session, || async move {
- let mut targets = config_provider.lock().await.list_targets().await?;
- targets.sort_by(|a, b| a.name.cmp(&b.name));
- Ok(GetTargetsResponse::Ok(Json(targets)))
- })
- .await
+ ) -> poem::Result {
+ let mut targets = config_provider.lock().await.list_targets().await?;
+ targets.sort_by(|a, b| a.name.cmp(&b.name));
+ Ok(GetTargetsResponse::Ok(Json(targets)))
}
}
diff --git a/warpgate-admin/src/api/tickets_detail.rs b/warpgate-admin/src/api/tickets_detail.rs
index e982425..4c84079 100644
--- a/warpgate-admin/src/api/tickets_detail.rs
+++ b/warpgate-admin/src/api/tickets_detail.rs
@@ -1,5 +1,3 @@
-use crate::helpers::{authorized, ApiResult};
-use poem::session::Session;
use poem::web::Data;
use poem_openapi::param::Path;
use poem_openapi::{ApiResponse, OpenApi};
@@ -30,28 +28,24 @@ impl Api {
&self,
db: Data<&Arc>>,
id: Path,
- session: &Session,
- ) -> ApiResult {
- authorized(session, || async move {
- use warpgate_db_entities::Ticket;
- let db = db.lock().await;
+ ) -> poem::Result {
+ use warpgate_db_entities::Ticket;
+ let db = db.lock().await;
- let ticket = Ticket::Entity::find_by_id(id.0)
- .one(&*db)
- .await
- .map_err(poem::error::InternalServerError)?;
+ let ticket = Ticket::Entity::find_by_id(id.0)
+ .one(&*db)
+ .await
+ .map_err(poem::error::InternalServerError)?;
- match ticket {
- Some(ticket) => {
- ticket
- .delete(&*db)
- .await
- .map_err(poem::error::InternalServerError)?;
- Ok(DeleteTicketResponse::Deleted)
- }
- None => Ok(DeleteTicketResponse::NotFound),
+ match ticket {
+ Some(ticket) => {
+ ticket
+ .delete(&*db)
+ .await
+ .map_err(poem::error::InternalServerError)?;
+ Ok(DeleteTicketResponse::Deleted)
}
- })
- .await
+ None => Ok(DeleteTicketResponse::NotFound),
+ }
}
}
diff --git a/warpgate-admin/src/api/tickets_list.rs b/warpgate-admin/src/api/tickets_list.rs
index e333e8f..f10403a 100644
--- a/warpgate-admin/src/api/tickets_list.rs
+++ b/warpgate-admin/src/api/tickets_list.rs
@@ -1,6 +1,4 @@
-use crate::helpers::{authorized, ApiResult};
use anyhow::Context;
-use poem::session::Session;
use poem::web::Data;
use poem_openapi::payload::Json;
use poem_openapi::{ApiResponse, Object, OpenApi};
@@ -47,23 +45,19 @@ impl Api {
async fn api_get_all_tickets(
&self,
db: Data<&Arc>>,
- session: &Session,
- ) -> ApiResult {
- authorized(session, || async move {
- use warpgate_db_entities::Ticket;
+ ) -> poem::Result {
+ use warpgate_db_entities::Ticket;
- let db = db.lock().await;
- let tickets = Ticket::Entity::find()
- .all(&*db)
- .await
- .map_err(poem::error::InternalServerError)?;
- let tickets = tickets
- .into_iter()
- .map(Into::into)
- .collect::>();
- Ok(GetTicketsResponse::Ok(Json(tickets)))
- })
- .await
+ let db = db.lock().await;
+ let tickets = Ticket::Entity::find()
+ .all(&*db)
+ .await
+ .map_err(poem::error::InternalServerError)?;
+ let tickets = tickets
+ .into_iter()
+ .map(Into::into)
+ .collect::>();
+ Ok(GetTicketsResponse::Ok(Json(tickets)))
}
#[oai(path = "/tickets", method = "post", operation_id = "create_ticket")]
@@ -71,36 +65,32 @@ impl Api {
&self,
db: Data<&Arc>>,
body: Json,
- session: &Session,
- ) -> ApiResult {
- authorized(session, || async move {
- use warpgate_db_entities::Ticket;
+ ) -> poem::Result {
+ use warpgate_db_entities::Ticket;
- if body.username.is_empty() {
- return Ok(CreateTicketResponse::BadRequest(Json("username".into())));
- }
- if body.target_name.is_empty() {
- return Ok(CreateTicketResponse::BadRequest(Json("target_name".into())));
- }
+ if body.username.is_empty() {
+ return Ok(CreateTicketResponse::BadRequest(Json("username".into())));
+ }
+ if body.target_name.is_empty() {
+ return Ok(CreateTicketResponse::BadRequest(Json("target_name".into())));
+ }
- let db = db.lock().await;
- let secret = generate_ticket_secret();
- let values = Ticket::ActiveModel {
- id: Set(Uuid::new_v4()),
- secret: Set(secret.expose_secret().to_string()),
- username: Set(body.username.clone()),
- target: Set(body.target_name.clone()),
- created: Set(chrono::Utc::now()),
- ..Default::default()
- };
+ let db = db.lock().await;
+ let secret = generate_ticket_secret();
+ let values = Ticket::ActiveModel {
+ id: Set(Uuid::new_v4()),
+ secret: Set(secret.expose_secret().to_string()),
+ username: Set(body.username.clone()),
+ target: Set(body.target_name.clone()),
+ created: Set(chrono::Utc::now()),
+ ..Default::default()
+ };
- let ticket = values.insert(&*db).await.context("Error saving ticket")?;
+ let ticket = values.insert(&*db).await.context("Error saving ticket")?;
- Ok(CreateTicketResponse::Created(Json(TicketAndSecret {
- secret: secret.expose_secret().to_string(),
- ticket,
- })))
- })
- .await
+ Ok(CreateTicketResponse::Created(Json(TicketAndSecret {
+ secret: secret.expose_secret().to_string(),
+ ticket,
+ })))
}
}
diff --git a/warpgate-admin/src/api/users_list.rs b/warpgate-admin/src/api/users_list.rs
index d9ccae0..f206adf 100644
--- a/warpgate-admin/src/api/users_list.rs
+++ b/warpgate-admin/src/api/users_list.rs
@@ -1,5 +1,3 @@
-use crate::helpers::{authorized, ApiResult};
-use poem::session::Session;
use poem::web::Data;
use poem_openapi::payload::Json;
use poem_openapi::{ApiResponse, OpenApi};
@@ -21,13 +19,9 @@ impl Api {
async fn api_get_all_users(
&self,
config_provider: Data<&Arc>>,
- session: &Session,
- ) -> ApiResult {
- authorized(session, || async move {
- let mut users = config_provider.lock().await.list_users().await?;
- users.sort_by(|a, b| a.username.cmp(&b.username));
- Ok(GetUsersResponse::Ok(Json(users)))
- })
- .await
+ ) -> poem::Result {
+ let mut users = config_provider.lock().await.list_users().await?;
+ users.sort_by(|a, b| a.username.cmp(&b.username));
+ Ok(GetUsersResponse::Ok(Json(users)))
}
}
diff --git a/warpgate-admin/src/helpers.rs b/warpgate-admin/src/helpers.rs
deleted file mode 100644
index 4e5ee74..0000000
--- a/warpgate-admin/src/helpers.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-use poem::http::StatusCode;
-use poem::session::Session;
-
-pub type ApiResult = poem::Result;
-
-pub trait SessionExt {
- fn is_authorized(&self) -> bool;
-}
-
-impl SessionExt for Session {
- fn is_authorized(&self) -> bool {
- self.get::("username").is_some()
- }
-}
-
-pub async fn authorized(session: &Session, f: FN) -> ApiResult
-where
- FN: FnOnce() -> FT,
- FT: futures::Future