fix: check ACL read reach against the entry it connects from

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BRoYE5ZeAVvrDYdfhDAYXb
This commit is contained in:
Diego Imbert
2026-09-17 09:00:04 +02:00
co-authored by Claude Opus 5
parent b7623f12eb
commit e79bc14903
4 changed files with 31 additions and 5 deletions
+1 -1
View File
@@ -1 +1 @@
85e15fc93b238159fc90acfc8427d29e219b88ca
0edd40979cf36bfba59323f3f6a0811ae1369cf5
@@ -39,7 +39,7 @@ use windmill_common::error::{pg_error_message, Error, JsonResult, Result};
use windmill_common::workspaces::{resolve_governing_datatable, DataTable, GoverningDatatable};
use windmill_common::{PgDatabase, DB};
use crate::datatable_permissions::{ensure_governs_datatable, ensure_reaches_datatable};
use crate::datatable_permissions::{ensure_governs_datatable, ensure_reaches_governing_datatable};
pub(crate) fn routes() -> Router {
Router::new()
@@ -1018,8 +1018,8 @@ async fn get_datatable_acl(
) -> JsonResult<DatatableAclInfo> {
crate::datatable_acl_oss::ensure_datatable_acl_available()?;
let target: AclTarget = query.try_into()?;
ensure_reaches_datatable(&db, &w_id, &datatable_name, &authed).await?;
let governing = resolve_governing_datatable(&db, &w_id, &datatable_name).await?;
ensure_reaches_governing_datatable(&db, &w_id, &datatable_name, &governing, &authed).await?;
ensure_instance(&governing)?;
let editable = ensure_governs_datatable(&db, &authed, &w_id, &governing)
.await
@@ -65,3 +65,15 @@ pub(crate) async fn ensure_reaches_datatable(
) -> Result<()> {
roles::ensure_reaches_datatable(db, w_id, datatable_name, authed).await
}
/// [`ensure_reaches_datatable`] against an entry already resolved, for a caller that goes on to
/// connect from that same entry.
pub(crate) async fn ensure_reaches_governing_datatable(
db: &DB,
w_id: &str,
datatable_name: &str,
governing: &GoverningDatatable,
authed: &ApiAuthed,
) -> Result<()> {
roles::ensure_reaches_governing_datatable(db, w_id, datatable_name, governing, authed).await
}
@@ -12,8 +12,8 @@
#[cfg(all(feature = "private", feature = "enterprise"))]
pub(crate) use crate::datatable_permissions_ee::{
ensure_governs_datatable, ensure_reaches_datatable, get_datatable_permissions,
list_usable_datatable_roles, set_datatable_permissions,
ensure_governs_datatable, ensure_reaches_datatable, ensure_reaches_governing_datatable,
get_datatable_permissions, list_usable_datatable_roles, set_datatable_permissions,
};
#[cfg(not(all(feature = "private", feature = "enterprise")))]
@@ -56,6 +56,20 @@ mod ce {
}
}
pub(crate) async fn ensure_reaches_governing_datatable(
_db: &DB,
_w_id: &str,
_datatable_name: &str,
governing: &GoverningDatatable,
_authed: &ApiAuthed,
) -> Result<()> {
if governing.datatable.permissions.is_none() {
Ok(())
} else {
Err(unavailable())
}
}
// The routes stay registered so the API has one shape; each answers after authentication,
// before anything is read.