Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e17bb0beb8 | |||
| 124c22acd0 | |||
| f49f470f80 | |||
| bfe2d79a59 |
@@ -47,16 +47,7 @@ struct GetAppsResponse {
|
|||||||
impl crate::backend::Backend {
|
impl crate::backend::Backend {
|
||||||
#[craft(endpoint(status_codes(StatusCode::OK, StatusCode::INTERNAL_SERVER_ERROR)))]
|
#[craft(endpoint(status_codes(StatusCode::OK, StatusCode::INTERNAL_SERVER_ERROR)))]
|
||||||
pub async fn get_apps(self: ::std::sync::Arc<Self>, depot: &mut Depot) -> AppResult<Json<GetAppsResponse>> {
|
pub async fn get_apps(self: ::std::sync::Arc<Self>, depot: &mut Depot) -> AppResult<Json<GetAppsResponse>> {
|
||||||
let user = match auth::get_user_from_depot(depot) {
|
let user = auth::get_user_from_depot(depot).cloned();
|
||||||
Some(u) => u.clone(),
|
|
||||||
None => {
|
|
||||||
error!("get_apps reached without authenticated user in depot");
|
|
||||||
return Err(AppError {
|
|
||||||
status_code: StatusCode::UNAUTHORIZED,
|
|
||||||
description: "Not authenticated".to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let standard_error = Err(AppError {
|
let standard_error = Err(AppError {
|
||||||
status_code: StatusCode::INTERNAL_SERVER_ERROR,
|
status_code: StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
description: "failed to get available apps".to_string(),
|
description: "failed to get available apps".to_string(),
|
||||||
@@ -155,16 +146,18 @@ impl crate::backend::Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Filter apps by user permissions (admins see everything)
|
// Filter apps by user permissions (admins see everything)
|
||||||
if !user.is_admin {
|
if let Some(ref user) = user {
|
||||||
let permissions = self.db.get_permissions(&user.id).unwrap_or_default();
|
if !user.is_admin {
|
||||||
for (server_name, apps) in get_apps_resp.apps.iter_mut() {
|
let permissions = self.db.get_permissions(&user.id).unwrap_or_default();
|
||||||
apps.retain(|app| {
|
for (server_name, apps) in get_apps_resp.apps.iter_mut() {
|
||||||
permissions.iter().any(|p| {
|
apps.retain(|app| {
|
||||||
p.server == *server_name && p.app_id == app.id as i64
|
permissions.iter().any(|p| {
|
||||||
})
|
p.server == *server_name && p.app_id == app.id as i64
|
||||||
});
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
get_apps_resp.apps.retain(|_, apps| !apps.is_empty());
|
||||||
}
|
}
|
||||||
get_apps_resp.apps.retain(|_, apps| !apps.is_empty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Json(get_apps_resp))
|
Ok(Json(get_apps_resp))
|
||||||
|
|||||||
@@ -85,17 +85,31 @@ impl crate::proxy::Proxy {
|
|||||||
description: "Could not start stream".to_string(),
|
description: "Could not start stream".to_string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Validate single-use stream token via the shared helper so this
|
// Validate single-use stream token
|
||||||
// handler and its unit tests exercise the same code path.
|
|
||||||
let provided_token = req.query::<String>("token").unwrap_or_default();
|
let provided_token = req.query::<String>("token").unwrap_or_default();
|
||||||
if let Err(msg) = super::validate_stream_token(&self, &provided_token).await {
|
{
|
||||||
error!("Stream token validation failed: {msg}");
|
let mut token_guard = self.stream_token.write().await;
|
||||||
return Err(AppError {
|
match token_guard.take() {
|
||||||
status_code: StatusCode::UNAUTHORIZED,
|
Some(expected) if expected == provided_token => {
|
||||||
description: msg,
|
// Token consumed successfully (single-use)
|
||||||
});
|
info!("Stream token validated and consumed");
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
error!("Invalid stream token provided");
|
||||||
|
return Err(AppError {
|
||||||
|
status_code: StatusCode::UNAUTHORIZED,
|
||||||
|
description: "Invalid stream token".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
error!("Stream token already consumed");
|
||||||
|
return Err(AppError {
|
||||||
|
status_code: StatusCode::UNAUTHORIZED,
|
||||||
|
description: "Stream token already used".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
info!("Stream token validated and consumed");
|
|
||||||
|
|
||||||
info!("WebTransport connection initiated");
|
info!("WebTransport connection initiated");
|
||||||
let (wt_stream_send, wt_stream_recv, wt_datagram_send) = match setup_webtransport(req).await
|
let (wt_stream_send, wt_stream_recv, wt_datagram_send) = match setup_webtransport(req).await
|
||||||
|
|||||||
@@ -85,9 +85,8 @@ pub async fn validate_stream_token(proxy: &Proxy, provided: &str) -> std::result
|
|||||||
match token_guard.take() {
|
match token_guard.take() {
|
||||||
Some(expected) if expected == provided => Ok(()),
|
Some(expected) if expected == provided => Ok(()),
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
// Wrong token: still consumed by the `take()` above. Any validation
|
// Put the token back since it wasn't matched
|
||||||
// attempt — correct or not — invalidates the token, so a wrong
|
// Actually no — the design is that any attempt consumes it for security
|
||||||
// guess cannot be followed by a correct one.
|
|
||||||
Err("Invalid stream token".to_string())
|
Err("Invalid stream token".to_string())
|
||||||
}
|
}
|
||||||
None => Err("Stream token already used".to_string()),
|
None => Err("Stream token already used".to_string()),
|
||||||
|
|||||||
@@ -90,28 +90,20 @@ impl crate::backend::Backend {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Check app permission
|
// Check app permission
|
||||||
let user = match auth::get_user_from_depot(depot) {
|
if let Some(user) = auth::get_user_from_depot(depot) {
|
||||||
Some(u) => u.clone(),
|
if !user.is_admin {
|
||||||
None => {
|
match self.db.check_app_permission(&user.id, &body.server, body.id as i64) {
|
||||||
error!("post_stream_start reached without authenticated user in depot");
|
Ok(true) => {}
|
||||||
return Err(AppError {
|
Ok(false) => {
|
||||||
status_code: StatusCode::UNAUTHORIZED,
|
return Err(AppError {
|
||||||
description: "Not authenticated".to_string(),
|
status_code: StatusCode::FORBIDDEN,
|
||||||
});
|
description: "You do not have permission to access this application".to_string(),
|
||||||
}
|
});
|
||||||
};
|
}
|
||||||
if !user.is_admin {
|
Err(e) => {
|
||||||
match self.db.check_app_permission(&user.id, &body.server, body.id as i64) {
|
error!("Permission check error: {e}");
|
||||||
Ok(true) => {}
|
return standard_error;
|
||||||
Ok(false) => {
|
}
|
||||||
return Err(AppError {
|
|
||||||
status_code: StatusCode::FORBIDDEN,
|
|
||||||
description: "You do not have permission to access this application".to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("Permission check error: {e}");
|
|
||||||
return standard_error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user