fix(datatables): report create privileges for the role the manager is on

This commit is contained in:
Diego Imbert
2026-08-31 06:01:13 +02:00
parent 6e76edb279
commit e989a4d9ae
3 changed files with 63 additions and 11 deletions
@@ -2282,10 +2282,20 @@ async fn list_datatable_schemas(
Ok(Json(results))
}
/// The role the caller is on, and the data table it belongs to. The tree covers
/// every data table, and a role name means nothing outside the one it was
/// picked on, so it is named rather than applied to all of them.
#[derive(Deserialize)]
struct ListDataTableTablesQuery {
role_for: Option<String>,
role: Option<String>,
}
async fn list_datatable_tables(
authed: ApiAuthed,
Extension(db): Extension<DB>,
Path(w_id): Path<String>,
Query(query): Query<ListDataTableTablesQuery>,
) -> JsonResult<Vec<DataTableTables>> {
let datatable_names = list_datatable_names(&db, &w_id).await?;
let mut roles = list_datatable_roles(&db, &authed, &w_id).await?;
@@ -2295,7 +2305,14 @@ async fn list_datatable_tables(
let (usable_roles, default_role) = roles
.remove(&datatable_name)
.unwrap_or_else(|| (vec![], ADMIN_DATATABLE_ROLE.to_string()));
let tables = match get_datatable_tables(&db, &authed, &w_id, &datatable_name).await {
// Privileges are the connected role's, so the answer for the data table
// the caller is on has to be read as the role they picked, not as the
// one it defaults to.
let role = query
.role
.as_deref()
.filter(|_| query.role_for.as_deref() == Some(datatable_name.as_str()));
let tables = match get_datatable_tables(&db, &authed, &w_id, &datatable_name, role).await {
Ok(contents) => DataTableTables {
datatable_name,
schemas: contents.tables,
@@ -2532,8 +2549,16 @@ async fn get_datatable_tables(
authed: &ApiAuthed,
w_id: &str,
datatable_name: &str,
role: Option<&str>,
) -> Result<DataTableContents> {
let db_resource = get_datatable_resource_as_admin(db, authed, w_id, datatable_name).await?;
let db_resource = get_datatable_resource_from_db(
db,
w_id,
datatable_name,
role,
DatatableAccess::Authed(authed.to_authed_ref()),
)
.await?;
let pg_db: PgDatabase = serde_json::from_value(db_resource)
.map_err(|e| Error::internal_err(format!("Failed to parse database credentials: {}", e)))?;
let (client, connection) = pg_db.connect(Some(db)).await?;
+12
View File
@@ -4865,6 +4865,18 @@ paths:
- workspace
parameters:
- $ref: "#/components/parameters/WorkspaceId"
- name: role_for
in: query
required: false
schema:
type: string
description: the data table the `role` below was picked on
- name: role
in: query
required: false
schema:
type: string
description: read that data table's privileges as this role rather than its default
responses:
"200":
description: table metadata of all datatables
@@ -42,15 +42,21 @@
// Every data table with its schemas and tables, in one call: this is what the
// left pane's tree navigates, so it has to cover the data tables the user is
// not currently on, not just the selected one.
const datatables = resource<DataTableTables[]>([], async () => {
if (!ws) return []
try {
return await WorkspaceService.listDataTableTables({ workspace: ws })
} catch (e) {
console.error('Failed to load datatables:', e)
return []
}
})
const datatables = resource(
// The privileges it reports are the connected role's, so the role picked on
// the open data table is part of what is being asked.
() => [ws, uriState.selectedDatatable, uriState.selectedRole] as const,
async ([workspace, roleFor, role]): Promise<DataTableTables[]> => {
if (!workspace) return []
try {
return await WorkspaceService.listDataTableTables({ workspace, roleFor, role })
} catch (e) {
console.error('Failed to load datatables:', e)
return []
}
},
{ initialValue: [] }
)
// Roles the *caller* may use, so the picker never offers one that would be
// refused. Absent/disabled permissions yield no roles and hide the picker.
@@ -194,6 +200,14 @@
function refreshManager() {
dbManagerContent?.refresh()
dbManagerContent?.dbManager()?.dbTable()?.refresh()
refreshRoles()
}
/** Re-read what the tree and the role picker show: both are answers about the
* data table's roles, which the permissions drawer can have just changed. */
function refreshRoles() {
datatables.refetch()
usableRoles.refetch()
}
async function handleExportSchema(explicitSource?: string) {
@@ -327,6 +341,7 @@
hideTrigger
workspace={ws}
datatable={actionDatatable}
onSaved={refreshRoles}
/>
{/if}