DUNGEONS-34: add one-command app launcher
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -71,6 +71,7 @@ struct App {
|
||||
key: Arc<Vec<u8>>,
|
||||
setup: Arc<String>,
|
||||
origin: Arc<String>,
|
||||
allowed_origins: Arc<Vec<String>>,
|
||||
secure: bool,
|
||||
peers: Arc<Mutex<Peers>>,
|
||||
limits: Arc<Mutex<HashMap<String, (i64, u32)>>>,
|
||||
@@ -232,11 +233,14 @@ async fn guard(State(app): State<App>, req: Request, next: Next) -> Response {
|
||||
axum::http::Method::GET | axum::http::Method::HEAD | axum::http::Method::OPTIONS
|
||||
);
|
||||
if (mutation || req.uri().path() == "/api/signal")
|
||||
&& req
|
||||
&& !req
|
||||
.headers()
|
||||
.get(header::ORIGIN)
|
||||
.and_then(|v| v.to_str().ok())
|
||||
!= Some(app.origin.as_str())
|
||||
.is_some_and(|origin| {
|
||||
origin == app.origin.as_str()
|
||||
|| app.allowed_origins.iter().any(|allowed| allowed == origin)
|
||||
})
|
||||
{
|
||||
return ApiError(StatusCode::FORBIDDEN, "origin").into_response();
|
||||
}
|
||||
@@ -701,6 +705,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
key: Arc::new(key.into_bytes()),
|
||||
setup: Arc::new(setup),
|
||||
secure: origin.starts_with("https://"),
|
||||
allowed_origins: Arc::new(
|
||||
std::env::var("WEBKA_ALLOWED_ORIGINS")
|
||||
.unwrap_or_default()
|
||||
.split(',')
|
||||
.map(str::trim)
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(str::to_owned)
|
||||
.collect(),
|
||||
),
|
||||
origin: Arc::new(origin),
|
||||
peers: Default::default(),
|
||||
limits: Default::default(),
|
||||
|
||||
@@ -14,6 +14,7 @@ async fn app() -> (App, tempfile::TempDir) {
|
||||
key: Arc::new(vec![42; 32]),
|
||||
setup: Arc::new("setup-test".into()),
|
||||
origin: Arc::new("http://localhost:3000".into()),
|
||||
allowed_origins: Arc::new(vec![]),
|
||||
secure: false,
|
||||
peers: Default::default(),
|
||||
limits: Default::default(),
|
||||
@@ -556,3 +557,36 @@ async fn saturated_signaling_queue_fails_closed() {
|
||||
send(&client, json!({"type":"viewer-left"}));
|
||||
assert!(*rx.borrow());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn launcher_origins_are_explicit_and_port_specific() {
|
||||
let (mut app, _dir) = app().await;
|
||||
app.allowed_origins = Arc::new(vec![
|
||||
"http://127.0.0.1:3002".into(),
|
||||
"http://192.168.1.23:3002".into(),
|
||||
]);
|
||||
for (origin, expected) in [
|
||||
("http://127.0.0.1:3002", StatusCode::UNAUTHORIZED),
|
||||
("http://192.168.1.23:3002", StatusCode::UNAUTHORIZED),
|
||||
("http://localhost:3000", StatusCode::UNAUTHORIZED),
|
||||
("http://192.168.1.23:3003", StatusCode::FORBIDDEN),
|
||||
("http://evil.example", StatusCode::FORBIDDEN),
|
||||
("null", StatusCode::FORBIDDEN),
|
||||
] {
|
||||
let req = Request::builder()
|
||||
.method("POST")
|
||||
.uri("/api/logout")
|
||||
.header("origin", origin)
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
router(app.clone(), "/none".into())
|
||||
.oneshot(req)
|
||||
.await
|
||||
.unwrap()
|
||||
.status(),
|
||||
expected,
|
||||
"{origin}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user