mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-24 08:03:55 +00:00
user path restriction works !
This commit is contained in:
@@ -17,15 +17,17 @@ use windmill_common::{
|
||||
db::UserDB,
|
||||
error::{to_anyhow, Error, Result},
|
||||
jwt,
|
||||
s3_helpers::{build_object_store_client, ObjectStoreResource},
|
||||
s3_helpers::{
|
||||
build_object_store_client, check_lfs_object_path_permissions, ObjectStoreResource,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
auth::AuthCache,
|
||||
db::DB,
|
||||
job_helpers_ee::upload_file_from_req,
|
||||
job_helpers_ee::{get_large_file_storage, upload_file_from_req},
|
||||
job_helpers_oss::{
|
||||
delete_s3_file_internal, get_workspace_s3_resource, read_object_streamable,
|
||||
delete_s3_file_internal, get_workspace_s3_resource_from_lfs, read_object_streamable,
|
||||
DeleteS3FileQuery,
|
||||
},
|
||||
};
|
||||
@@ -46,7 +48,7 @@ async fn get_object(
|
||||
Extension(auth_cache): Extension<Arc<AuthCache>>,
|
||||
req: Request<Body>,
|
||||
) -> Result<Response> {
|
||||
let uri = format!("/api/w/{w_id}/s3_proxy/{storage_str}/{object_key}");
|
||||
let uri = format!("/api/w/{}/s3_proxy/{}", w_id, req.uri().to_string());
|
||||
let token = get_token(req.headers(), req.method().as_str(), &uri).await?;
|
||||
let Some(authed) = auth_cache.get_authed(Some(w_id.clone()), &token).await else {
|
||||
return Err(Error::NotAuthorized("Invalid token".to_string()));
|
||||
@@ -56,8 +58,16 @@ async fn get_object(
|
||||
_ => Some(storage_str.clone()),
|
||||
};
|
||||
|
||||
let lfs = get_large_file_storage(&db, &w_id, storage).await?;
|
||||
let lfs = match lfs {
|
||||
Some(lfs) => lfs,
|
||||
None => {
|
||||
return Err(Error::InternalErr(format!("Large file storage not found",)));
|
||||
}
|
||||
};
|
||||
check_lfs_object_path_permissions(&lfs, &object_key, &authed.clone().into())?;
|
||||
let (_, s3_resource) =
|
||||
get_workspace_s3_resource(&authed, &db, Some(user_db), &token, &w_id, storage).await?;
|
||||
get_workspace_s3_resource_from_lfs(&authed, &db, Some(user_db), &token, &w_id, lfs).await?;
|
||||
let s3_resource = s3_resource.ok_or_else(|| {
|
||||
Error::InternalErr(format!(
|
||||
"Storage {} not found at the workspace level",
|
||||
@@ -78,7 +88,7 @@ async fn put_object(
|
||||
Extension(auth_cache): Extension<Arc<AuthCache>>,
|
||||
req: Request<Body>,
|
||||
) -> Result<Response> {
|
||||
let uri = format!("/api/w/{w_id}/s3_proxy/{storage_str}/{object_key}");
|
||||
let uri = format!("/api/w/{}/s3_proxy/{}", w_id, req.uri().to_string());
|
||||
let token = get_token(req.headers(), req.method().as_str(), &uri).await?;
|
||||
let Some(authed) = auth_cache.get_authed(Some(w_id.clone()), &token).await else {
|
||||
return Err(Error::NotAuthorized("Invalid token".to_string()));
|
||||
@@ -88,8 +98,16 @@ async fn put_object(
|
||||
_ => Some(storage_str.clone()),
|
||||
};
|
||||
|
||||
let lfs = get_large_file_storage(&db, &w_id, storage).await?;
|
||||
let lfs = match lfs {
|
||||
Some(lfs) => lfs,
|
||||
None => {
|
||||
return Err(Error::InternalErr(format!("Large file storage not found",)));
|
||||
}
|
||||
};
|
||||
check_lfs_object_path_permissions(&lfs, &object_key, &authed.clone().into())?;
|
||||
let (_, s3_resource) =
|
||||
get_workspace_s3_resource(&authed, &db, Some(user_db), &token, &w_id, storage).await?;
|
||||
get_workspace_s3_resource_from_lfs(&authed, &db, Some(user_db), &token, &w_id, lfs).await?;
|
||||
let s3_resource = s3_resource.ok_or_else(|| {
|
||||
Error::InternalErr(format!(
|
||||
"Storage {} not found at the workspace level",
|
||||
@@ -131,7 +149,7 @@ async fn post_object(
|
||||
Extension(auth_cache): Extension<Arc<AuthCache>>,
|
||||
req: Request<Body>,
|
||||
) -> Result<Response> {
|
||||
let uri = format!("/api/w/{w_id}/s3_proxy/{storage_str}/{object_key}");
|
||||
let uri = format!("/api/w/{}/s3_proxy/{}", w_id, req.uri().to_string());
|
||||
let token = get_token(req.headers(), req.method().as_str(), &uri).await?;
|
||||
let Some(authed) = auth_cache.get_authed(Some(w_id.clone()), &token).await else {
|
||||
return Err(Error::NotAuthorized("Invalid token".to_string()));
|
||||
@@ -141,8 +159,16 @@ async fn post_object(
|
||||
_ => Some(storage_str.clone()),
|
||||
};
|
||||
|
||||
let lfs = get_large_file_storage(&db, &w_id, storage).await?;
|
||||
let lfs = match lfs {
|
||||
Some(lfs) => lfs,
|
||||
None => {
|
||||
return Err(Error::InternalErr(format!("Large file storage not found",)));
|
||||
}
|
||||
};
|
||||
check_lfs_object_path_permissions(&lfs, &object_key, &authed.clone().into())?;
|
||||
let (_, s3_resource) =
|
||||
get_workspace_s3_resource(&authed, &db, Some(user_db), &token, &w_id, storage).await?;
|
||||
get_workspace_s3_resource_from_lfs(&authed, &db, Some(user_db), &token, &w_id, lfs).await?;
|
||||
let s3_resource = s3_resource.ok_or_else(|| {
|
||||
Error::InternalErr(format!(
|
||||
"Storage {} not found at the workspace level",
|
||||
@@ -167,7 +193,7 @@ async fn delete_object(
|
||||
Extension(auth_cache): Extension<Arc<AuthCache>>,
|
||||
req: Request<Body>,
|
||||
) -> Result<()> {
|
||||
let uri = format!("/api/w/{w_id}/s3_proxy/{storage_str}/{object_key}");
|
||||
let uri = format!("/api/w/{}/s3_proxy/{}", w_id, req.uri().to_string());
|
||||
let token = get_token(req.headers(), req.method().as_str(), &uri).await?;
|
||||
let Some(authed) = auth_cache.get_authed(Some(w_id.clone()), &token).await else {
|
||||
return Err(Error::NotAuthorized("Invalid token".to_string()));
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::db::Authed;
|
||||
use crate::error::{self};
|
||||
#[cfg(feature = "parquet")]
|
||||
use aws_sdk_sts::config::ProvideCredentials;
|
||||
@@ -236,6 +237,16 @@ impl LargeFileStorage {
|
||||
LargeFileStorage::GoogleCloudStorage(gcs_lfs) => &gcs_lfs.gcs_resource_path,
|
||||
}
|
||||
}
|
||||
pub fn is_restricted_to_user_paths(&self) -> bool {
|
||||
match self {
|
||||
LargeFileStorage::S3Storage(lfs) => lfs.restricted_to_user_paths,
|
||||
LargeFileStorage::S3AwsOidc(lfs) => lfs.restricted_to_user_paths,
|
||||
LargeFileStorage::AzureBlobStorage(lfs) => lfs.restricted_to_user_paths,
|
||||
LargeFileStorage::AzureWorkloadIdentity(lfs) => lfs.restricted_to_user_paths,
|
||||
LargeFileStorage::GoogleCloudStorage(glfs) => glfs.restricted_to_user_paths,
|
||||
}
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@@ -1104,3 +1115,23 @@ impl ObjectStoreResource {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_lfs_object_path_permissions(
|
||||
lfs: &LargeFileStorage,
|
||||
object_path: &str,
|
||||
authed: &Authed,
|
||||
) -> error::Result<()> {
|
||||
if authed.is_admin {
|
||||
return Ok(());
|
||||
}
|
||||
let username = authed.username.as_str();
|
||||
|
||||
if lfs.is_restricted_to_user_paths() {
|
||||
if !object_path.starts_with(&format!("u/{username}/")) {
|
||||
return Err(error::Error::NotAuthorized(format!(
|
||||
"Can only access u/{username}/**"
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user